mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 18:57:32 +00:00
Counter, Dodge, Guard Abilities [WIP]
This commit is contained in:
1 parent
d3d9dd352a
commit
fc71fb5a33
12 files changed
+624
-421
No files matched your search
+1
-1
Submodule src/api updated: e504fc4b4a...27a41e3a8e.
@@ -21,6 +21,8 @@ import kamkeel.npcs.controllers.data.ability.CustomAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityCharge;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityCutter;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityDash;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityCounter;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityDodge;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityGuard;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityHazard;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityEffect;
|
||||
@@ -156,6 +158,8 @@ public class AbilityController implements IAbilityHandler {
|
||||
registerType(AbilityShockwave::new);
|
||||
|
||||
registerType(AbilityGuard::new);
|
||||
registerType(AbilityCounter::new);
|
||||
registerType(AbilityDodge::new);
|
||||
registerType(AbilityEffect::new);
|
||||
|
||||
registerType(AbilityHazard::new);
|
||||
|
||||
@@ -390,11 +390,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
* Unlike {@link #cleanup()}, this does NOT kill spawned entities (they may
|
||||
* still be in flight for overlap mode).
|
||||
*/
|
||||
public void resetForBurst() {
|
||||
}
|
||||
|
||||
public void onDamageTaken(EntityLivingBase caster, EntityLivingBase attacker, DamageSource source, float damage) {
|
||||
}
|
||||
public void resetForBurst() {}
|
||||
|
||||
/**
|
||||
* Apply damage to an entity with ability hit event support.
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
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.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.api.ability.type.IAbilityCounter;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Counter ability: Absorbs incoming damage and counter-attacks the attacker.
|
||||
*/
|
||||
public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
|
||||
|
||||
private CounterType counterType = CounterType.FLAT;
|
||||
private float counterValue = 6.0f;
|
||||
private int counterAnimationId = -1;
|
||||
private String counterAnimationName = "Ability_Guard_Counter";
|
||||
|
||||
public AbilityCounter() {
|
||||
this.typeId = "ability.cnpc.counter";
|
||||
this.name = "Counter";
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 20;
|
||||
this.allowedBy = UserType.BOTH;
|
||||
this.activeSound = "random.anvil_land";
|
||||
|
||||
this.activeAnimationName = "Ability_Guard_Active";
|
||||
this.defaultIconLayers = new DefaultIconLayer[]{
|
||||
new DefaultIconLayer("customnpcs:textures/gui/ability/guard.png")
|
||||
};
|
||||
}
|
||||
|
||||
public enum CounterType {
|
||||
FLAT,
|
||||
PERCENT;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (this) {
|
||||
case FLAT:
|
||||
return "ability.counter.flat";
|
||||
case PERCENT:
|
||||
return "ability.counter.percent";
|
||||
default:
|
||||
return name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// DEFEND HOOKS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
protected void onDefendTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float performDefend(float amount) {
|
||||
return amount;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// OVERRIDES
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public boolean hasDamage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// NBT
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
protected void writeSubTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setString("counterType", counterType.name());
|
||||
nbt.setFloat("counterValue", counterValue);
|
||||
nbt.setInteger("counterAnimationId", counterAnimationId);
|
||||
nbt.setString("counterAnimationName", resolveAnimationName(counterAnimationId, counterAnimationName));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readSubTypeNBT(NBTTagCompound nbt) {
|
||||
try {
|
||||
this.counterType = CounterType.valueOf(nbt.getString("counterType"));
|
||||
} catch (Exception e) {
|
||||
this.counterType = CounterType.FLAT;
|
||||
}
|
||||
this.counterValue = nbt.getFloat("counterValue");
|
||||
this.counterAnimationId = nbt.getInteger("counterAnimationId");
|
||||
this.counterAnimationName = nbt.getString("counterAnimationName");
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GUI
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
protected void getTypeDefinitions(List<FieldDef> defs) {
|
||||
defs.addAll(Arrays.asList(
|
||||
FieldDef.enumField("gui.type", CounterType.class, this::getCounterTypeEnum, this::setCounterTypeEnum)
|
||||
.hover("ability.hover.counterType"),
|
||||
FieldDef.floatField("gui.value", this::getCounterValue, this::setCounterValue),
|
||||
AbilityFieldDefs.effectsListField("ability.effects", this::getEffects, this::setEffects)
|
||||
));
|
||||
|
||||
FieldDef.insertAfter(defs, "ability.dazedAnimation",
|
||||
FieldDef.animSubGui("ability.counterAnimation",
|
||||
this::getCounterAnimationId, this::setCounterAnimationId,
|
||||
this::getCounterAnimationName, this::setCounterAnimationName)
|
||||
.tab("Effects"));
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GETTERS / SETTERS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
public CounterType getCounterTypeEnum() {
|
||||
return counterType;
|
||||
}
|
||||
|
||||
public void setCounterTypeEnum(CounterType counterType) {
|
||||
this.counterType = counterType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCounterType() {
|
||||
return counterType.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCounterType(int type) {
|
||||
CounterType[] values = CounterType.values();
|
||||
this.counterType = type >= 0 && type < values.length ? values[type] : CounterType.FLAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCounterValue() {
|
||||
return counterValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCounterValue(float counterValue) {
|
||||
this.counterValue = counterValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCounterAnimationId() {
|
||||
return counterAnimationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCounterAnimationId(int counterAnimationId) {
|
||||
this.counterAnimationId = counterAnimationId;
|
||||
}
|
||||
|
||||
public String getCounterAnimationName() {
|
||||
return counterAnimationName;
|
||||
}
|
||||
|
||||
public void setCounterAnimationName(String name) {
|
||||
this.counterAnimationName = name != null ? name : "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
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.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import noppes.npcs.api.ability.type.IAbilityDefend;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract base class for defensive abilities (Guard, Counter, Dodge).
|
||||
* <p>
|
||||
* {@link #onDefend} handles shared checks (physical melee only) then delegates
|
||||
* to {@link #performDefend(float)} which subclasses override for their specific behavior.
|
||||
*/
|
||||
public abstract class AbilityDefend extends Ability implements IAbilityDefend {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// SHARED PERSIST STATE
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
protected int durationTicks = 60;
|
||||
protected int maxHitAmount = 3;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// TRANSIENT RUNTIME STATE
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
protected transient int hitCount;
|
||||
protected transient EntityLivingBase lastAttacker;
|
||||
protected transient float lastDamageTaken;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// CONSTRUCTOR DEFAULTS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
protected AbilityDefend() {
|
||||
this.targetingMode = TargetingMode.SELF;
|
||||
this.lockMovement = LockMode.ACTIVE;
|
||||
this.interruptible = false;
|
||||
this.telegraphType = TelegraphType.NONE;
|
||||
this.showTelegraph = false;
|
||||
this.windUpTicks = 0;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// LIFECYCLE
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public void onExecute(EntityLivingBase caster, EntityLivingBase target) {
|
||||
hitCount = 0;
|
||||
lastAttacker = null;
|
||||
lastDamageTaken = 0.0f;
|
||||
onDefendStart(caster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActiveTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
|
||||
onDefendTick(caster, target, tick);
|
||||
|
||||
if (tick >= durationTicks) {
|
||||
signalCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
hitCount = 0;
|
||||
lastAttacker = null;
|
||||
lastDamageTaken = 0.0f;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// DEFEND HOOK
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Called when the caster is hit while defending.
|
||||
* Checks for physical melee damage only, then delegates to {@link #performDefend(float)}.
|
||||
*
|
||||
* @param attacker The entity that hit the caster
|
||||
* @param source The damage source
|
||||
* @param amount The original incoming damage
|
||||
* @return The modified damage to apply (unchanged if not physical melee)
|
||||
*/
|
||||
public final float onDefend(EntityLivingBase attacker, DamageSource source, float amount) {
|
||||
if (!isDefending())
|
||||
return amount;
|
||||
if (attacker == null || source == null)
|
||||
return amount;
|
||||
|
||||
// Only react to physical melee damage
|
||||
if (source.isMagicDamage() || source.isFireDamage() || source.isExplosion() || source.isProjectile())
|
||||
return amount;
|
||||
|
||||
return performDefend(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass-specific defend behavior. Called only for physical melee hits while defending.
|
||||
*
|
||||
* @param amount The incoming damage
|
||||
* @return The modified damage the caster should take
|
||||
*/
|
||||
protected abstract float performDefend(float amount);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// SUBCLASS HOOKS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
protected void onDefendStart(EntityLivingBase caster) {}
|
||||
|
||||
protected abstract void onDefendTick(EntityLivingBase caster, EntityLivingBase target, int tick);
|
||||
|
||||
/**
|
||||
* Subclass-specific NBT persistence.
|
||||
*/
|
||||
protected abstract void writeSubTypeNBT(NBTTagCompound nbt);
|
||||
protected abstract void readSubTypeNBT(NBTTagCompound nbt);
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
protected abstract void getTypeDefinitions(List<FieldDef> defs);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// STATE QUERIES
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public boolean isDefending() {
|
||||
return isExecuting() && getPhase() == AbilityPhase.ACTIVE;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// OVERRIDES
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public boolean hasDamage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowBurst() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTargetingModeLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetingMode[] getAllowedTargetingModes() {
|
||||
return new TargetingMode[]{TargetingMode.SELF};
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTelegraphRadius() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppresses the auto-play of activeSound on ACTIVE phase entry.
|
||||
* Defend abilities play their sound on hit reaction, not on phase start.
|
||||
*/
|
||||
@Override
|
||||
public boolean keepTelegraphDuringActive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// NBT
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public final void writeTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setInteger("durationTicks", durationTicks);
|
||||
nbt.setInteger("maxHitAmount", maxHitAmount);
|
||||
writeSubTypeNBT(nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void readTypeNBT(NBTTagCompound nbt) {
|
||||
this.durationTicks = nbt.hasKey("durationTicks") ? nbt.getInteger("durationTicks") : 60;
|
||||
this.maxHitAmount = nbt.hasKey("maxHitAmount") ? nbt.getInteger("maxHitAmount") : 3;
|
||||
readSubTypeNBT(nbt);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GUI
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public final void getAbilityDefinitions(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.intField("ability.duration", this::getDurationTicks, this::setDurationTicks).range(1, 6000));
|
||||
defs.add(FieldDef.intField("ability.maxHitAmount", this::getMaxHitAmount, this::setMaxHitAmount).range(0, 100)
|
||||
.hover("ability.hover.maxHitAmount"));
|
||||
|
||||
getTypeDefinitions(defs);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GETTERS / SETTERS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public int getDurationTicks() {
|
||||
return durationTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDurationTicks(int ticks) {
|
||||
this.durationTicks = Math.max(1, ticks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHitAmount() {
|
||||
return maxHitAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxHitAmount(int amount) {
|
||||
this.maxHitAmount = Math.max(0, amount);
|
||||
}
|
||||
|
||||
public int getHitCount() {
|
||||
return hitCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
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.enums.UserType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.api.ability.type.IAbilityDodge;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Dodge ability: Completely cancels incoming attacks and plays a random dodge animation.
|
||||
*/
|
||||
public class AbilityDodge extends AbilityDefend implements IAbilityDodge {
|
||||
|
||||
private int dodgeAnimation1Id = -1;
|
||||
private String dodgeAnimation1Name = "";
|
||||
private int dodgeAnimation2Id = -1;
|
||||
private String dodgeAnimation2Name = "";
|
||||
private int dodgeAnimation3Id = -1;
|
||||
private String dodgeAnimation3Name = "";
|
||||
|
||||
public AbilityDodge() {
|
||||
this.typeId = "ability.cnpc.dodge";
|
||||
this.name = "Dodge";
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 10;
|
||||
this.allowedBy = UserType.BOTH;
|
||||
|
||||
this.activeAnimationName = "";
|
||||
this.defaultIconLayers = new DefaultIconLayer[]{
|
||||
new DefaultIconLayer("customnpcs:textures/gui/ability/guard.png")
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// DEFEND HOOKS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
protected void onDefendTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float performDefend(float amount) {
|
||||
return amount;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// NBT
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
protected void writeSubTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setInteger("dodgeAnimation1Id", dodgeAnimation1Id);
|
||||
nbt.setString("dodgeAnimation1Name", resolveAnimationName(dodgeAnimation1Id, dodgeAnimation1Name));
|
||||
nbt.setInteger("dodgeAnimation2Id", dodgeAnimation2Id);
|
||||
nbt.setString("dodgeAnimation2Name", resolveAnimationName(dodgeAnimation2Id, dodgeAnimation2Name));
|
||||
nbt.setInteger("dodgeAnimation3Id", dodgeAnimation3Id);
|
||||
nbt.setString("dodgeAnimation3Name", resolveAnimationName(dodgeAnimation3Id, dodgeAnimation3Name));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readSubTypeNBT(NBTTagCompound nbt) {
|
||||
this.dodgeAnimation1Id = nbt.getInteger("dodgeAnimation1Id");
|
||||
this.dodgeAnimation1Name = nbt.getString("dodgeAnimation1Name");
|
||||
this.dodgeAnimation2Id = nbt.getInteger("dodgeAnimation2Id");
|
||||
this.dodgeAnimation2Name = nbt.getString("dodgeAnimation2Name");
|
||||
this.dodgeAnimation3Id = nbt.getInteger("dodgeAnimation3Id");
|
||||
this.dodgeAnimation3Name = nbt.getString("dodgeAnimation3Name");
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GUI
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
protected void getTypeDefinitions(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.section("ability.section.dodgeAnimations"));
|
||||
|
||||
FieldDef.insertAfter(defs, "ability.dazedAnimation",
|
||||
FieldDef.animSubGui("ability.dodgeAnimation1",
|
||||
this::getDodgeAnimation1Id, this::setDodgeAnimation1Id,
|
||||
this::getDodgeAnimation1Name, this::setDodgeAnimation1Name)
|
||||
.tab("Effects"));
|
||||
|
||||
FieldDef.insertAfter(defs, "ability.dodgeAnimation1",
|
||||
FieldDef.animSubGui("ability.dodgeAnimation2",
|
||||
this::getDodgeAnimation2Id, this::setDodgeAnimation2Id,
|
||||
this::getDodgeAnimation2Name, this::setDodgeAnimation2Name)
|
||||
.tab("Effects"));
|
||||
|
||||
FieldDef.insertAfter(defs, "ability.dodgeAnimation2",
|
||||
FieldDef.animSubGui("ability.dodgeAnimation3",
|
||||
this::getDodgeAnimation3Id, this::setDodgeAnimation3Id,
|
||||
this::getDodgeAnimation3Name, this::setDodgeAnimation3Name)
|
||||
.tab("Effects"));
|
||||
|
||||
FieldDef.modifyVisibility(defs, "ability.windUpAnimation", () -> false);
|
||||
FieldDef.modifyVisibility(defs, "ability.windUpSound", () -> false);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GETTERS / SETTERS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public int getDodgeAnimation1Id() {
|
||||
return dodgeAnimation1Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDodgeAnimation1Id(int animationId) {
|
||||
this.dodgeAnimation1Id = animationId;
|
||||
}
|
||||
|
||||
public String getDodgeAnimation1Name() {
|
||||
return dodgeAnimation1Name;
|
||||
}
|
||||
|
||||
public void setDodgeAnimation1Name(String name) {
|
||||
this.dodgeAnimation1Name = name != null ? name : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDodgeAnimation2Id() {
|
||||
return dodgeAnimation2Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDodgeAnimation2Id(int animationId) {
|
||||
this.dodgeAnimation2Id = animationId;
|
||||
}
|
||||
|
||||
public String getDodgeAnimation2Name() {
|
||||
return dodgeAnimation2Name;
|
||||
}
|
||||
|
||||
public void setDodgeAnimation2Name(String name) {
|
||||
this.dodgeAnimation2Name = name != null ? name : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDodgeAnimation3Id() {
|
||||
return dodgeAnimation3Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDodgeAnimation3Id(int animationId) {
|
||||
this.dodgeAnimation3Id = animationId;
|
||||
}
|
||||
|
||||
public String getDodgeAnimation3Name() {
|
||||
return dodgeAnimation3Name;
|
||||
}
|
||||
|
||||
public void setDodgeAnimation3Name(String name) {
|
||||
this.dodgeAnimation3Name = name != null ? name : "";
|
||||
}
|
||||
}
|
||||
@@ -2,288 +2,76 @@ 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.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
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.ability.type.IAbilityGuard;
|
||||
import noppes.npcs.api.handler.data.IAnimation;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
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.util.ValueUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Guard ability: Defensive stance that reduces incoming damage.
|
||||
* Can optionally counter-attack when hit.
|
||||
*/
|
||||
public class AbilityGuard extends Ability implements IAbilityGuard {
|
||||
public class AbilityGuard extends AbilityDefend implements IAbilityGuard {
|
||||
|
||||
private int durationTicks = 60;
|
||||
private float damageReduction = 0.5f;
|
||||
private AbilityGuard.CounterType counterType = AbilityGuard.CounterType.FLAT;
|
||||
private float counterValue = 6.0f;
|
||||
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 = LockMode.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.activeSound = "random.anvil_use";
|
||||
this.allowedBy = UserType.BOTH;
|
||||
|
||||
this.activeAnimationName = "Ability_Guard_Active";
|
||||
this.counterAnimationName = "Ability_Guard_Counter";
|
||||
this.defaultIconLayers = new DefaultIconLayer[]{
|
||||
new DefaultIconLayer("customnpcs:textures/gui/ability/guard.png")
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// DEFEND HOOKS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public boolean hasDamage() {
|
||||
return canCounter;
|
||||
protected void onDefendTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowBurst() {
|
||||
return false;
|
||||
protected float performDefend(float amount) {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public enum CounterType {
|
||||
FLAT,
|
||||
PERCENT;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (this) {
|
||||
case FLAT:
|
||||
return "ability.counter.flat";
|
||||
case PERCENT:
|
||||
return "ability.counter.percent";
|
||||
default:
|
||||
return name();
|
||||
}
|
||||
}
|
||||
}
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// NBT
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public boolean isTargetingModeLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetingMode[] getAllowedTargetingModes() {
|
||||
return new TargetingMode[]{TargetingMode.SELF};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecute(EntityLivingBase caster, EntityLivingBase target) {
|
||||
lastAttacker = null;
|
||||
counterTriggered = false;
|
||||
lastDamageTaken = 0.0f;
|
||||
counterEligible = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActiveTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
|
||||
if (canCounter && counterEligible && counterTriggered && lastAttacker != null && !caster.worldObj.isRemote) {
|
||||
performCounter(caster, caster.worldObj);
|
||||
}
|
||||
|
||||
if (tick > counterWindow && counterEligible) {
|
||||
counterEligible = false;
|
||||
}
|
||||
|
||||
if (tick >= durationTicks) {
|
||||
signalCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
// Just enough to see the counter animation
|
||||
if (counterTriggered && tick >= counterWindow + 10) {
|
||||
signalCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
public void onDamageTaken(EntityLivingBase caster, EntityLivingBase attacker, DamageSource source, float damage) {
|
||||
if (!isGuarding() || attacker == null) return;
|
||||
|
||||
lastAttacker = attacker;
|
||||
lastDamageTaken = damage;
|
||||
|
||||
// Players: damage reduction handled via LivingHurtEvent (event.ammount modification)
|
||||
// NPCs: called AFTER damageEntity() so health was already reduced — heal back here
|
||||
if (caster instanceof EntityPlayer) {
|
||||
if (canCounter && counterEligible && !counterTriggered && isDirectHit(source)) {
|
||||
counterTriggered = true;
|
||||
}
|
||||
} else {
|
||||
float reduction = ValueUtil.clamp(getDamageReductionFactor(), 0, damage);
|
||||
float newHealth = ValueUtil.clamp(caster.getHealth() + reduction, 0, caster.getMaxHealth());
|
||||
|
||||
if (canCounter && counterEligible && !counterTriggered && isDirectHit(source)) {
|
||||
caster.setHealth(ValueUtil.clamp(caster.getHealth() + damage, 0, caster.getMaxHealth()));
|
||||
counterTriggered = true;
|
||||
} else {
|
||||
caster.setHealth(newHealth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 == AbilityGuard.CounterType.PERCENT
|
||||
? lastDamageTaken * (counterValue / 100.0f)
|
||||
: counterValue;
|
||||
|
||||
boolean wasHit = applyAbilityDamage(caster, lastAttacker, counterDamage, 0.5f);
|
||||
if (wasHit && counterSound != null && !counterSound.isEmpty()) {
|
||||
world.playSoundAtEntity(caster, counterSound, 1.0f, 1.2f);
|
||||
}
|
||||
// Use shared animation utility - only available for NPCs
|
||||
if (caster instanceof EntityNPCInterface) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
counterEligible = false;
|
||||
lastAttacker = null;
|
||||
lastDamageTaken = 0.0f;
|
||||
}
|
||||
|
||||
private IAnimation getCounterAnimation() {
|
||||
if (AnimationController.Instance == null) return null;
|
||||
|
||||
if (counterAnimationId >= 0) {
|
||||
return AnimationController.Instance.get(counterAnimationId);
|
||||
}
|
||||
|
||||
if (counterAnimationName != null && !counterAnimationName.isEmpty()) {
|
||||
return AnimationController.Instance.get(counterAnimationName, true);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the damage reduction factor while guarding.
|
||||
* Returns 0.5 for 50% reduction.
|
||||
*/
|
||||
public float getDamageReductionFactor() {
|
||||
if (isExecuting() && getPhase() == AbilityPhase.ACTIVE) {
|
||||
return damageReduction;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the caster is currently in guard stance.
|
||||
*/
|
||||
public boolean isGuarding() {
|
||||
return isExecuting() && getPhase() == AbilityPhase.ACTIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
lastAttacker = null;
|
||||
counterTriggered = false;
|
||||
lastDamageTaken = 0.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTelegraphRadius() {
|
||||
return 0; // No telegraph for guard
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setInteger("durationTicks", durationTicks);
|
||||
protected void writeSubTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setFloat("damageReduction", damageReduction);
|
||||
nbt.setBoolean("canCounter", canCounter);
|
||||
nbt.setInteger("counterWindow", counterWindow);
|
||||
nbt.setString("counterType", counterType.name());
|
||||
nbt.setFloat("counterValue", counterValue);
|
||||
nbt.setString("counterSound", counterSound);
|
||||
nbt.setInteger("counterAnimationId", counterAnimationId);
|
||||
nbt.setString("counterAnimationName", resolveAnimationName(counterAnimationId, counterAnimationName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readTypeNBT(NBTTagCompound nbt) {
|
||||
this.durationTicks = nbt.hasKey("durationTicks") ? nbt.getInteger("durationTicks") : 60;
|
||||
protected void readSubTypeNBT(NBTTagCompound nbt) {
|
||||
this.damageReduction = nbt.hasKey("damageReduction") ? nbt.getFloat("damageReduction") : 0.5f;
|
||||
this.canCounter = nbt.getBoolean("canCounter");
|
||||
this.counterWindow = nbt.getInteger("counterWindow");
|
||||
try {
|
||||
this.counterType = CounterType.valueOf(nbt.getString("counterType"));
|
||||
} catch (Exception e) {
|
||||
this.counterType = CounterType.FLAT;
|
||||
}
|
||||
this.counterValue = nbt.getFloat("counterValue");
|
||||
this.counterSound = nbt.getString("counterSound");
|
||||
this.counterAnimationId = nbt.getInteger("counterAnimationId");
|
||||
this.counterAnimationName = nbt.getString("counterAnimationName");
|
||||
}
|
||||
|
||||
// Getters & Setters
|
||||
public int getDurationTicks() {
|
||||
return durationTicks;
|
||||
}
|
||||
|
||||
public void setDurationTicks(int durationTicks) {
|
||||
this.durationTicks = Math.max(1, durationTicks);
|
||||
}
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GUI
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public int getCounterWindow() {
|
||||
return this.counterWindow;
|
||||
protected void getTypeDefinitions(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.floatField("ability.damageReduction", this::getDamageReduction, this::setDamageReduction));
|
||||
|
||||
FieldDef.modifyVisibility(defs, "ability.windUpAnimation", () -> false);
|
||||
FieldDef.modifyVisibility(defs, "ability.windUpSound", () -> false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCounterWindow(int ticks) {
|
||||
this.counterWindow = Math.min(durationTicks, ticks);
|
||||
}
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// GETTERS / SETTERS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@Override
|
||||
public float getDamageReduction() {
|
||||
@@ -294,105 +82,4 @@ public class AbilityGuard extends Ability implements IAbilityGuard {
|
||||
public void setDamageReduction(float damageReduction) {
|
||||
this.damageReduction = damageReduction;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canCounter() {
|
||||
return canCounter;
|
||||
}
|
||||
|
||||
public void setCanCounter(boolean canCounter) {
|
||||
this.canCounter = canCounter;
|
||||
}
|
||||
|
||||
public CounterType getCounterTypeEnum() {
|
||||
return counterType;
|
||||
}
|
||||
|
||||
public void setCounterTypeEnum(CounterType counterType) {
|
||||
this.counterType = counterType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCounterType() {
|
||||
return counterType.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCounterType(int type) {
|
||||
CounterType[] values = CounterType.values();
|
||||
this.counterType = type >= 0 && type < values.length ? values[type] : CounterType.FLAT;
|
||||
}
|
||||
|
||||
public float getCounterValue() {
|
||||
return counterValue;
|
||||
}
|
||||
|
||||
public void setCounterValue(float counterValue) {
|
||||
this.counterValue = counterValue;
|
||||
}
|
||||
|
||||
public String getCounterSound() {
|
||||
return counterSound;
|
||||
}
|
||||
|
||||
public void setCounterSound(String counterSound) {
|
||||
this.counterSound = counterSound;
|
||||
}
|
||||
|
||||
public int getCounterAnimationId() {
|
||||
return counterAnimationId;
|
||||
}
|
||||
|
||||
public void setCounterAnimationId(int counterAnimationId) {
|
||||
this.counterAnimationId = counterAnimationId;
|
||||
}
|
||||
|
||||
public boolean isCounterTriggered() {
|
||||
return counterTriggered;
|
||||
}
|
||||
|
||||
public String getCounterAnimationName() {
|
||||
return counterAnimationName;
|
||||
}
|
||||
|
||||
public void setCounterAnimationName(String name) {
|
||||
this.counterAnimationName = name != null ? name : "";
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void getAbilityDefinitions(List<FieldDef> defs) {
|
||||
defs.addAll(Arrays.asList(
|
||||
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::canCounter, this::setCanCounter).hover("ability.hover.canCounter"),
|
||||
FieldDef.enumField("gui.type", CounterType.class, this::getCounterTypeEnum, this::setCounterTypeEnum)
|
||||
.hover("ability.hover.counterType").visibleWhen(this::canCounter),
|
||||
FieldDef.row(
|
||||
FieldDef.floatField("gui.value", this::getCounterValue, this::setCounterValue)
|
||||
.visibleWhen(this::canCounter),
|
||||
FieldDef.intField("ability.counterWindow", this::getCounterWindow, this::setCounterWindow)
|
||||
.visibleWhen(this::canCounter)
|
||||
),
|
||||
FieldDef.stringField("gui.sound", this::getCounterSound, this::setCounterSound)
|
||||
.visibleWhen(this::canCounter),
|
||||
AbilityFieldDefs.effectsListField("ability.effects", this::getEffects, this::setEffects)
|
||||
|
||||
));
|
||||
|
||||
FieldDef.insertAfter(defs, "ability.activeSound",
|
||||
FieldDef.soundSubGui("ability.counterSound", this::getCounterSound, this::setCounterSound)
|
||||
.tab("Effects").visibleWhen(this::canCounter));
|
||||
|
||||
FieldDef.insertAfter(defs, "ability.dazedAnimation",
|
||||
FieldDef.animSubGui("ability.counterAnimation",
|
||||
this::getCounterAnimationId, this::setCounterAnimationId,
|
||||
this::getCounterAnimationName, this::setCounterAnimationName)
|
||||
.tab("Effects").visibleWhen(this::canCounter));
|
||||
|
||||
FieldDef.modifyVisibility(defs, "ability.windUpAnimation", () -> false);
|
||||
FieldDef.modifyVisibility(defs, "ability.windUpSound", () -> false);
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,11 @@ import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.data.IAbilityAction;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.AbilityToggleEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityGuard;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityDefend;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
import kamkeel.npcs.network.packets.data.telegraph.TelegraphRemovePacket;
|
||||
import kamkeel.npcs.network.packets.data.telegraph.TelegraphSpawnPacket;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
@@ -516,11 +517,6 @@ public class DataAbilities extends AbstractDataAbilities {
|
||||
|
||||
// Track hit for hit count condition
|
||||
recordHit();
|
||||
|
||||
net.minecraft.entity.Entity sourceEntity = source.getEntity();
|
||||
EntityLivingBase attacker = sourceEntity instanceof EntityLivingBase ? (EntityLivingBase) sourceEntity : null;
|
||||
currentAbility.onDamageTaken(npc, attacker, source, amount);
|
||||
|
||||
if (currentAbility.canInterrupt(source)) {
|
||||
interruptCurrentAbility(source, amount);
|
||||
return true;
|
||||
@@ -529,18 +525,6 @@ public class DataAbilities extends AbstractDataAbilities {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the damage reduction factor if a Guard ability is currently active.
|
||||
*
|
||||
* @return The damage reduction factor (0.0 = no reduction, 1.0 = full immunity), or 0 if not guarding
|
||||
*/
|
||||
public float getGuardDamageReduction() {
|
||||
if (currentAbility instanceof AbilityGuard) {
|
||||
return ((AbilityGuard) currentAbility).getDamageReductionFactor();
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a hit for the hit count condition.
|
||||
* Includes periodic cleanup to prevent memory leak.
|
||||
@@ -650,20 +634,6 @@ public class DataAbilities extends AbstractDataAbilities {
|
||||
// SCRIPT EVENTS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Fire an ability hit event. Called by abilities when they hit an entity.
|
||||
* Returns null if the event was cancelled, otherwise returns the (possibly modified) event.
|
||||
*
|
||||
* @param ability The ability doing the hit
|
||||
* @param target The original target of the ability
|
||||
* @param hitEntity The entity being hit
|
||||
* @param event The mutable hit event to fire
|
||||
* @return true if the event was cancelled
|
||||
*/
|
||||
public boolean fireHitEvent(Ability ability, AbilityEvent.HitEvent event) {
|
||||
return EventHooks.onAbilityHit(ability, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the NPC this DataAbilities belongs to.
|
||||
*/
|
||||
|
||||
@@ -596,6 +596,7 @@ public class ScriptPlayerEventHandler {
|
||||
if (event.entityLiving.worldObj instanceof WorldServer) {
|
||||
boolean cancel = event.isCanceled();
|
||||
Entity source = NoppesUtilServer.GetDamageSource(event.source);
|
||||
|
||||
if (event.entityLiving instanceof EntityPlayerMP) {
|
||||
PlayerDataScript handler = ScriptController.Instance.getPlayerScripts((EntityPlayer) event.entityLiving);
|
||||
noppes.npcs.scripted.event.player.PlayerEvent.AttackedEvent pevent = new noppes.npcs.scripted.event.player.PlayerEvent.AttackedEvent((IPlayer) NpcAPI.Instance().getIEntity((EntityPlayer) event.entityLiving), source, event.ammount, event.source);
|
||||
@@ -645,20 +646,21 @@ public class ScriptPlayerEventHandler {
|
||||
}
|
||||
|
||||
if (event.entityLiving instanceof EntityPlayerMP) {
|
||||
// Handle ability damage interactions (Guard counter, damage reduction)
|
||||
// Handle ability damage interactions
|
||||
PlayerData pData = PlayerData.get((EntityPlayer) event.entityLiving);
|
||||
if (pData != null && pData.abilityData != null && pData.abilityData.isExecutingAbility()) {
|
||||
if (pData.abilityData.isCurrentAbilityInvulnerable()) {
|
||||
event.ammount = 0;
|
||||
cancel = true;
|
||||
} else {
|
||||
event.ammount = pData.abilityData.onDamage(event.source, event.ammount);
|
||||
float modified = pData.abilityData.onDamage(event.source, event.ammount);
|
||||
event.ammount = modified;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerDataScript handler = ScriptController.Instance.getPlayerScripts((EntityPlayer) event.entityLiving);
|
||||
noppes.npcs.scripted.event.player.PlayerEvent.DamagedEvent pevent = new noppes.npcs.scripted.event.player.PlayerEvent.DamagedEvent((IPlayer) NpcAPI.Instance().getIEntity((EntityPlayer) event.entityLiving), source, event.ammount, event.source);
|
||||
cancel = EventHooks.onPlayerDamaged(handler, pevent);
|
||||
cancel = cancel || EventHooks.onPlayerDamaged(handler, pevent);
|
||||
event.ammount = pevent.damage;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,16 +79,6 @@ public class CombatHandler {
|
||||
aggressors.put(el, f + damageAmount);
|
||||
}
|
||||
|
||||
// Handle guard counter before interruption
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.data.IAbilityAction;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.AbilityToggleEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityGuard;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
import kamkeel.npcs.network.packets.data.ability.AbilityCooldownSyncPacket;
|
||||
import kamkeel.npcs.network.packets.data.ability.PlayerAbilityStatePacket;
|
||||
@@ -787,32 +786,6 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
return 0;
|
||||
}
|
||||
|
||||
net.minecraft.entity.Entity sourceEntity = source.getEntity();
|
||||
EntityLivingBase attacker = sourceEntity instanceof EntityLivingBase ? (EntityLivingBase) sourceEntity : null;
|
||||
|
||||
// Track counter state before calling onDamageTaken
|
||||
boolean wasCounterTriggered = false;
|
||||
if (currentAbility instanceof AbilityGuard) {
|
||||
wasCounterTriggered = ((AbilityGuard) currentAbility).isCounterTriggered();
|
||||
}
|
||||
|
||||
currentAbility.onDamageTaken(playerData.player, attacker, source, amount);
|
||||
|
||||
// Guard: handle damage reduction and counter absorption
|
||||
if (currentAbility instanceof AbilityGuard) {
|
||||
AbilityGuard guard = (AbilityGuard) currentAbility;
|
||||
if (guard.isGuarding()) {
|
||||
// Counter just triggered — absorb full damage
|
||||
if (!wasCounterTriggered && guard.isCounterTriggered()) {
|
||||
return 0;
|
||||
}
|
||||
// Normal guard — apply flat damage reduction
|
||||
float reduction = guard.getDamageReductionFactor();
|
||||
return Math.max(0, amount - reduction);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for ability interruption
|
||||
if (currentAbility != null && currentAbility.canInterrupt(source)) {
|
||||
interruptCurrentAbility(source, amount);
|
||||
}
|
||||
@@ -1027,9 +1000,6 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
*/
|
||||
private void validateUnlockedAbilities() {
|
||||
if (AbilityController.Instance == null) return;
|
||||
// Only validate on the server - the client trusts server-sent data.
|
||||
// Custom ability definitions may not have been synced to the client yet,
|
||||
// which would cause valid abilities to be incorrectly pruned.
|
||||
if (playerData == null || !(playerData.player instanceof EntityPlayerMP)) return;
|
||||
|
||||
Iterator<String> it = unlockedAbilities.iterator();
|
||||
|
||||
@@ -1713,6 +1713,8 @@ ability.cnpc.teleport=Teleport
|
||||
ability.cnpc.vortex=Vortex
|
||||
ability.cnpc.shockwave=Shockwave
|
||||
ability.cnpc.guard=Guard
|
||||
ability.cnpc.counter=Counter
|
||||
ability.cnpc.dodge=Dodge
|
||||
ability.cnpc.effect=Effect
|
||||
ability.cnpc.hazard=Hazard
|
||||
ability.cnpc.trap=Trap
|
||||
@@ -1763,6 +1765,9 @@ ability.windUpAnimation=Wind Up Animation
|
||||
ability.activeAnimation=Active Animation
|
||||
ability.dazedAnimation=Dazed Animation
|
||||
ability.counterAnimation=Counter Animation
|
||||
ability.dodgeAnimation1=Dodge Animation 1
|
||||
ability.dodgeAnimation2=Dodge Animation 2
|
||||
ability.dodgeAnimation3=Dodge Animation 3
|
||||
ability.showTelegraph=Show Telegraph
|
||||
ability.windUpColor=Wind Up Color
|
||||
ability.activeColor=Active Color
|
||||
@@ -1785,6 +1790,7 @@ ability.section.explosive=Explosive
|
||||
ability.section.colors=Colors
|
||||
ability.section.effects=Effects
|
||||
ability.section.burst=Burst
|
||||
ability.section.dodgeAnimations=Dodge Animations
|
||||
ability.section.visual=Visual
|
||||
ability.section.projectile1=Projectile 1
|
||||
ability.section.projectile2=Projectile 2
|
||||
@@ -1915,9 +1921,9 @@ ability.maxTargets=Max Targets
|
||||
# Shockwave
|
||||
ability.pushStrength=Push Strength
|
||||
|
||||
# Guard
|
||||
# Defend (Guard / Counter / Dodge)
|
||||
ability.maxHitAmount=Max Hit Amount
|
||||
ability.damageReduction=Damage Reduction
|
||||
ability.counterWindow=Counter Ticks
|
||||
|
||||
# Effect
|
||||
ability.instantHeal=Instant
|
||||
@@ -2061,6 +2067,8 @@ ability.hover.multiHitDelay=Ticks between repeat hits on the same entity when Hi
|
||||
ability.hover.maxHits=Maximum total hits before projectile ends in Pierce/Multi modes. Lower values reduce repeated hit spam and improve performance.
|
||||
ability.hover.explosive=Creates explosion on impact
|
||||
ability.hover.explosionRad=Size of explosion radius
|
||||
ability.hover.maxHitAmount=Maximum hits absorbed before defend ability ends (0 = unlimited)
|
||||
ability.hover.counterType=Flat: fixed damage counter-attack. Percent: counter-attack deals a percentage of the absorbed damage.
|
||||
ability.hover.piercing=Hits multiple targets instead of stopping on first hit
|
||||
ability.hover.lightning=Add electrical effect to visuals
|
||||
ability.hover.outerGlow=Enable outer glow layer around projectile
|
||||
|
||||
Reference in new issue
Block a user