diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCounter.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCounter.java index f03543028..64fb10743 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCounter.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityCounter.java @@ -3,14 +3,21 @@ package kamkeel.npcs.controllers.data.ability.type; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import kamkeel.npcs.addon.DBCAddon; +import noppes.npcs.AbstractDataAbilities; import noppes.npcs.controllers.AnimationController; +import kamkeel.npcs.controllers.data.ability.Ability; import kamkeel.npcs.controllers.data.ability.enums.UserType; import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.DamageSource; import noppes.npcs.controllers.data.Animation; +import noppes.npcs.controllers.data.PlayerData; import noppes.npcs.api.ability.type.IAbilityCounter; import noppes.npcs.client.gui.builder.FieldDef; +import noppes.npcs.entity.EntityNPCInterface; import java.util.Arrays; import java.util.List; @@ -25,6 +32,11 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter { private int counterAnimationId = -1; private String counterAnimationName = "Ability_Guard_Counter"; + // True only while this counter is synchronously dispatching its retaliation damage. + // Another counter that is hit by that retaliation inspects this flag on the source + // entity's active counter to avoid reacting and creating infinite mutual retaliation. + private transient boolean retaliating; + public AbilityCounter() { this.typeId = "ability.cnpc.counter"; this.name = "Counter"; @@ -60,6 +72,35 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter { // DEFEND HOOKS // ═══════════════════════════════════════════════════════════════════ + @Override + protected boolean isValidDamageSource(DamageSource source) { + if (!super.isValidDamageSource(source)) { + return false; + } + // Break counter-vs-counter recursion: if the hit is itself a retaliation + // from another counter that is mid-dispatch, don't counter it back. + Entity sourceEntity = source.getEntity(); + AbstractDataAbilities abilities = getAbilitiesOf(sourceEntity); + if (abilities != null) { + Ability current = abilities.getCurrentAbility(); + if (current instanceof AbilityCounter && ((AbilityCounter) current).retaliating) { + return false; + } + } + return true; + } + + private static AbstractDataAbilities getAbilitiesOf(Entity entity) { + if (entity instanceof EntityPlayer) { + PlayerData pData = PlayerData.get((EntityPlayer) entity); + return pData != null ? pData.abilityData : null; + } + if (entity instanceof EntityNPCInterface) { + return ((EntityNPCInterface) entity).abilities; + } + return null; + } + @Override protected float performDefend(EntityLivingBase attacker, float amount) { // For PERCENT mode with DBC, use the full DBC-calculated damage, not vanilla base @@ -78,13 +119,24 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter { // Counter-attack through the standard ability damage pipeline. // DBCAbilityExtender handles DBC stat routing (ignore flags, scaling, etc.) automatically. - if (caster != null && attacker.isEntityAlive() && damage > 0) { - applyAbilityDamage(caster, attacker, damage, 0); + if (caster != null && attacker != null && attacker.isEntityAlive() && damage > 0) { + retaliating = true; + try { + applyAbilityDamage(caster, attacker, damage, 0); + } finally { + retaliating = false; + } } return 0; } + @Override + public void reset() { + super.reset(); + retaliating = false; + } + // ═══════════════════════════════════════════════════════════════════ // OVERRIDES // ═══════════════════════════════════════════════════════════════════ diff --git a/src/main/java/kamkeel/npcs/network/packets/request/category/CategoryItemsRequestPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/category/CategoryItemsRequestPacket.java index 9e06ab938..fef2a0f2e 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/category/CategoryItemsRequestPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/category/CategoryItemsRequestPacket.java @@ -73,7 +73,7 @@ public final class CategoryItemsRequestPacket extends AbstractPacket { // Send tag map for the items HashMap> tagMap = getTagMapByCat(type, categoryId); - if (tagMap != null && !tagMap.isEmpty()) { + if (tagMap != null) { TagController.sendCategoryTagMap((EntityPlayerMP) player, tagMap); } } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/tags/TagsGetPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/tags/TagsGetPacket.java index 4414131f1..85fe8e5c3 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/tags/TagsGetPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/tags/TagsGetPacket.java @@ -6,8 +6,6 @@ import io.netty.buffer.ByteBuf; import kamkeel.npcs.network.AbstractPacket; import kamkeel.npcs.network.PacketChannel; import kamkeel.npcs.network.PacketHandler; -import kamkeel.npcs.network.PacketUtil; -import kamkeel.npcs.network.enums.EnumItemPacketType; import kamkeel.npcs.network.enums.EnumRequestPacket; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -38,9 +36,6 @@ public final class TagsGetPacket extends AbstractPacket { @Override public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { - if (!PacketUtil.verifyItemPacket(packetName, player, EnumItemPacketType.WAND, EnumItemPacketType.CLONER)) - return; - NoppesUtilServer.sendTagDataAll((EntityPlayerMP) player); } diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java index 8b444cbf2..c979d5641 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java @@ -773,21 +773,21 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA } /** - * Fully reset ability state when changing dimensions. - * Clears cooldowns so the player can immediately use abilities in the new dimension. + * Fully reset transient ability execution state when changing dimensions. + * Cooldowns are preserved so dimension changes cannot bypass them. * Keeps active toggles (dimension change shouldn't reset ongoing effects). */ public void resetOnDimensionChange() { trackingAbilityPosition = false; - onEntityReconstructed(true, false); + onEntityReconstructed(false, false); } /** * Reset ability state when a large-distance teleport is detected during execution. - * Same behavior as dimension change — silent cleanup, clear cooldowns, keep toggles. + * Same behavior as dimension change: silent cleanup, preserve cooldowns, keep toggles. */ public void resetOnTeleport() { - onEntityReconstructed(true, false); + onEntityReconstructed(false, false); } /**