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 f755bc370..11ceffea8 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java @@ -910,9 +910,9 @@ public abstract class Ability implements IAbility { if (phase == AbilityPhase.ACTIVE) { // Check if more burst iterations remain if (burstEnabled && burstAmount > 0 && burstIndex < burstAmount) { - if (!burstOverlap) { - cleanup(); - } + // Don't cleanup here - entities may still be in flight + // For non-overlap: cleaned up when next burst starts (in onExecute) + // For overlap: entities continue flying indefinitely burstIndex++; phase = AbilityPhase.BURST_DELAY; currentTick = 0; diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbstractEnergyProjectileAbility.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbstractEnergyProjectileAbility.java index 2be9256eb..f3ee572c2 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbstractEnergyProjectileAbility.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbstractEnergyProjectileAbility.java @@ -258,9 +258,13 @@ public abstract class AbstractEnergyProjectileAbility 5 && entity.worldObj != null + && entity.worldObj.getEntityByID(entity.getEntityId()) != entity) { + entity.setDead(); + continue; + } + + allDead = false; + break; + } + + // Failsafe timeout: force completion if active phase exceeded max possible flight time + if (!allDead) { + int lastFireTick = fireDelay > 0 ? fireDelay * (projectileCount - 1) : 0; + int maxFlightTime = lifespanData.maxLifetime > 0 ? lifespanData.maxLifetime : 200; + if (tick > lastFireTick + maxFlightTime + 20) { + cleanup(); + allDead = true; } } + if (allDead) { signalCompletion(); } diff --git a/src/main/java/noppes/npcs/DataAbilities.java b/src/main/java/noppes/npcs/DataAbilities.java index eb3082a59..78692162f 100644 --- a/src/main/java/noppes/npcs/DataAbilities.java +++ b/src/main/java/noppes/npcs/DataAbilities.java @@ -269,7 +269,7 @@ public class DataAbilities { // This allows entity-spawning abilities to immediately chain next burst // without each type needing to check burst state in their onActiveTick() // Wait until all staggered projectiles have been fired before auto-completing - if (currentAbility.isBurstEnabled() && currentAbility.isBurstOverlap() + if (currentAbility.isBurstEnabled() && currentAbility.getBurstIndex() < currentAbility.getBurstAmount() && currentAbility.getPhase() == AbilityPhase.ACTIVE && currentAbility.isReadyForBurstCompletion(currentAbility.getCurrentTick())) { diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java index aa8d4301d..f0a4c8c70 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java @@ -220,7 +220,7 @@ public class PlayerAbilityData implements IPlayerAbilityData { } // Auto-complete for burst overlap mode (entities fly independently) - if (currentAbility.isBurstEnabled() && currentAbility.isBurstOverlap() + if (currentAbility.isBurstEnabled() && currentAbility.getBurstIndex() < currentAbility.getBurstAmount() && currentAbility.getPhase() == AbilityPhase.ACTIVE && currentAbility.isReadyForBurstCompletion(currentAbility.getCurrentTick())) {