From 60b846b35e9463f2351bbd8c203642d278dbb277 Mon Sep 17 00:00:00 2001 From: Vidal Date: Tue, 3 Feb 2026 05:43:50 -0300 Subject: [PATCH] - Added onDamageTaken to Ability - Reworked AbilityGuard - Added AbilityOrbBarrage - Updated translation - Updated onDamageTaken method calls --- src/api | 2 +- .../controllers/data/ability/Ability.java | 15 + .../data/ability/AbilityController.java | 1 + .../data/ability/type/AbilityGuard.java | 169 ++--- .../data/ability/type/AbilityOrbBarrage.java | 576 ++++++++++++++++++ src/main/java/noppes/npcs/DataAbilities.java | 18 +- .../java/noppes/npcs/ai/CombatHandler.java | 16 +- .../controllers/data/PlayerAbilityData.java | 18 +- .../animations/Ability_Guard_Active.json | 50 ++ .../animations/Ability_Guard_Counter.json | 203 ++++++ .../assets/customnpcs/lang/en_US.lang | 4 + 11 files changed, 986 insertions(+), 86 deletions(-) create mode 100644 src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbBarrage.java create mode 100644 src/main/resources/assets/customnpcs/animations/Ability_Guard_Active.json create mode 100644 src/main/resources/assets/customnpcs/animations/Ability_Guard_Counter.json diff --git a/src/api b/src/api index bf650319d..f9de3f4f3 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit bf650319da422790bff294d570dd879143fe8d4c +Subproject commit f9de3f4f326d8e45750d28695a5f212db70bdfca 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 007e4f2f4..2148a239d 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java @@ -2,6 +2,7 @@ package kamkeel.npcs.controllers.data.ability; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import kamkeel.npcs.controllers.data.ability.type.AbilityGuard; import kamkeel.npcs.controllers.data.telegraph.Telegraph; import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance; import kamkeel.npcs.controllers.data.telegraph.TelegraphType; @@ -148,6 +149,9 @@ public abstract class Ability implements IAbility { public void onComplete(EntityLivingBase caster, EntityLivingBase target) { } + public void onDamageTaken(EntityLivingBase caster, EntityLivingBase attacker, DamageSource source, float damage) { + } + /** * Apply damage to an entity with ability hit event support. * Fires the abilityHit script event, allowing scripts to modify or cancel the damage. @@ -427,6 +431,17 @@ public abstract class Ability implements IAbility { this::getActiveAnimationName, this::setActiveAnimationName) .tab("Effects")); + if (this instanceof AbilityGuard) { + defs.add(FieldDef.animSubGui("ability.counterAnimation", + () -> ((AbilityGuard) this).getCounterAnimationId(), + (id) -> ((AbilityGuard) this).setCounterAnimationId(id), + () -> ((AbilityGuard) this).getCounterAnimationName(), + (name) -> ((AbilityGuard) this).setCounterAnimationName(name)) + .tab("Effects") + .visibleWhen(() -> ((AbilityGuard) this).canCounter()) + ); + } + // Effects tab - Telegraph TelegraphType tType = getTelegraphType(); if (tType != null && tType != TelegraphType.NONE) { diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java index eba8795f7..50e41d45c 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java @@ -364,6 +364,7 @@ public class AbilityController implements IAbilityHandler { registerType("cnpc:projectile", AbilityProjectile::new); registerType("cnpc:orb", AbilityOrb::new); registerType("cnpc:dual_orb", AbilityOrbDual::new); + registerType("cnpc:orb_barrage", AbilityOrbBarrage::new); registerType("cnpc:dual_disc", AbilityDisc::new); registerType("cnpc:dual_disc", AbilityDiscDual::new); registerType("cnpc:laser_shot", AbilityLaserShot::new); diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java index 9e420741f..9d792f7c3 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityGuard.java @@ -1,14 +1,16 @@ package kamkeel.npcs.controllers.data.ability.type; -import kamkeel.npcs.controllers.data.ability.Ability; -import kamkeel.npcs.controllers.data.ability.LockMovementType; -import kamkeel.npcs.controllers.data.ability.TargetingMode; -import kamkeel.npcs.controllers.data.ability.UserType; +import kamkeel.npcs.controllers.data.ability.*; import kamkeel.npcs.controllers.data.telegraph.TelegraphType; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.DamageSource; import net.minecraft.world.World; +import noppes.npcs.api.handler.data.IAnimation; +import noppes.npcs.controllers.AnimationController; +import noppes.npcs.controllers.data.Animation; +import noppes.npcs.controllers.data.PlayerData; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.api.ability.type.IAbilityGuard; @@ -21,7 +23,6 @@ import cpw.mods.fml.relauncher.SideOnly; import java.util.Arrays; import java.util.List; -import java.util.Random; /** * Guard ability: Defensive stance that reduces incoming damage. @@ -29,23 +30,40 @@ import java.util.Random; */ public class AbilityGuard extends Ability implements IAbilityGuard { - private static final Random RANDOM = new Random(); - - // Type-specific parameters private int durationTicks = 60; private float damageReduction = 0.5f; - private boolean canCounter = false; - private CounterType counterType = CounterType.FLAT; + private AbilityGuard.CounterType counterType = AbilityGuard.CounterType.FLAT; private float counterValue = 6.0f; - private float counterChance = 0.3f; - private String counterSound = "random.wood_click"; + private String counterSound = "random.anvil_land"; + private boolean canCounter = false; + private int counterWindow = 10; private int counterAnimationId = -1; + private String counterAnimationName; // Runtime state private transient EntityLivingBase lastAttacker; private transient boolean counterTriggered; + private transient boolean counterEligible; private transient float lastDamageTaken; + public AbilityGuard() { + this.typeId = "ability.cnpc.guard"; + this.name = "Guard"; + this.targetingMode = TargetingMode.SELF; + this.lockMovement = LockMovementType.ACTIVE; + this.cooldownTicks = 0; + this.windUpTicks = 0; + this.interruptible = false; // Hard to interrupt while guarding + // No telegraph for guard - it's a defensive stance + this.telegraphType = TelegraphType.NONE; + this.showTelegraph = false; + this.windUpSound = "random.anvil_use"; + this.allowedBy = UserType.BOTH; + + this.activeAnimationName = "Ability_Guard_Active"; + this.counterAnimationName = "Ability_Guard_Counter"; + } + public enum CounterType { FLAT, PERCENT; @@ -60,22 +78,6 @@ public class AbilityGuard extends Ability implements IAbilityGuard { } } - public AbilityGuard() { - this.typeId = "ability.cnpc.guard"; - this.name = "Guard"; - this.targetingMode = TargetingMode.SELF; - this.lockMovement = LockMovementType.ACTIVE; - this.cooldownTicks = 0; - this.windUpTicks = 10; - this.interruptible = false; // Hard to interrupt while guarding - // No telegraph for guard - it's a defensive stance - this.telegraphType = TelegraphType.NONE; - this.showTelegraph = false; - this.windUpSound = "random.anvil_use"; - this.activeSound = "random.anvil_land"; - this.allowedBy = UserType.NPC_ONLY; - } - @Override public boolean isTargetingModeLocked() { return true; @@ -91,32 +93,28 @@ public class AbilityGuard extends Ability implements IAbilityGuard { lastAttacker = null; counterTriggered = false; lastDamageTaken = 0.0f; + counterEligible = true; } @Override public void onActiveTick(EntityLivingBase caster, EntityLivingBase target, World world, int tick) { - // Counter attack logic if triggered - if (canCounter && counterTriggered && lastAttacker != null && !world.isRemote) { + if (canCounter && counterEligible && counterTriggered && lastAttacker != null && !world.isRemote) { performCounter(caster, world); } - // Check if guard duration has ended + if (tick > counterWindow && counterEligible) { + counterEligible = false; + } + if (tick >= durationTicks) { signalCompletion(); } } - /** - * Called externally when the caster takes damage while guarding. - * This should be called from the damage handling code. - * - * @param attacker The entity that attacked - * @param damage The damage amount (after reduction) - */ public void onDamageTaken(EntityLivingBase caster, EntityLivingBase attacker, DamageSource source, float damage) { - if (!canCounter || attacker == null) return; + if (!isGuarding()) return; + if (!canCounter || attacker == null || !counterEligible) return; if (!isDirectHit(source)) return; - if (RANDOM.nextFloat() >= counterChance) return; lastAttacker = attacker; lastDamageTaken = damage; @@ -124,8 +122,14 @@ public class AbilityGuard extends Ability implements IAbilityGuard { // Counter will be performed in onActiveTick - don't self-interrupt } + private boolean isDirectHit(DamageSource source) { + if (source == null) return false; + if (source.isMagicDamage() || source.isFireDamage() || source.isExplosion()) return false; + return source.getEntity() instanceof EntityLivingBase; + } + private void performCounter(EntityLivingBase caster, World world) { - float counterDamage = counterType == CounterType.PERCENT + float counterDamage = counterType == AbilityGuard.CounterType.PERCENT ? lastDamageTaken * (counterValue / 100.0f) : counterValue; @@ -135,18 +139,33 @@ public class AbilityGuard extends Ability implements IAbilityGuard { } // Use shared animation utility - only available for NPCs if (caster instanceof EntityNPCInterface) { - ((EntityNPCInterface) caster).abilities.playAbilityAnimation(counterAnimationId); + if (getCounterAnimation() instanceof IAnimation) { + ((EntityNPCInterface) caster).abilities.playAbilityAnimation((Animation) getCounterAnimation()); + } + } else if (caster instanceof EntityPlayer) { + if (getCounterAnimation() instanceof IAnimation) { + PlayerData.get((EntityPlayer) caster).abilityData.playAbilityAnimation((Animation) getCounterAnimation()); + } } counterTriggered = false; + counterEligible = false; lastAttacker = null; lastDamageTaken = 0.0f; } - private boolean isDirectHit(DamageSource source) { - if (source == null) return false; - if (source.isMagicDamage() || source.isFireDamage() || source.isExplosion()) return false; - return source.getEntity() instanceof EntityLivingBase; + private IAnimation getCounterAnimation() { + if (AnimationController.Instance == null) return null; + + if (counterAnimationId > 0) { + return AnimationController.Instance.get(counterAnimationId); + } + + if (!counterAnimationName.isEmpty()) { + return AnimationController.Instance.get(counterAnimationName, true); + } + + return null; } /** @@ -154,7 +173,7 @@ public class AbilityGuard extends Ability implements IAbilityGuard { * Returns 0.5 for 50% reduction. */ public float getDamageReductionFactor() { - if (isExecuting() && getPhase() == kamkeel.npcs.controllers.data.ability.AbilityPhase.ACTIVE) { + if (isExecuting() && getPhase() == AbilityPhase.ACTIVE) { return damageReduction; } return 0.0f; @@ -164,7 +183,7 @@ public class AbilityGuard extends Ability implements IAbilityGuard { * Check if the caster is currently in guard stance. */ public boolean isGuarding() { - return isExecuting() && getPhase() == kamkeel.npcs.controllers.data.ability.AbilityPhase.ACTIVE; + return isExecuting() && getPhase() == AbilityPhase.ACTIVE; } @Override @@ -185,11 +204,12 @@ public class AbilityGuard extends Ability implements IAbilityGuard { nbt.setInteger("durationTicks", durationTicks); nbt.setFloat("damageReduction", damageReduction); nbt.setBoolean("canCounter", canCounter); + nbt.setInteger("counterWindow", counterWindow); nbt.setString("counterType", counterType.name()); nbt.setFloat("counterValue", counterValue); - nbt.setFloat("counterChance", counterChance); nbt.setString("counterSound", counterSound); nbt.setInteger("counterAnimationId", counterAnimationId); + nbt.setString("counterAnimationName", counterAnimationName); } @Override @@ -197,6 +217,7 @@ public class AbilityGuard extends Ability implements IAbilityGuard { this.durationTicks = nbt.hasKey("durationTicks") ? nbt.getInteger("durationTicks") : 60; this.damageReduction = nbt.hasKey("damageReduction") ? nbt.getFloat("damageReduction") : 0.5f; this.canCounter = nbt.hasKey("canCounter") && nbt.getBoolean("canCounter"); + this.counterWindow = nbt.hasKey("counterWindow") ? nbt.getInteger("counterWindow") : 10; try { this.counterType = CounterType.valueOf(nbt.getString("counterType")); } catch (Exception e) { @@ -209,9 +230,11 @@ public class AbilityGuard extends Ability implements IAbilityGuard { } else { this.counterValue = 6.0f; } - this.counterChance = nbt.hasKey("counterChance") ? nbt.getFloat("counterChance") : 0.3f; this.counterSound = nbt.hasKey("counterSound") ? nbt.getString("counterSound") : "random.wood_click"; this.counterAnimationId = nbt.hasKey("counterAnimationId") ? nbt.getInteger("counterAnimationId") : -1; + this.counterAnimationName = nbt.hasKey("counterAnimationName") ? + nbt.getString("counterAnimationName") : + "Ability_Guard_Counter"; } // Getters & Setters @@ -223,17 +246,26 @@ public class AbilityGuard extends Ability implements IAbilityGuard { this.durationTicks = Math.max(1, durationTicks); } + @Override + public int getCounterWindow() { + return this.counterWindow; + } + + @Override + public void setCounterWindow(int ticks) { + this.counterWindow = Math.min(durationTicks, ticks); + } + + @Override public float getDamageReduction() { return damageReduction; } + @Override public void setDamageReduction(float damageReduction) { this.damageReduction = damageReduction; } - public boolean isCanCounter() { - return canCounter; - } @Override public boolean canCounter() { @@ -271,14 +303,6 @@ public class AbilityGuard extends Ability implements IAbilityGuard { this.counterValue = counterValue; } - public float getCounterChance() { - return counterChance; - } - - public void setCounterChance(float counterChance) { - this.counterChance = counterChance; - } - public String getCounterSound() { return counterSound; } @@ -295,6 +319,14 @@ public class AbilityGuard extends Ability implements IAbilityGuard { this.counterAnimationId = counterAnimationId; } + public String getCounterAnimationName() { + return counterAnimationName; + } + + public void setCounterAnimationName(String name) { + this.counterAnimationName = name; + } + @SideOnly(Side.CLIENT) @Override public List getFieldDefinitions() { @@ -302,20 +334,19 @@ public class AbilityGuard extends Ability implements IAbilityGuard { FieldDef.intField("ability.duration", this::getDurationTicks, this::setDurationTicks).range(1, 1000), FieldDef.floatField("ability.damageReduction", this::getDamageReduction, this::setDamageReduction), FieldDef.section("ability.section.counter"), - FieldDef.boolField("gui.enabled", this::isCanCounter, this::setCanCounter).hover("ability.hover.canCounter"), + FieldDef.boolField("gui.enabled", this::canCounter, this::setCanCounter).hover("ability.hover.canCounter"), FieldDef.enumField("gui.type", CounterType.class, this::getCounterTypeEnum, this::setCounterTypeEnum) - .hover("ability.hover.counterType").visibleWhen(this::isCanCounter), + .hover("ability.hover.counterType").visibleWhen(this::canCounter), FieldDef.row( FieldDef.floatField("gui.value", this::getCounterValue, this::setCounterValue) - .visibleWhen(this::isCanCounter), - FieldDef.floatField("gui.chance", this::getCounterChance, this::setCounterChance) - .visibleWhen(this::isCanCounter) + .visibleWhen(this::canCounter), + FieldDef.intField("ability.counterWindow", this::getCounterWindow, this::setCounterWindow) + .visibleWhen(this::canCounter) ), FieldDef.stringField("gui.sound", this::getCounterSound, this::setCounterSound) - .visibleWhen(this::isCanCounter), - FieldDef.intField("gui.animation", this::getCounterAnimationId, this::setCounterAnimationId) - .visibleWhen(this::isCanCounter), + .visibleWhen(this::canCounter), AbilityFieldDefs.effectsListField("ability.effects", this::getEffects, this::setEffects) + ); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbBarrage.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbBarrage.java new file mode 100644 index 000000000..f0425fa9a --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityOrbBarrage.java @@ -0,0 +1,576 @@ +package kamkeel.npcs.controllers.data.ability.type; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import kamkeel.npcs.controllers.data.ability.Ability; +import kamkeel.npcs.controllers.data.ability.AnchorPoint; +import kamkeel.npcs.controllers.data.ability.LockMovementType; +import kamkeel.npcs.controllers.data.ability.TargetingMode; +import kamkeel.npcs.controllers.data.ability.data.*; +import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs; +import kamkeel.npcs.controllers.data.telegraph.Telegraph; +import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance; +import kamkeel.npcs.controllers.data.telegraph.TelegraphType; +import kamkeel.npcs.entity.EntityAbilityOrb; +import kamkeel.npcs.util.AnchorPointHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.Vec3; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; +import noppes.npcs.client.gui.builder.FieldDef; +import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.util.ValueUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AbilityOrbBarrage extends Ability { + + // Ability-specific properties + private float orbSize = 0.5f; + private int orbAmount = 16; + private int firingSpeed = 2; + private boolean dualCharging = true; + + // Data classes for energy properties + private final EnergyColorData colorData = new EnergyColorData(); + private final EnergyCombatData combatData = new EnergyCombatData(4.0f, 0.0f, 0.0f, false, 1.5f, 0.5f); + private final EnergyHomingData homingData = new EnergyHomingData(); + private final EnergyLightningData lightningData = new EnergyLightningData(); + private final EnergyLifespanData lifespanData = new EnergyLifespanData(); + private final EnergyAnchorData[] anchorData = new EnergyAnchorData[]{ + new EnergyAnchorData(AnchorPoint.RIGHT_HAND), new EnergyAnchorData(AnchorPoint.LEFT_HAND) + }; + + // Array to store entities + private transient List orbEntities = new ArrayList<>(); + private transient EntityAbilityOrb[] chargingEntities = null; + + // Field to keep track of the currently firing orb + private int currentOrb = 0; + + public AbilityOrbBarrage() { + this.typeId = "ability.cnpc.orb_barrage"; + this.name = "Orb Barrage"; + this.targetingMode = TargetingMode.AGGRO_TARGET; + this.maxRange = 75.0f; + this.minRange = 5.0f; + this.cooldownTicks = 0; + this.windUpTicks = 30; + this.lockMovement = LockMovementType.WINDUP; + this.telegraphType = TelegraphType.CIRCLE; + this.showTelegraph = true; + // Default built-in animation + this.windUpAnimationName = "Ability_BeamDual_Windup"; + this.activeAnimationName = "Ability_BeamDual_Active"; + } + +// @Override +// @SideOnly(Side.CLIENT) +// public SubGuiAbilityConfig createConfigGui(IAbilityConfigCallback callback) { +// return new SubGuiAbilityOrbBarrage(this, callback); +// } + + @Override + public boolean isTargetingModeLocked() { + return true; + } + + @Override + public TargetingMode[] getAllowedTargetingModes() { + return new TargetingMode[]{TargetingMode.AGGRO_TARGET}; + } + + @Override + public void onExecute(EntityLivingBase caster, EntityLivingBase target, World world) { + if (world.isRemote) { + signalCompletion(); + return; + } + + for (int i = 0; i < chargingEntities.length; i++) { + EntityAbilityOrb orbEntity = chargingEntities[i]; + + if (orbEntity != null && !orbEntity.isDead) { + orbEntity.setDead(); + } + + chargingEntities[i] = null; + } + } + + @Override + public void onWindUpTick(EntityLivingBase caster, EntityLivingBase target, World world, int tick) { + if (world.isRemote) return; + + // Spawn orb in charging mode on first tick of windup + if (tick == 1) { + // Create orb in charging mode - follows caster based on anchor point during windup + + if (dualCharging) { + chargingEntities = new EntityAbilityOrb[2]; + } else { + chargingEntities = new EntityAbilityOrb[1]; + } + + for (int i = 0; i < chargingEntities.length; i++) { + Vec3 spawnPos = AnchorPointHelper.calculateAnchorPosition(caster, anchorData[i]); + + EntityAbilityOrb orbEntity = new EntityAbilityOrb( + world, caster, target, + spawnPos.xCoord, spawnPos.yCoord, spawnPos.zCoord, orbSize, + colorData, combatData, homingData, lightningData, lifespanData + ); + + orbEntity.setupCharging(anchorData[i], windUpTicks); + + chargingEntities[i] = orbEntity; + world.spawnEntityInWorld(orbEntity); + } + } + } + + @Override + public void onActiveTick(EntityLivingBase caster, EntityLivingBase target, World world, int tick) { + if (!orbEntities.isEmpty()) { + boolean allDead = true; + + for (EntityAbilityOrb orbEntity : orbEntities) { + if (orbEntity != null && !orbEntity.isDead) { + allDead = false; + break; + } + } + + if (allDead && currentOrb >= orbAmount) { + signalCompletion(); + return; + } + } + + if (currentOrb >= orbAmount) { + return; + } + + int ticksPerOrb = Math.max(1, 20 / firingSpeed); + + if (tick % ticksPerOrb != 0) { + return; + } + + EnergyAnchorData anchor; + + if (dualCharging) { + anchor = anchorData[currentOrb % 2]; + } else { + anchor = anchorData[0]; + } + + Vec3 spawnPos = AnchorPointHelper.calculateAnchorPosition(caster, anchor); + + EntityAbilityOrb orb = new EntityAbilityOrb( + world, caster, target, spawnPos.xCoord, spawnPos.yCoord, spawnPos.zCoord, + orbSize, colorData, combatData, homingData, lightningData, lifespanData + ); + + world.spawnEntityInWorld(orb); + + orbEntities.add(orb); + currentOrb++; + } + + @Override + public void cleanup() { + currentOrb = 0; + + if (orbEntities != null && !orbEntities.isEmpty()) { + for (EntityAbilityOrb orbEntity : orbEntities) { + if (orbEntity != null && !orbEntity.isDead) { + orbEntity.setDead(); + } + } + orbEntities.clear(); + } + + if (chargingEntities != null) { + for (int i = 0; i < chargingEntities.length; i++) { + if (chargingEntities[i] != null && !chargingEntities[i].isDead) { + chargingEntities[i].setDead(); + } + chargingEntities[i] = null; + } + chargingEntities = null; + } + } + + @Override + public TelegraphInstance createTelegraph(EntityLivingBase caster, EntityLivingBase target) { + if (!showTelegraph || telegraphType == TelegraphType.NONE || target == null) { + return null; + } + + // Create small circle telegraph at target position + Telegraph telegraph = Telegraph.circle(orbSize * 1.5f); + telegraph.setDurationTicks(windUpTicks); + telegraph.setColor(windUpColor); + telegraph.setWarningColor(activeColor); + telegraph.setWarningStartTick(Math.max(5, windUpTicks / 4)); + telegraph.setHeightOffset(telegraphHeightOffset); + + // Position at target and follow target during windup + TelegraphInstance instance = new TelegraphInstance(telegraph, target.posX, target.posY, target.posZ, caster.rotationYaw); + instance.setCasterEntityId(caster.getEntityId()); + instance.setEntityIdToFollow(target.getEntityId()); + + return instance; + } + + @Override + public float getTelegraphRadius() { + return orbSize * 1.5f; + } + + @Override + public void writeTypeNBT(NBTTagCompound nbt) { + nbt.setFloat("orbSize", orbSize); + nbt.setInteger("orbAmount", orbAmount); + nbt.setInteger("firingSpeed", firingSpeed); + nbt.setBoolean("dualCharging", dualCharging); + colorData.writeNBT(nbt); + combatData.writeNBT(nbt); + homingData.writeNBT(nbt); + lightningData.writeNBT(nbt); + lifespanData.writeNBT(nbt); + + for (int i = 0; i < anchorData.length; i++) { + NBTTagCompound comp = new NBTTagCompound(); + anchorData[i].writeNBT(comp); + nbt.setTag("Anchor_" + i, comp); + } + } + + @Override + public void readTypeNBT(NBTTagCompound nbt) { + this.orbSize = nbt.hasKey("orbSize") ? nbt.getFloat("orbSize") : 1.0f; + this.orbAmount = nbt.hasKey("orbAmount") ? nbt.getInteger("orbAmount") : 16; + this.firingSpeed = nbt.hasKey("firingSpeed") ? nbt.getInteger("firingSpeed") : 2; + this.dualCharging = !nbt.hasKey("dualCharging") || nbt.getBoolean("dualCharging"); + colorData.readNBT(nbt); + combatData.readNBT(nbt); + homingData.readNBT(nbt); + lightningData.readNBT(nbt); + lifespanData.readNBT(nbt); + + for (int i = 0; i < anchorData.length; i++) { + if (nbt.hasKey("Anchor_" + i, Constants.NBT.TAG_COMPOUND)) { + anchorData[i].readNBT(nbt.getCompoundTag("Anchor_" + i)); + } else { + anchorData[i] = new EnergyAnchorData(i == 0 ? AnchorPoint.RIGHT_HAND : AnchorPoint.LEFT_HAND); + } + } + } + + // Getters & Setters + public float getOrbSpeed() { + return homingData.speed; + } + + public void setOrbSpeed(float orbSpeed) { + homingData.speed = orbSpeed; + } + + public float getOrbSize() { + return orbSize; + } + + public void setOrbSize(float orbSize) { + this.orbSize = orbSize; + } + + public int getOrbAmount() { return orbAmount; } + + public void setOrbAmount(int orbAmount) { this.orbAmount = orbAmount; } + + public int getFiringSpeed() { return firingSpeed; } + + public void setFiringSpeed(int firingSpeed) { this.firingSpeed = firingSpeed; } + + public boolean isDualCharging() { return dualCharging; } + + public void setDualCharging(boolean dualCharging) { this.dualCharging = dualCharging; } + + public float getMaxDistance() { + return lifespanData.maxDistance; + } + + public void setMaxDistance(float maxDistance) { + lifespanData.maxDistance = maxDistance; + } + + public int getMaxLifetime() { + return lifespanData.maxLifetime; + } + + public void setMaxLifetime(int maxLifetime) { + lifespanData.maxLifetime = maxLifetime; + } + + public float getDamage() { + return combatData.damage; + } + + public void setDamage(float damage) { + combatData.damage = damage; + } + + public float getKnockback() { + return combatData.knockback; + } + + public void setKnockback(float knockback) { + combatData.knockback = knockback; + } + + public float getKnockbackUp() { + return combatData.knockbackUp; + } + + public void setKnockbackUp(float knockbackUp) { + combatData.knockbackUp = knockbackUp; + } + + public boolean isHoming() { + return homingData.homing; + } + + public void setHoming(boolean homing) { + homingData.homing = homing; + } + + public float getHomingStrength() { + return homingData.homingStrength; + } + + public void setHomingStrength(float homingStrength) { + homingData.homingStrength = homingStrength; + } + + public float getHomingRange() { + return homingData.homingRange; + } + + public void setHomingRange(float homingRange) { + homingData.homingRange = homingRange; + } + + public boolean isExplosive() { + return combatData.explosive; + } + + public void setExplosive(boolean explosive) { + combatData.explosive = explosive; + } + + public float getExplosionRadius() { + return combatData.explosionRadius; + } + + public void setExplosionRadius(float explosionRadius) { + combatData.explosionRadius = explosionRadius; + } + + public float getExplosionDamageFalloff() { + return combatData.explosionDamageFalloff; + } + + public void setExplosionDamageFalloff(float explosionDamageFalloff) { + combatData.explosionDamageFalloff = explosionDamageFalloff; + } + + public int getInnerColor() { + return colorData.innerColor; + } + + public void setInnerColor(int innerColor) { + colorData.innerColor = innerColor; + } + + public int getOuterColor() { + return colorData.outerColor; + } + + public void setOuterColor(int outerColor) { + colorData.outerColor = outerColor; + } + + public float getOuterColorWidth() { + return colorData.outerColorWidth; + } + + public void setOuterColorWidth(float outerColorWidth) { + colorData.outerColorWidth = outerColorWidth; + } + + public float getOuterColorAlpha() { + return colorData.outerColorAlpha; + } + + public void setOuterColorAlpha(float outerColorAlpha) { + colorData.outerColorAlpha = outerColorAlpha; + } + + public boolean isOuterColorEnabled() { + return colorData.outerColorEnabled; + } + + public void setOuterColorEnabled(boolean outerColorEnabled) { + colorData.outerColorEnabled = outerColorEnabled; + } + + public float getRotationSpeed() { + return colorData.rotationSpeed; + } + + public void setRotationSpeed(float rotationSpeed) { + colorData.rotationSpeed = rotationSpeed; + } + + public boolean hasLightningEffect() { + return lightningData.lightningEffect; + } + + public void setLightningEffect(boolean lightningEffect) { + lightningData.lightningEffect = lightningEffect; + } + + public float getLightningDensity() { + return lightningData.lightningDensity; + } + + public void setLightningDensity(float lightningDensity) { + lightningData.lightningDensity = lightningDensity; + } + + public float getLightningRadius() { + return lightningData.lightningRadius; + } + + public void setLightningRadius(float lightningRadius) { + lightningData.lightningRadius = lightningRadius; + } + + public int getLightningFadeTime() { + return lightningData.lightningFadeTime; + } + + public void setLightningFadeTime(int lightningFadeTime) { + lightningData.lightningFadeTime = lightningFadeTime; + } + + private EnergyAnchorData getAnchorData(int orb) { + orb = ValueUtil.clamp(orb, 0, 1); + return anchorData[orb]; + } + + public AnchorPoint getAnchorPointEnum(int orb) { return getAnchorData(orb).anchorPoint; } + public void setAnchorPointEnum(int orb, AnchorPoint anchorPoint) { getAnchorData(orb).anchorPoint = anchorPoint; } + public float getAnchorOffsetX(int orb) { return getAnchorData(orb).anchorOffsetX; } + public void setAnchorOffsetX(int orb, float x) { getAnchorData(orb).anchorOffsetX = x; } + public float getAnchorOffsetY(int orb) { return getAnchorData(orb).anchorOffsetY; } + public void setAnchorOffsetY(int orb, float y) { getAnchorData(orb).anchorOffsetY = y; } + public float getAnchorOffsetZ(int orb) { return getAnchorData(orb).anchorOffsetZ; } + public void setAnchorOffsetZ(int orb, float z) { getAnchorData(orb).anchorOffsetZ = z; } + + public AnchorPoint getAnchorPointEnum() { return getAnchorPointEnum(0); } + public void setAnchorPointEnum(AnchorPoint anchorPoint) { setAnchorPointEnum(0, anchorPoint); } + public float getAnchorOffsetX() { return getAnchorOffsetX(0); } + public void setAnchorOffsetX(float x) { setAnchorOffsetX(0, x); } + public float getAnchorOffsetY() { return getAnchorOffsetY(0); } + public void setAnchorOffsetY(float y) { setAnchorOffsetY(0, y); } + public float getAnchorOffsetZ() { return getAnchorOffsetZ(0); } + public void setAnchorOffsetZ(float z) { setAnchorOffsetZ(0, z); } + + //@Override + public int getAnchorPoint(int orb) { return getAnchorData(orb).anchorPoint.ordinal(); } + public int getAnchorPoint() { return getAnchorData(0).anchorPoint.ordinal(); } + + //@Override + public void setAnchorPoint(int orb, int point) { getAnchorData(orb).anchorPoint = AnchorPoint.fromOrdinal(point); } + public void setAnchorPoint(int point) { getAnchorData(0).anchorPoint = AnchorPoint.fromOrdinal(point); } + + @Override + @SideOnly(Side.CLIENT) + public Entity createPreviewEntity(EntityNPCInterface npc) { + if (npc == null || npc.worldObj == null) return null; + + EntityAbilityOrb orb = new EntityAbilityOrb(npc.worldObj); + orb.setupPreview(npc, orbSize, colorData, lightningData, anchorData[0], windUpTicks); + return orb; + } + + @Override + public int getPreviewActiveDuration() { + return lifespanData.maxLifetime > 0 ? Math.min(lifespanData.maxLifetime, 100) : 100; + } + + @SideOnly(Side.CLIENT) + @Override + public List getFieldDefinitions() { + return Arrays.asList( + // Type tab + FieldDef.row( + FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage), + FieldDef.floatField("stats.speed", this::getOrbSpeed, this::setOrbSpeed) + ), + FieldDef.row( + FieldDef.floatField("stats.size", this::getOrbSize, this::setOrbSize), + FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback) + ), + FieldDef.row( + FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance), + FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime) + ), + FieldDef.section("ability.section.barrage"), + FieldDef.row( + FieldDef.intField("ability.amount", this::getOrbAmount, this::setOrbAmount), + FieldDef.intField("ability.firingSpeed", this::getFiringSpeed, this::setFiringSpeed) + ), + FieldDef.boolField("ability.dualCharge", this::isDualCharging, this::setDualCharging), + FieldDef.section("ability.section.homing"), + FieldDef.boolField("gui.enabled", this::isHoming, this::setHoming).hover("ability.hover.homing"), + FieldDef.floatField("gui.strength", this::getHomingStrength, this::setHomingStrength).visibleWhen(this::isHoming), + FieldDef.section("ability.section.explosive"), + FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"), + FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius).visibleWhen(this::isExplosive), + AbilityFieldDefs.effectsListField("ability.effects", this::getEffects, this::setEffects), + + + // Visual tab + FieldDef.enumField("ability.anchorPoint", AnchorPoint.class, this::getAnchorPointEnum, this::setAnchorPointEnum) + .tab("ability.tab.visual"), + FieldDef.section("ability.section.colors").tab("ability.tab.visual"), + FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor).tab("ability.tab.visual"), + FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled).tab("ability.tab.visual"), + FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor) + .tab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled), + FieldDef.row( + FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth) + .visibleWhen(this::isOuterColorEnabled), + FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha) + .range(0, 1).visibleWhen(this::isOuterColorEnabled) + ).tab("ability.tab.visual"), + FieldDef.section("ability.section.effects").tab("ability.tab.visual"), + FieldDef.floatField("ability.rotationSpeed", this::getRotationSpeed, this::setRotationSpeed).tab("ability.tab.visual"), + FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect).tab("ability.tab.visual"), + FieldDef.row( + FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity) + .range(0.01f, 5.0f).visibleWhen(this::hasLightningEffect), + FieldDef.floatField("gui.radius", this::getLightningRadius, this::setLightningRadius) + .range(0.1f, 10.0f).visibleWhen(this::hasLightningEffect) + ).tab("ability.tab.visual") + ); + } +} diff --git a/src/main/java/noppes/npcs/DataAbilities.java b/src/main/java/noppes/npcs/DataAbilities.java index 8c44c482e..635b741ac 100644 --- a/src/main/java/noppes/npcs/DataAbilities.java +++ b/src/main/java/noppes/npcs/DataAbilities.java @@ -429,6 +429,14 @@ public class DataAbilities { playAbilityAnimation((Animation) AnimationController.Instance.get(animation)); } + public void playAbilityAnimation(String animation) { + if (animation.isEmpty()) return; + if (AnimationController.Instance == null) return; + if (AnimationController.Instance.get(animation, true) == null) return; + + playAbilityAnimation((Animation) AnimationController.Instance.get(animation, true)); + } + /** * Stop any currently playing ability animation. */ @@ -501,18 +509,12 @@ public class DataAbilities { // Track hit for hit count condition recordHit(); - // Check if currently executing a Guard ability and notify it of damage taken - if (currentAbility instanceof AbilityGuard) { - AbilityGuard guard = (AbilityGuard) currentAbility; - if (guard.isGuarding() && source.getEntity() instanceof EntityLivingBase) { - guard.onDamageTaken(npc, (EntityLivingBase) source.getEntity(), source, amount); - } - } - if (currentAbility == null || !currentAbility.isExecuting()) { return false; } + currentAbility.onDamageTaken(npc, (EntityLivingBase) source.getEntity(), source, amount); + if (currentAbility.canInterrupt(source)) { interruptCurrentAbility(source, amount); return true; diff --git a/src/main/java/noppes/npcs/ai/CombatHandler.java b/src/main/java/noppes/npcs/ai/CombatHandler.java index 750725551..ff81e4821 100644 --- a/src/main/java/noppes/npcs/ai/CombatHandler.java +++ b/src/main/java/noppes/npcs/ai/CombatHandler.java @@ -1,6 +1,7 @@ package noppes.npcs.ai; import kamkeel.npcs.addon.DBCAddon; +import kamkeel.npcs.controllers.data.ability.type.AbilityGuard; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; @@ -80,13 +81,14 @@ public class CombatHandler { } // Handle guard counter before interruption - if (npc.abilities.getCurrentAbility() instanceof kamkeel.npcs.controllers.data.ability.type.AbilityGuard) { - kamkeel.npcs.controllers.data.ability.type.AbilityGuard guard = - (kamkeel.npcs.controllers.data.ability.type.AbilityGuard) npc.abilities.getCurrentAbility(); - if (guard.isGuarding() && e instanceof EntityLivingBase) { - guard.onDamageTaken(npc, (EntityLivingBase) e, source, damageAmount); - } - } + +// if (npc.abilities.getCurrentAbility() instanceof AbilityGuard) { +// AbilityGuard guard = +// (AbilityGuard) npc.abilities.getCurrentAbility(); +// if (guard.isGuarding() && e instanceof EntityLivingBase) { +// guard.onDamageTaken(npc, (EntityLivingBase) e, source, damageAmount); +// } +// } // Check for ability interruption npc.abilities.onDamage(source, damageAmount); diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java index 45f9dfe44..724b054c0 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java @@ -453,13 +453,29 @@ public class PlayerAbilityData implements IPlayerAbilityData { // ANIMATION // ═══════════════════════════════════════════════════════════════════ - private void playAbilityAnimation(Animation animation) { + public void playAbilityAnimation(Animation animation) { if (animation == null || AnimationController.Instance == null) return; playerData.animationData.setEnabled(true); playerData.animationData.setAnimation(animation); playerData.animationData.updateClient(); } + public void playAbilityAnimation(int animation) { + if (animation < 0) return; + if (AnimationController.Instance == null) return; + if (AnimationController.Instance.get(animation) == null) return; + + playAbilityAnimation((Animation) AnimationController.Instance.get(animation)); + } + + public void playAbilityAnimation(String animation) { + if (animation.isEmpty()) return; + if (AnimationController.Instance == null) return; + if (AnimationController.Instance.get(animation, true) == null) return; + + playAbilityAnimation((Animation) AnimationController.Instance.get(animation, true)); + } + private void stopAbilityAnimation() { playerData.animationData.setAnimation(null); playerData.animationData.updateClient(); diff --git a/src/main/resources/assets/customnpcs/animations/Ability_Guard_Active.json b/src/main/resources/assets/customnpcs/animations/Ability_Guard_Active.json new file mode 100644 index 000000000..258320341 --- /dev/null +++ b/src/main/resources/assets/customnpcs/animations/Ability_Guard_Active.json @@ -0,0 +1,50 @@ +{ + "Loop": 0, + "Speed": 1.0f, + "Frames": [ + { + "ColorMarker": 16777215, + "Comment": "", + "Speed": 2.0f, + "FrameParts": [ + { + "Pivot2": -3.0f, + "Part": "RIGHT_ARM", + "Rotation1": -80.0f, + "Rotation0": -120.0f, + "Pivot0": 2.0f, + "Pivot1": 2.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": -3.0f, + "Part": "LEFT_ARM", + "Rotation1": 80.0f, + "Rotation0": -120.0f, + "Pivot0": -2.0f, + "Pivot1": 2.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": 0.0f, + "Part": "HEAD", + "Rotation1": 0.0f, + "Rotation0": 12.0f, + "Pivot0": 0.0f, + "Pivot1": 0.0f, + "Rotation2": 0.0f + } + ], + "Smooth": 0b, + "Duration": 10 + } + ], + "CurrentFrameTime": 0, + "CurrentFrame": 0, + "Smooth": 0b, + "ID": 18, + "WhileAttacking": 1b, + "WhileWalking": 1b, + "Name": "Ability_Guard_Active", + "WhileStanding": 1b +} diff --git a/src/main/resources/assets/customnpcs/animations/Ability_Guard_Counter.json b/src/main/resources/assets/customnpcs/animations/Ability_Guard_Counter.json new file mode 100644 index 000000000..9194dfd72 --- /dev/null +++ b/src/main/resources/assets/customnpcs/animations/Ability_Guard_Counter.json @@ -0,0 +1,203 @@ +{ + "Loop": 2, + "Speed": 1.0f, + "Frames": [ + { + "ColorMarker": 16777215, + "Comment": "", + "Speed": 2.0f, + "FrameParts": [ + { + "Pivot2": 0.0f, + "Part": "LEFT_LEG", + "Rotation1": 0.0f, + "Rotation0": 0.0f, + "Pivot0": 0.0f, + "Pivot1": 0.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": -3.0f, + "Part": "RIGHT_ARM", + "Rotation1": -80.0f, + "Rotation0": -120.0f, + "Pivot0": 2.0f, + "Pivot1": 2.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": -3.0f, + "Part": "LEFT_ARM", + "Rotation1": 80.0f, + "Rotation0": -120.0f, + "Pivot0": -2.0f, + "Pivot1": 2.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": 0.0f, + "Part": "HEAD", + "Rotation1": 0.0f, + "Rotation0": 12.0f, + "Pivot0": 0.0f, + "Pivot1": 0.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": 0.0f, + "Part": "BODY", + "Rotation1": 0.0f, + "Rotation0": 0.0f, + "Pivot0": 0.0f, + "Pivot1": 0.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": 0.0f, + "Part": "RIGHT_LEG", + "Rotation1": 0.0f, + "Rotation0": 0.0f, + "Pivot0": 0.0f, + "Pivot1": 0.0f, + "Rotation2": 0.0f + } + ], + "Smooth": 0b, + "Duration": 3 + }, + { + "ColorMarker": 16777215, + "Comment": "", + "Speed": 2.0f, + "FrameParts": [ + { + "Pivot2": -3.0f, + "Part": "LEFT_LEG", + "Rotation1": 0.0f, + "Rotation0": 6.0f, + "Pivot0": 0.0f, + "Pivot1": 0.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": -3.0f, + "Part": "RIGHT_ARM", + "Rotation1": -20.0f, + "Rotation0": 90.0f, + "Pivot0": 0.0f, + "Pivot1": 3.0f, + "Rotation2": 60.0f + }, + { + "Pivot2": -3.0f, + "Part": "LEFT_ARM", + "Rotation1": 20.0f, + "Rotation0": 90.0f, + "Pivot0": 0.0f, + "Pivot1": 3.0f, + "Rotation2": -60.0f + }, + { + "Pivot2": -6.0f, + "Part": "HEAD", + "Rotation1": 0.0f, + "Rotation0": 25.0f, + "Pivot0": 0.0f, + "Pivot1": 3.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": -6.0f, + "Part": "BODY", + "Rotation1": 0.0f, + "Rotation0": 31.0f, + "Pivot0": 0.0f, + "Pivot1": 3.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": 0.0f, + "Part": "RIGHT_LEG", + "Rotation1": 0.0f, + "Rotation0": 31.0f, + "Pivot0": 0.0f, + "Pivot1": 1.0f, + "Rotation2": 0.0f + } + ], + "Smooth": 0b, + "Duration": 3 + }, + { + "ColorMarker": 16777215, + "Comment": "", + "Speed": 0.8f, + "FrameParts": [ + { + "Pivot2": -3.0f, + "Part": "LEFT_LEG", + "Rotation1": 0.0f, + "Rotation0": 6.0f, + "Pivot0": 0.0f, + "Pivot1": 0.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": -3.0f, + "Part": "RIGHT_ARM", + "Rotation1": -20.0f, + "Rotation0": 90.0f, + "Pivot0": 0.0f, + "Pivot1": 3.0f, + "Rotation2": 60.0f + }, + { + "Pivot2": -3.0f, + "Part": "LEFT_ARM", + "Rotation1": 20.0f, + "Rotation0": 90.0f, + "Pivot0": 0.0f, + "Pivot1": 3.0f, + "Rotation2": -60.0f + }, + { + "Pivot2": -6.0f, + "Part": "HEAD", + "Rotation1": 0.0f, + "Rotation0": 25.0f, + "Pivot0": 0.0f, + "Pivot1": 3.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": -6.0f, + "Part": "BODY", + "Rotation1": 0.0f, + "Rotation0": 31.0f, + "Pivot0": 0.0f, + "Pivot1": 3.0f, + "Rotation2": 0.0f + }, + { + "Pivot2": 0.0f, + "Part": "RIGHT_LEG", + "Rotation1": 0.0f, + "Rotation0": 31.0f, + "Pivot0": 0.0f, + "Pivot1": 1.0f, + "Rotation2": 0.0f + } + ], + "Smooth": 0b, + "Duration": 10 + } + ], + "CurrentFrameTime": 0, + "CurrentFrame": 0, + "Smooth": 0b, + "ID": 17, + "WhileAttacking": 1b, + "WhileWalking": 1b, + "Name": "Ability_Guard_Counter", + "WhileStanding": 1b +} diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index a95b71f67..afa79dad0 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -1608,6 +1608,7 @@ ability.cnpc.beam_dual=Beam Dual ability.cnpc.projectile=Projectile ability.cnpc.orb=Orb ability.cnpc.orb_dual=Orb Dual +ability.cnpc.orb_barrage=Orb Barrage ability.cnpc.charge=Charge ability.cnpc.dash=Dash ability.cnpc.teleport=Teleport @@ -1645,6 +1646,7 @@ ability.windUpSound=Wind Up Sound ability.activeSound=Active Sound ability.windUpAnimation=Wind Up Animation ability.activeAnimation=Active Animation +ability.counterAnimation=Counter Animation ability.showTelegraph=Show Telegraph ability.windUpColor=Wind Up Color ability.activeColor=Active Color @@ -1672,6 +1674,7 @@ ability.section.disc1=Disc 1 ability.section.disc2=Disc 2 ability.section.orb1=Orb 1 ability.section.orb2=Orb 2 +ability.section.barrage=Barrage ability.section.counter=Counter ability.section.area=Area ability.section.damage=Damage @@ -1763,6 +1766,7 @@ ability.pushStrength=Push Strength # Guard ability.damageReduction=Damage Reduction +ability.counterWindow=Counter Ticks # Heal ability.instantHeal=Instant Heal