diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java index f0a4c8c70..22ec80c4b 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java @@ -54,6 +54,13 @@ public class PlayerAbilityData implements IPlayerAbilityData { */ private int selectedIndex = 0; + /** + * Tracks whether the current animation was started by an ability. + * Persisted to NBT so orphaned ability animations can be detected and + * cleared on login (ability state is transient, but animation state is not). + */ + private boolean playingAbilityAnimation = false; + // ═══════════════════════════════════════════════════════════════════ // RUNTIME STATE (not saved) // ═══════════════════════════════════════════════════════════════════ @@ -779,6 +786,7 @@ public class PlayerAbilityData implements IPlayerAbilityData { playerData.animationData.setEnabled(true); playerData.animationData.setAnimation(animation); playerData.animationData.updateClient(); + playingAbilityAnimation = true; } public void playAbilityAnimation(int animation) { @@ -800,6 +808,23 @@ public class PlayerAbilityData implements IPlayerAbilityData { private void stopAbilityAnimation() { playerData.animationData.setAnimation(null); playerData.animationData.updateClient(); + playingAbilityAnimation = false; + } + + /** + * Returns true if an ability-driven animation is currently playing. + * Used by PlayerData.onLogin() to detect orphaned ability animations. + */ + public boolean isPlayingAbilityAnimation() { + return playingAbilityAnimation; + } + + /** + * Clears an orphaned ability animation (e.g. after relog when the ability is gone). + * Stops the animation and resets the tracking flag. + */ + public void clearOrphanedAbilityAnimation() { + stopAbilityAnimation(); } private void playAbilitySound(EntityPlayer player, String sound) { @@ -819,6 +844,7 @@ public class PlayerAbilityData implements IPlayerAbilityData { } compound.setTag("PlayerAbilities", list); compound.setInteger("PlayerAbilitySelected", selectedIndex); + compound.setBoolean("AbilityAnimating", playingAbilityAnimation); } public void readFromNBT(NBTTagCompound compound) { @@ -836,5 +862,6 @@ public class PlayerAbilityData implements IPlayerAbilityData { if (selectedIndex >= unlockedAbilities.size()) { selectedIndex = Math.max(0, unlockedAbilities.size() - 1); } + playingAbilityAnimation = compound.getBoolean("AbilityAnimating"); } } diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerData.java b/src/main/java/noppes/npcs/controllers/data/PlayerData.java index fa4e7d961..02fd67563 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerData.java @@ -95,13 +95,21 @@ public class PlayerData implements IExtendedEntityProperties, IPlayerData { private boolean specialKeyDown = false; public void onLogin() { - // Continue playing animation for self when re-logging + // Handle animation state from previous session AnimationData animationData = this.animationData; if (animationData != null && animationData.isClientAnimating()) { - Animation currentAnimation = animationData.currentClientAnimation; - NBTTagCompound compound = currentAnimation.writeToNBT(); - animationData.viewAnimation(currentAnimation, animationData, compound, - animationData.isClientAnimating(), currentAnimation.currentFrame, currentAnimation.currentFrameTime); + if (abilityData.isPlayingAbilityAnimation()) { + // Ability animation was playing when player disconnected. + // Ability state is transient (lost on disconnect), so this animation + // is orphaned - clear it instead of replaying a stuck animation. + abilityData.clearOrphanedAbilityAnimation(); + } else { + // Non-ability animation (e.g. script-driven) - continue playing + Animation currentAnimation = animationData.currentClientAnimation; + NBTTagCompound compound = currentAnimation.writeToNBT(); + animationData.viewAnimation(currentAnimation, animationData, compound, + animationData.isClientAnimating(), currentAnimation.currentFrame, currentAnimation.currentFrameTime); + } } CustomEffectController controller = CustomEffectController.getInstance();