diff --git a/src/main/java/com/minelittlepony/unicopia/UGameRules.java b/src/main/java/com/minelittlepony/unicopia/UGameRules.java new file mode 100644 index 00000000..a42b9b76 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/UGameRules.java @@ -0,0 +1,10 @@ +package com.minelittlepony.unicopia; + +import net.minecraft.world.GameRules; +import net.minecraft.world.GameRules.BooleanRule; + +public interface UGameRules { + GameRules.Key SWAP_TRIBE_ON_DEATH = GameRules.register("swapTribeOnDeath", GameRules.Category.SPAWNING, BooleanRule.create(false)); + + static void bootstrap() { } +} diff --git a/src/main/java/com/minelittlepony/unicopia/Unicopia.java b/src/main/java/com/minelittlepony/unicopia/Unicopia.java index 58c39dd3..6b708b49 100644 --- a/src/main/java/com/minelittlepony/unicopia/Unicopia.java +++ b/src/main/java/com/minelittlepony/unicopia/Unicopia.java @@ -84,6 +84,7 @@ public class Unicopia implements ModInitializer { Abilities.bootstrap(); UScreenHandlers.bootstrap(); UTreeGen.bootstrap(); + UGameRules.bootstrap(); } public interface SidedAccess { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/TraitDiscovery.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/TraitDiscovery.java index a4c5c244..929f54e1 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/TraitDiscovery.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/TraitDiscovery.java @@ -14,6 +14,7 @@ import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.MsgMarkTraitRead; import com.minelittlepony.unicopia.network.MsgUnlockTraits; +import com.minelittlepony.unicopia.util.Copyable; import com.minelittlepony.unicopia.util.NbtSerialisable; import net.fabricmc.api.EnvType; @@ -31,7 +32,7 @@ import net.minecraft.util.Identifier; import net.minecraft.registry.Registries; import net.minecraft.world.World; -public class TraitDiscovery implements NbtSerialisable { +public class TraitDiscovery implements NbtSerialisable, Copyable { private final Set unreadTraits = new HashSet<>(); private final Set traits = new HashSet<>(); @@ -152,7 +153,8 @@ public class TraitDiscovery implements NbtSerialisable { return SpellTraits.fromNbt(nbt); } - public void copyFrom(TraitDiscovery old) { + @Override + public void copyFrom(TraitDiscovery old, boolean alive) { clear(); unreadTraits.addAll(old.unreadTraits); traits.addAll(old.traits); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntityPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/EntityPhysics.java index ade7dbfb..ffcb2076 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/EntityPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntityPhysics.java @@ -151,8 +151,10 @@ public class EntityPhysics implements Physics, Copyable other) { - setBaseGravityModifier(other.getBaseGravityModifier()); + public void copyFrom(EntityPhysics other, boolean alive) { + if (alive) { + setBaseGravityModifier(other.getBaseGravityModifier()); + } } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ItemTracker.java b/src/main/java/com/minelittlepony/unicopia/entity/ItemTracker.java index 9dd3695d..dbbbb0bb 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/ItemTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/ItemTracker.java @@ -123,9 +123,11 @@ public class ItemTracker implements NbtSerialisable, Copyable, Tick } @Override - public void copyFrom(ItemTracker other) { + public void copyFrom(ItemTracker other, boolean alive) { items.clear(); - items.putAll(other.items); + if (alive) { + items.putAll(other.items); + } } public interface Trackable extends ItemConvertible { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java index b1fdae5d..d7213f0c 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java @@ -70,6 +70,10 @@ public class RaceChangeStatusEffect extends StatusEffect { return; } + if (eq instanceof Pony pony) { + pony.setRespawnRace(race); + } + int ticks = Math.max(0, MAX_DURATION - state.getDuration()); Stage stage = Stage.forDuration(ticks / STAGE_DURATION); @@ -80,23 +84,20 @@ public class RaceChangeStatusEffect extends StatusEffect { int progression = ticks % (stage.ordinal() * STAGE_DURATION); - if ((eq instanceof Pony pony ? pony.getActualSpecies() : eq.getSpecies()) == race || !race.isPermitted(entity instanceof PlayerEntity ? (PlayerEntity)entity : null)) { - if (progression == 0 && entity instanceof PlayerEntity && stage == Stage.CRAWLING) { - ((PlayerEntity)entity).sendMessage(Stage.INITIAL.getMessage(race), true); + if ((eq instanceof Pony pony ? pony.getActualSpecies() : eq.getSpecies()) == race || !race.isPermitted(entity instanceof PlayerEntity player ? player : null)) { + if (progression == 0 && entity instanceof PlayerEntity player && stage == Stage.CRAWLING) { + player.sendMessage(Stage.INITIAL.getMessage(race), true); } return; } - if (progression == 0) { - if (stage != Stage.DEATH && entity instanceof PlayerEntity) { - ((PlayerEntity)entity).sendMessage(stage.getMessage(race), true); - } + if (progression == 0 && stage != Stage.DEATH && entity instanceof PlayerEntity player) { + player.sendMessage(stage.getMessage(race), true); } entity.setHealth(1); - if (entity instanceof PlayerEntity) { - Pony pony = (Pony)eq; + if (eq instanceof Pony pony) { MagicReserves magic = pony.getMagicalReserves(); magic.getExertion().add(50); magic.getEnergy().add(3); @@ -108,8 +109,6 @@ public class RaceChangeStatusEffect extends StatusEffect { } if (stage == Stage.DEATH) { - - eq.setSpecies(race); if (eq instanceof Caster) { ((Caster)eq).getSpellSlot().clear(); } @@ -119,11 +118,10 @@ public class RaceChangeStatusEffect extends StatusEffect { magic.getEnergy().set(0.6F); magic.getExhaustion().set(0); magic.getExertion().set(0); - pony.setDirty(); + entity.damage(MagicalDamageSource.TRIBE_SWAP, Float.MAX_VALUE); + } else { + eq.setSpecies(race); } - - entity.damage(MagicalDamageSource.TRIBE_SWAP, Float.MAX_VALUE); - entity.setHealth(0); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 9c6141e8..667d0464 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -21,10 +21,8 @@ import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect; import com.minelittlepony.unicopia.entity.effect.UEffects; import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import com.minelittlepony.unicopia.item.UItems; -import com.minelittlepony.unicopia.network.Channel; -import com.minelittlepony.unicopia.network.MsgOtherPlayerCapabilities; -import com.minelittlepony.unicopia.network.MsgPlayerAnimationChange; import com.minelittlepony.unicopia.util.*; +import com.minelittlepony.unicopia.network.*; import com.minelittlepony.unicopia.network.datasync.EffectSync.UpdateCallback; import com.minelittlepony.unicopia.trinkets.TrinketsDelegate; import com.minelittlepony.common.util.animation.LinearInterpolator; @@ -82,6 +80,8 @@ public class Pony extends Living implements Copyable, Update private final Interpolator interpolator = new LinearInterpolator(); + private Race respawnRace = Race.UNSET; + private boolean dirty; private int ticksHanging; @@ -151,6 +151,10 @@ public class Pony extends Living implements Copyable, Update return advancementProgress; } + public void setRespawnRace(Race race) { + respawnRace = race; + } + @Override public Race getSpecies() { if (AmuletSelectors.ALICORN_AMULET.test(entity)) { @@ -621,21 +625,41 @@ public class Pony extends Living implements Copyable, Update } @Override - public void copyFrom(Pony oldPlayer) { - if (!oldPlayer.asEntity().isRemoved()) { + public void copyFrom(Pony oldPlayer, boolean alive) { + + boolean forcedSwap = !alive + && entity instanceof ServerPlayerEntity + && entity.world.getGameRules().getBoolean(UGameRules.SWAP_TRIBE_ON_DEATH) + && oldPlayer.asEntity().getDamageTracker().getMostRecentDamage().getDamageSource() != MagicalDamageSource.TRIBE_SWAP; + + if (alive) { oldPlayer.getSpellSlot().stream(true).forEach(getSpellSlot()::put); } else { - oldPlayer.getSpellSlot().stream(true).filter(SpellPredicate.IS_PLACED).forEach(getSpellSlot()::put); + if (forcedSwap) { + Channel.SERVER_SELECT_TRIBE.sendToPlayer(new MsgTribeSelect(Race.allPermitted(entity), Text.translatable("gui.unicopia.tribe_selection.respawn")), (ServerPlayerEntity)entity); + } else { + oldPlayer.getSpellSlot().stream(true).filter(SpellPredicate.IS_PLACED).forEach(getSpellSlot()::put); + } } + oldPlayer.getSpellSlot().put(null); - getArmour().copyFrom(oldPlayer.getArmour()); - setSpecies(oldPlayer.getActualSpecies()); - getDiscoveries().copyFrom(oldPlayer.getDiscoveries()); - getCharms().equipSpell(Hand.MAIN_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.MAIN_HAND)); - getCharms().equipSpell(Hand.OFF_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.OFF_HAND)); - corruption.set(oldPlayer.getCorruption().get()); - levels.set(oldPlayer.getLevel().get()); - mana.getXp().set(oldPlayer.getMagicalReserves().getXp().get()); + setSpecies(oldPlayer.respawnRace != Race.UNSET && !alive ? oldPlayer.respawnRace : oldPlayer.getActualSpecies()); + getDiscoveries().copyFrom(oldPlayer.getDiscoveries(), alive); + getPhysics().copyFrom(oldPlayer.getPhysics(), alive); + if (!forcedSwap) { + getArmour().copyFrom(oldPlayer.getArmour(), alive); + getCharms().equipSpell(Hand.MAIN_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.MAIN_HAND)); + getCharms().equipSpell(Hand.OFF_HAND, oldPlayer.getCharms().getEquippedSpell(Hand.OFF_HAND)); + corruption.set(oldPlayer.getCorruption().get()); + levels.set(oldPlayer.getLevel().get()); + mana.getXp().set(oldPlayer.getMagicalReserves().getXp().get()); + } else { + mana.getEnergy().set(0.6F); + mana.getExhaustion().set(0); + mana.getExertion().set(0); + } + + advancementProgress.putAll(oldPlayer.getAdvancementProgress()); setDirty(); onSpawn(); diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinServerPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinServerPlayerEntity.java index 69b310dc..0d84d448 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinServerPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinServerPlayerEntity.java @@ -24,7 +24,7 @@ abstract class MixinServerPlayerEntity extends PlayerEntity implements ScreenHan @SuppressWarnings("unchecked") @Inject(method = "copyFrom(Lnet/minecraft/server/network/ServerPlayerEntity;Z)V", at = @At("HEAD")) private void onCopyFrom(ServerPlayerEntity oldPlayer, boolean alive, CallbackInfo info) { - get().copyFrom(((Equine.Container)oldPlayer).get()); + get().copyFrom(((Equine.Container)oldPlayer).get(), alive); } } diff --git a/src/main/java/com/minelittlepony/unicopia/network/Channel.java b/src/main/java/com/minelittlepony/unicopia/network/Channel.java index 8998ff2e..d49f3f0e 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/Channel.java +++ b/src/main/java/com/minelittlepony/unicopia/network/Channel.java @@ -7,6 +7,7 @@ import com.sollace.fabwork.api.packets.*; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; public interface Channel { C2SPacketType> CLIENT_PLAYER_ABILITY = SimpleNetworking.clientToServer(Unicopia.id("player_ability"), MsgPlayerAbility::read); @@ -39,7 +40,7 @@ public interface Channel { race = Race.UNSET; } if (race.isUnset()) { - sender.sendPacket(SERVER_SELECT_TRIBE.id(), new MsgTribeSelect(Race.allPermitted(handler.player)).toBuffer()); + sender.sendPacket(SERVER_SELECT_TRIBE.id(), new MsgTribeSelect(Race.allPermitted(handler.player), Text.translatable("gui.unicopia.tribe_selection.journey")).toBuffer()); } else { pony.setSpecies(race); Unicopia.LOGGER.info("Setting {}'s race to {} due to host setting", handler.player.getDisplayName().getString(), Race.REGISTRY.getId(race).toString()); diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgTribeSelect.java b/src/main/java/com/minelittlepony/unicopia/network/MsgTribeSelect.java index d6ed4545..25128d8b 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgTribeSelect.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgTribeSelect.java @@ -8,19 +8,19 @@ import com.sollace.fabwork.api.packets.Packet; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.PacketByteBuf; +import net.minecraft.text.Text; -public record MsgTribeSelect (Set availableRaces) implements Packet { +public record MsgTribeSelect (Set availableRaces, Text serverMessage) implements Packet { public MsgTribeSelect(PacketByteBuf buffer) { - this(new HashSet<>()); - int len = buffer.readInt(); - while (len-- > 0) { - availableRaces.add(buffer.readRegistryValue(Race.REGISTRY)); - } + this( + buffer.readCollection(HashSet::new, buf -> buf.readRegistryValue(Race.REGISTRY)), + buffer.readText() + ); } @Override public void toBuffer(PacketByteBuf buffer) { - buffer.writeInt(availableRaces.size()); - availableRaces.forEach(race -> buffer.writeRegistryValue(Race.REGISTRY, race)); + buffer.writeCollection(availableRaces, (buf, race) -> buf.writeRegistryValue(Race.REGISTRY, race)); + buffer.writeText(serverMessage); } } diff --git a/src/main/java/com/minelittlepony/unicopia/util/Copyable.java b/src/main/java/com/minelittlepony/unicopia/util/Copyable.java index 907e8615..e9c3ad17 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/Copyable.java +++ b/src/main/java/com/minelittlepony/unicopia/util/Copyable.java @@ -1,5 +1,5 @@ package com.minelittlepony.unicopia.util; public interface Copyable> { - void copyFrom(T other); + void copyFrom(T other, boolean alive); } diff --git a/src/main/java/com/minelittlepony/unicopia/util/MagicalDamageSource.java b/src/main/java/com/minelittlepony/unicopia/util/MagicalDamageSource.java index 3b248d5e..bcb7be69 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/MagicalDamageSource.java +++ b/src/main/java/com/minelittlepony/unicopia/util/MagicalDamageSource.java @@ -18,7 +18,7 @@ public class MagicalDamageSource extends EntityDamageSource { public static final DamageSource EXHAUSTION = new MagicalDamageSource("magical_exhaustion", null, true, true); public static final DamageSource ALICORN_AMULET = new MagicalDamageSource("alicorn_amulet", null, true, true); public static final DamageSource FOOD_POISONING = new DamageSource("food_poisoning"); - public static final DamageSource TRIBE_SWAP = new DamageSource("tribe_swap"); + public static final DamageSource TRIBE_SWAP = new DamageSource("tribe_swap").setOutOfWorld().setUnblockable(); public static final DamageSource ZAP_APPLE = create("zap"); public static final DamageSource KICK = create("kick"); public static final DamageSource SUN = new DamageSource("sun").setBypassesArmor().setFire(); diff --git a/src/main/resources/unicopia.aw b/src/main/resources/unicopia.aw index 668c9c3f..06f93b39 100644 --- a/src/main/resources/unicopia.aw +++ b/src/main/resources/unicopia.aw @@ -1,4 +1,6 @@ accessWidener v1 named accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters -accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;IZZLnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase; -accessible class net/minecraft/client/render/item/HeldItemRenderer$HandRenderType \ No newline at end of file +accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;IZZLnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase; +accessible class net/minecraft/client/render/item/HeldItemRenderer$HandRenderType +accessible method net/minecraft/world/GameRules register (Ljava/lang/String;Lnet/minecraft/world/GameRules$Category;Lnet/minecraft/world/GameRules$Type;)Lnet/minecraft/world/GameRules$Key; +accessible method net/minecraft/world/GameRules$BooleanRule create (Z)Lnet/minecraft/world/GameRules$Type; \ No newline at end of file