From e210bac2bfb7c6a9fb6573805882951710a24eeb Mon Sep 17 00:00:00 2001 From: Kam Date: Sat, 21 Feb 2026 04:41:51 -0500 Subject: [PATCH] Fix Caster Positoning and Lock / Movement Dashing Abilities --- .../controllers/data/ability/Ability.java | 12 ++++-- .../data/ability/type/AbilityMovement.java | 38 +++++++++++++++++-- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java index 5d793f5ec..5517caef2 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java @@ -863,11 +863,17 @@ public abstract class Ability implements IAbility, IAbilityAction { // AOE_SELF abilities: telegraph follows caster during windup instance.setEntityIdToFollow(caster.getEntityId()); - // For LINE/CONE telegraphs: track target direction during windup + // For LINE/CONE telegraphs: track direction during windup // Only track if rotation is NOT locked during windup (NPC can still turn) - if ((telegraphType == TelegraphType.LINE || telegraphType == TelegraphType.CONE) && target != null) { + if (telegraphType == TelegraphType.LINE || telegraphType == TelegraphType.CONE) { if (!isRotationLockedDuringWindup()) { - instance.setTargetEntityId(target.getEntityId()); + if (target != null) { + // Face the target entity + instance.setTargetEntityId(target.getEntityId()); + } else { + // No target (AOE_SELF etc.): track caster's own rotationYaw + instance.setTrackFollowedEntityYaw(true); + } } // If rotation is locked, yaw stays fixed at creation time } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityMovement.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityMovement.java index b54056146..331b91920 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityMovement.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityMovement.java @@ -2,6 +2,8 @@ package kamkeel.npcs.controllers.data.ability.type; import kamkeel.npcs.controllers.data.ability.Ability; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.play.server.S12PacketEntityVelocity; import net.minecraft.util.Vec3; /** @@ -213,6 +215,24 @@ public abstract class AbilityMovement extends Ability { // VELOCITY APPLICATION // ═══════════════════════════════════════════════════════════════════ + /** + * Send an authoritative velocity packet to a player. + *

+ * The ability tick runs BEFORE moveEntityWithHeading (at PlayerTickEvent.START). + * If we use velocityChanged, the entity tracker sends S12 AFTER friction has + * reduced the motion (~55% of set value). The client then moves at reduced speed, + * reports a short position via C04, and the server resets to the client's position. + *

+ * By sending S12 manually here — before friction — the client receives the full + * velocity and moves at the correct speed. + */ + private void sendPlayerVelocity(EntityLivingBase caster) { + if (caster instanceof EntityPlayerMP) { + ((EntityPlayerMP) caster).playerNetServerHandler.sendPacket( + new S12PacketEntityVelocity(caster)); + } + } + /** * Apply movement velocity to the caster (horizontal only, Y unchanged). */ @@ -220,7 +240,11 @@ public abstract class AbilityMovement extends Ability { caster.motionX = movementDirection.xCoord * speed; caster.motionZ = movementDirection.zCoord * speed; if (!isPreview()) { - caster.velocityChanged = true; + if (caster instanceof EntityPlayerMP) { + sendPlayerVelocity(caster); + } else { + caster.velocityChanged = true; + } } } @@ -233,7 +257,11 @@ public abstract class AbilityMovement extends Ability { caster.motionY = 0; caster.motionZ = movementDirection.zCoord * speed; if (!isPreview()) { - caster.velocityChanged = true; + if (caster instanceof EntityPlayerMP) { + sendPlayerVelocity(caster); + } else { + caster.velocityChanged = true; + } } } @@ -244,7 +272,11 @@ public abstract class AbilityMovement extends Ability { caster.motionX = 0; caster.motionZ = 0; if (!isPreview()) { - caster.velocityChanged = true; + if (caster instanceof EntityPlayerMP) { + sendPlayerVelocity(caster); + } else { + caster.velocityChanged = true; + } } }