33import cpw .mods .fml .relauncher .Side ;
44import cpw .mods .fml .relauncher .SideOnly ;
55import kamkeel .npcs .addon .DBCAddon ;
6+ import noppes .npcs .AbstractDataAbilities ;
67import noppes .npcs .controllers .AnimationController ;
8+ import kamkeel .npcs .controllers .data .ability .Ability ;
79import kamkeel .npcs .controllers .data .ability .enums .UserType ;
810import kamkeel .npcs .controllers .data .ability .gui .AbilityFieldDefs ;
11+ import net .minecraft .entity .Entity ;
912import net .minecraft .entity .EntityLivingBase ;
13+ import net .minecraft .entity .player .EntityPlayer ;
1014import net .minecraft .nbt .NBTTagCompound ;
15+ import net .minecraft .util .DamageSource ;
1116import noppes .npcs .controllers .data .Animation ;
17+ import noppes .npcs .controllers .data .PlayerData ;
1218import noppes .npcs .api .ability .type .IAbilityCounter ;
1319import noppes .npcs .client .gui .builder .FieldDef ;
20+ import noppes .npcs .entity .EntityNPCInterface ;
1421
1522import java .util .Arrays ;
1623import java .util .List ;
@@ -25,6 +32,11 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
2532 private int counterAnimationId = -1 ;
2633 private String counterAnimationName = "Ability_Guard_Counter" ;
2734
35+ // True only while this counter is synchronously dispatching its retaliation damage.
36+ // Another counter that is hit by that retaliation inspects this flag on the source
37+ // entity's active counter to avoid reacting and creating infinite mutual retaliation.
38+ private transient boolean retaliating ;
39+
2840 public AbilityCounter () {
2941 this .typeId = "ability.cnpc.counter" ;
3042 this .name = "Counter" ;
@@ -60,6 +72,35 @@ public String toString() {
6072 // DEFEND HOOKS
6173 // ═══════════════════════════════════════════════════════════════════
6274
75+ @ Override
76+ protected boolean isValidDamageSource (DamageSource source ) {
77+ if (!super .isValidDamageSource (source )) {
78+ return false ;
79+ }
80+ // Break counter-vs-counter recursion: if the hit is itself a retaliation
81+ // from another counter that is mid-dispatch, don't counter it back.
82+ Entity sourceEntity = source .getEntity ();
83+ AbstractDataAbilities abilities = getAbilitiesOf (sourceEntity );
84+ if (abilities != null ) {
85+ Ability current = abilities .getCurrentAbility ();
86+ if (current instanceof AbilityCounter && ((AbilityCounter ) current ).retaliating ) {
87+ return false ;
88+ }
89+ }
90+ return true ;
91+ }
92+
93+ private static AbstractDataAbilities getAbilitiesOf (Entity entity ) {
94+ if (entity instanceof EntityPlayer ) {
95+ PlayerData pData = PlayerData .get ((EntityPlayer ) entity );
96+ return pData != null ? pData .abilityData : null ;
97+ }
98+ if (entity instanceof EntityNPCInterface ) {
99+ return ((EntityNPCInterface ) entity ).abilities ;
100+ }
101+ return null ;
102+ }
103+
63104 @ Override
64105 protected float performDefend (EntityLivingBase attacker , float amount ) {
65106 // For PERCENT mode with DBC, use the full DBC-calculated damage, not vanilla base
@@ -78,13 +119,24 @@ protected float performDefend(EntityLivingBase attacker, float amount) {
78119
79120 // Counter-attack through the standard ability damage pipeline.
80121 // DBCAbilityExtender handles DBC stat routing (ignore flags, scaling, etc.) automatically.
81- if (caster != null && attacker .isEntityAlive () && damage > 0 ) {
82- applyAbilityDamage (caster , attacker , damage , 0 );
122+ if (caster != null && attacker != null && attacker .isEntityAlive () && damage > 0 ) {
123+ retaliating = true ;
124+ try {
125+ applyAbilityDamage (caster , attacker , damage , 0 );
126+ } finally {
127+ retaliating = false ;
128+ }
83129 }
84130
85131 return 0 ;
86132 }
87133
134+ @ Override
135+ public void reset () {
136+ super .reset ();
137+ retaliating = false ;
138+ }
139+
88140 // ═══════════════════════════════════════════════════════════════════
89141 // OVERRIDES
90142 // ═══════════════════════════════════════════════════════════════════
0 commit comments