diff --git a/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java index 4e0c75c0..15a00851 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java @@ -3,14 +3,14 @@ package com.minelittlepony.unicopia.ability; import java.util.Optional; import com.minelittlepony.unicopia.Race; -import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit.Serializer; +import com.minelittlepony.unicopia.ability.data.Rot; import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.server.world.UGameRules; -public class TimeChangeAbility implements Ability { +public class TimeChangeAbility implements Ability { @Override public boolean canUse(Race race) { @@ -38,12 +38,12 @@ public class TimeChangeAbility implements Ability { } @Override - public Serializer getSerializer() { - return Hit.SERIALIZER; + public Serializer getSerializer() { + return Rot.SERIALIZER; } @Override - public Optional prepare(Pony player) { + public Optional prepare(Pony player) { if (!player.asWorld().getGameRules().getBoolean(UGameRules.DO_TIME_MAGIC)) { return Optional.empty(); @@ -53,11 +53,11 @@ public class TimeChangeAbility implements Ability { return Optional.empty(); } - return Hit.INSTANCE; + return Optional.of(Rot.of(player)); } @Override - public boolean apply(Pony player, Hit data) { + public boolean apply(Pony player, Rot data) { if (!player.asWorld().getGameRules().getBoolean(UGameRules.DO_TIME_MAGIC)) { return false; } @@ -65,7 +65,7 @@ public class TimeChangeAbility implements Ability { if (player.getSpellSlot().contains(SpellType.TIME_CONTROL)) { player.getSpellSlot().removeWhere(SpellType.TIME_CONTROL, true); } else { - SpellType.TIME_CONTROL.withTraits().apply(player, CastingMethod.INNATE); + SpellType.TIME_CONTROL.withTraits().apply(player, CastingMethod.INNATE).update(player, data.applyTo(player)); } return true; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/data/Rot.java b/src/main/java/com/minelittlepony/unicopia/ability/data/Rot.java new file mode 100644 index 00000000..104ed191 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/data/Rot.java @@ -0,0 +1,24 @@ +package com.minelittlepony.unicopia.ability.data; + +import com.minelittlepony.unicopia.EntityConvertable; + +import net.minecraft.util.math.Vec3d; + +public record Rot (float pitch, float yaw) implements Hit { + public static final Serializer SERIALIZER = new Serializer<>( + buf -> new Rot(buf.readFloat(), buf.readFloat()), + (buf, t) -> { + buf.writeFloat(t.pitch()); + buf.writeFloat(t.yaw()); + }); + + public Rot applyTo(EntityConvertable target) { + Vec3d pos = target.getOriginVector(); + target.asEntity().updatePositionAndAngles(pos.x, pos.y, pos.z, yaw, pitch); + return this; + } + + public static Rot of(EntityConvertable source) { + return new Rot(source.asEntity().getPitch(1), source.asEntity().getHeadYaw()); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellPredicate.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellPredicate.java index 970fd0c9..1e808801 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellPredicate.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellPredicate.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.ability.magic; +import java.util.UUID; import java.util.function.Predicate; import com.minelittlepony.unicopia.ability.magic.spell.*; @@ -15,6 +16,7 @@ public interface SpellPredicate extends Predicate { SpellPredicate IS_MIMIC = s -> s instanceof MimicSpell; SpellPredicate IS_SHIELD_LIKE = spell -> spell instanceof ShieldSpell; SpellPredicate IS_TIMED = spell -> spell instanceof TimedSpell; + SpellPredicate IS_ORIENTED = spell -> spell instanceof OrientedSpell; SpellPredicate IS_NOT_PLACED = IS_PLACED.negate(); SpellPredicate IS_VISIBLE = spell -> spell != null && !spell.isHidden(); @@ -42,4 +44,8 @@ public interface SpellPredicate extends Predicate { default boolean isOn(Entity entity) { return Caster.of(entity).filter(this::isOn).isPresent(); } + + default SpellPredicate withId(UUID uuid) { + return and(spell -> spell.getUuid().equals(uuid)); + } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/OrientedSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/OrientedSpell.java index eefb5642..38d35171 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/OrientedSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/OrientedSpell.java @@ -1,5 +1,11 @@ package com.minelittlepony.unicopia.ability.magic.spell; -public interface OrientedSpell { +import com.minelittlepony.unicopia.ability.data.Rot; + +public interface OrientedSpell extends Spell { void setOrientation(float pitch, float yaw); + + default void setOrientation(Rot rotation) { + setOrientation(rotation.pitch(), rotation.yaw()); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index f343e957..7154056a 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -10,6 +10,9 @@ import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.EntityReference.EntityValues; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import com.minelittlepony.unicopia.entity.mob.UEntities; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.network.Channel; +import com.minelittlepony.unicopia.network.MsgCasterLookRequest; import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; import com.minelittlepony.unicopia.particle.ParticleHandle; import com.minelittlepony.unicopia.particle.UParticles; @@ -19,6 +22,7 @@ import com.minelittlepony.unicopia.util.NbtSerialisable; import net.minecraft.nbt.*; import net.minecraft.registry.*; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; @@ -84,6 +88,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS if (dimension == null) { dimension = source.asWorld().getRegistryKey(); + if (source instanceof Pony) { + Channel.SERVER_REQUEST_PLAYER_LOOK.sendToPlayer(new MsgCasterLookRequest(getUuid()), (ServerPlayerEntity)source.asEntity()); + } setDirty(); } @@ -153,13 +160,12 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS public void setPosition(Caster source, Vec3d position) { this.position = Optional.of(position); - getWorld(source).ifPresent(world -> { - castEntity.ifPresent(world, entity -> { - entity.updatePositionAndAngles(position.x, position.y, position.z, entity.getYaw(), entity.getPitch()); - }); + this.dimension = source.asWorld().getRegistryKey(); + castEntity.ifPresent(source.asWorld(), entity -> { + entity.updatePositionAndAngles(position.x, position.y, position.z, entity.getYaw(), entity.getPitch()); }); getDelegates(spell -> spell instanceof PlaceableSpell o ? o : null) - .forEach(spell -> spell.setPosition(source ,position)); + .forEach(spell -> spell.setPosition(source, position)); setDirty(); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimeControlAbilitySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimeControlAbilitySpell.java index 4a232a96..6953ff87 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimeControlAbilitySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimeControlAbilitySpell.java @@ -1,6 +1,7 @@ package com.minelittlepony.unicopia.ability.magic.spell; import com.minelittlepony.unicopia.ability.Abilities; +import com.minelittlepony.unicopia.ability.data.Rot; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.*; import com.minelittlepony.unicopia.entity.player.Pony; @@ -13,7 +14,7 @@ import net.minecraft.server.world.ServerWorld; /** * Internal. *

- * Used by the Rainboom ability. + * Used by the time change ability */ public class TimeControlAbilitySpell extends AbstractSpell { @@ -38,38 +39,44 @@ public class TimeControlAbilitySpell extends AbstractSpell { return false; } - if (source.asWorld() instanceof ServerWorld sw) { + update(source, Rot.of(source)); + return source.subtractEnergyCost(2); + } - float yaw = -(source.asEntity().getHeadYaw() + 90); - float pitch = -(source.asEntity().getPitch(1) / 90F); - - long time = (long)(pitch * 6000); - - // sunrise(0) - midday(1) - sunset(2) - midnight(3) - - if (!initilized) { - initilized = true; - timeOffset = sw.getTimeOfDay() - time; - angleOffset = UnicopiaWorldProperties.forWorld(sw).getTangentalSkyAngle() - yaw; - } - - if (angleOffset > 90 && angleOffset < 270) { - time *= -1; - } - - time += timeOffset; - if (time < 0) { - time += 24000; - } - time %= 24000; - - sw.setTimeOfDay(time); - sw.getServer().sendTimeUpdatePackets(); - - UnicopiaWorldProperties.forWorld(sw).setTangentalSkyAngle(angleOffset + yaw); + public void update(Caster source, Rot rotation) { + if (!source.asWorld().getGameRules().getBoolean(UGameRules.DO_TIME_MAGIC)) { + return; + } + if (!(source.asWorld() instanceof ServerWorld sw)) { + return; } - return source.subtractEnergyCost(2); + float yaw = -(rotation.yaw() + 90); + float pitch = -(rotation.pitch() / 90F); + long time = (long)(pitch * 6000); + + // sunrise(0) - midday(1) - sunset(2) - midnight(3) + + if (!initilized) { + initilized = true; + timeOffset = sw.getTimeOfDay() - time; + angleOffset = UnicopiaWorldProperties.forWorld(sw).getTangentalSkyAngle() - yaw; + } + + if (angleOffset > 90 && angleOffset < 270) { + time *= -1; + } + + time += timeOffset; + if (time < 0) { + time += 24000; + } + time %= 24000; + + sw.setTimeOfDay(time); + sw.getServer().sendTimeUpdatePackets(); + + UnicopiaWorldProperties.forWorld(sw).setTangentalSkyAngle(angleOffset + yaw); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/network/Channel.java b/src/main/java/com/minelittlepony/unicopia/network/Channel.java index b62dd139..556477c8 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/Channel.java +++ b/src/main/java/com/minelittlepony/unicopia/network/Channel.java @@ -11,6 +11,7 @@ import net.minecraft.server.network.ServerPlayerEntity; public interface Channel { C2SPacketType> CLIENT_PLAYER_ABILITY = SimpleNetworking.clientToServer(Unicopia.id("player_ability"), MsgPlayerAbility::read); + C2SPacketType CLIENT_CASTER_LOOK = SimpleNetworking.clientToServer(Unicopia.id("caster_look"), MsgCasterLookRequest.Reply::new); C2SPacketType CLIENT_REQUEST_SPECIES_CHANGE = SimpleNetworking.clientToServer(Unicopia.id("request_capabilities"), MsgRequestSpeciesChange::new); C2SPacketType MARK_TRAIT_READ = SimpleNetworking.clientToServer(Unicopia.id("mark_trait_read"), MsgMarkTraitRead::new); C2SPacketType REMOVE_SPELL = SimpleNetworking.clientToServer(Unicopia.id("remove_spell"), MsgRemoveSpell::new); @@ -19,6 +20,7 @@ public interface Channel { S2CPacketType SERVER_SPAWN_PROJECTILE = SimpleNetworking.serverToClient(Unicopia.id("projectile_entity"), MsgSpawnProjectile::new); S2CPacketType SERVER_BLOCK_DESTRUCTION = SimpleNetworking.serverToClient(Unicopia.id("block_destruction"), MsgBlockDestruction::new); S2CPacketType CANCEL_PLAYER_ABILITY = SimpleNetworking.serverToClient(Unicopia.id("player_ability_cancel"), MsgCancelPlayerAbility::read); + S2CPacketType SERVER_REQUEST_PLAYER_LOOK = SimpleNetworking.serverToClient(Unicopia.id("request_player_look"), MsgCasterLookRequest::new); S2CPacketType UNLOCK_TRAITS = SimpleNetworking.serverToClient(Unicopia.id("unlock_traits"), MsgUnlockTraits::new); S2CPacketType SERVER_SELECT_TRIBE = SimpleNetworking.serverToClient(Unicopia.id("select_tribe"), MsgTribeSelect::new); diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgCasterLookRequest.java b/src/main/java/com/minelittlepony/unicopia/network/MsgCasterLookRequest.java new file mode 100644 index 00000000..d5be5999 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgCasterLookRequest.java @@ -0,0 +1,59 @@ +package com.minelittlepony.unicopia.network; + +import java.util.UUID; + +import com.minelittlepony.unicopia.ability.data.Rot; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.SpellPredicate; +import com.minelittlepony.unicopia.ability.magic.spell.OrientedSpell; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.sollace.fabwork.api.packets.HandledPacket; +import com.sollace.fabwork.api.packets.Packet; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.server.network.ServerPlayerEntity; + +/** + * Sent to the client when the server needs to know precisely where the player is looking. + */ +public record MsgCasterLookRequest (UUID spellId) implements Packet { + + public MsgCasterLookRequest(PacketByteBuf buffer) { + this(buffer.readUuid()); + } + + @Override + public void toBuffer(PacketByteBuf buffer) { + buffer.writeUuid(spellId); + } + + public record Reply ( + UUID spellId, + Rot rotation + ) implements HandledPacket { + + Reply(PacketByteBuf buffer) { + this(buffer.readUuid(), Rot.SERIALIZER.read().apply(buffer)); + } + + public Reply(OrientedSpell spell, Caster caster) { + this(spell.getUuid(), Rot.of(caster)); + } + + @Override + public void toBuffer(PacketByteBuf buffer) { + buffer.writeUuid(spellId); + Rot.SERIALIZER.write().accept(buffer, rotation); + } + + @Override + public void handle(ServerPlayerEntity sender) { + Pony.of(sender).getSpellSlot() + .get(SpellPredicate.IS_ORIENTED.withId(spellId), false) + .ifPresent(spell -> { + spell.setOrientation(rotation); + }); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerAnimationChange.java b/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerAnimationChange.java index 1161fea4..c19eadad 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerAnimationChange.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerAnimationChange.java @@ -5,9 +5,8 @@ import java.util.UUID; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.client.render.PlayerPoser.AnimationInstance; import com.minelittlepony.unicopia.entity.player.Pony; -import com.sollace.fabwork.api.packets.HandledPacket; +import com.sollace.fabwork.api.packets.Packet; -import net.minecraft.client.MinecraftClient; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.PacketByteBuf; @@ -18,10 +17,17 @@ public record MsgPlayerAnimationChange ( UUID playerId, AnimationInstance animation, int duration - ) implements HandledPacket { + ) implements Packet { MsgPlayerAnimationChange(PacketByteBuf buffer) { - this(buffer.readUuid(), new AnimationInstance(buffer.readEnumConstant(Animation.class), buffer.readEnumConstant(Animation.Recipient.class)), buffer.readInt()); + this( + buffer.readUuid(), + new AnimationInstance( + buffer.readEnumConstant(Animation.class), + buffer.readEnumConstant(Animation.Recipient.class) + ), + buffer.readInt() + ); } public MsgPlayerAnimationChange(Pony player, AnimationInstance animation, int duration) { @@ -35,14 +41,4 @@ public record MsgPlayerAnimationChange ( buffer.writeEnumConstant(animation.recipient()); buffer.writeInt(duration); } - - @Override - public void handle(PlayerEntity sender) { - Pony player = Pony.of(MinecraftClient.getInstance().world.getPlayerByUuid(playerId)); - if (player == null) { - return; - } - - player.setAnimation(animation, duration); - } } diff --git a/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java b/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java index 1f2798cf..a9e25256 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java +++ b/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java @@ -5,6 +5,7 @@ import java.util.Map; import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.ability.data.Rot; import com.minelittlepony.unicopia.ability.data.tree.TreeTypes; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; @@ -17,6 +18,8 @@ import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Cha import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.*; +import com.minelittlepony.unicopia.network.MsgCasterLookRequest.Reply; + import net.minecraft.client.MinecraftClient; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; @@ -34,6 +37,8 @@ public class ClientNetworkHandlerImpl { Channel.UNLOCK_TRAITS.receiver().addPersistentListener(this::handleUnlockTraits); Channel.SERVER_RESOURCES_SEND.receiver().addPersistentListener(this::handleServerResources); Channel.SERVER_SKY_ANGLE.receiver().addPersistentListener(this::handleSkyAngle); + Channel.SERVER_PLAYER_ANIMATION_CHANGE.receiver().addPersistentListener(this::handlePlayerAnimation); + Channel.SERVER_REQUEST_PLAYER_LOOK.receiver().addPersistentListener(this::handleCasterLookRequest); } private void handleTribeScreen(PlayerEntity sender, MsgTribeSelect packet) { @@ -93,4 +98,22 @@ public class ClientNetworkHandlerImpl { ClientChapters.load((Map)packet.chapters()); TreeTypes.load(packet.treeTypes()); } + + private void handlePlayerAnimation(PlayerEntity sender, MsgPlayerAnimationChange packet) { + Pony player = Pony.of(MinecraftClient.getInstance().world.getPlayerByUuid(packet.playerId())); + if (player == null) { + return; + } + + player.setAnimation(packet.animation(), packet.duration()); + } + + private void handleCasterLookRequest(PlayerEntity sender, MsgCasterLookRequest packet) { + Pony player = Pony.of(MinecraftClient.getInstance().player); + if (player == null) { + return; + } + + Channel.CLIENT_CASTER_LOOK.sendToServer(new Reply(packet.spellId(), Rot.of(player))); + } }