Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/api
Submodule api updated from 5cd088 to 0eae6c
74 changes: 39 additions & 35 deletions src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import kamkeel.npcs.controllers.data.ability.telegraph.TelegraphInstance;
import kamkeel.npcs.controllers.data.ability.telegraph.TelegraphType;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
Expand All @@ -15,6 +16,7 @@
import noppes.npcs.NpcDamageSource;
import noppes.npcs.api.INbt;
import noppes.npcs.api.ability.IAbility;
import noppes.npcs.api.ability.IAbilityHolder;
import noppes.npcs.client.gui.util.IAbilityConfigCallback;
import noppes.npcs.client.gui.advanced.SubGuiAbilityConfig;
import noppes.npcs.entity.EntityNPCInterface;
Expand Down Expand Up @@ -90,10 +92,10 @@ public abstract class Ability implements IAbility {
// ═══════════════════════════════════════════════════════════════════

/** Called first tick of ACTIVE phase */
public abstract void onExecute(EntityNPCInterface npc, EntityLivingBase target, World world);
public abstract void onExecute(IAbilityHolder entity, EntityLivingBase target, World world);

/** Called every tick of ACTIVE phase */
public abstract void onActiveTick(EntityNPCInterface npc, EntityLivingBase target, World world, int tick);
public abstract void onActiveTick(IAbilityHolder entity, EntityLivingBase target, World world, int tick);

/** Write type-specific config to NBT */
public abstract void writeTypeNBT(NBTTagCompound nbt);
Expand All @@ -105,24 +107,24 @@ public abstract class Ability implements IAbility {
// OPTIONAL OVERRIDES
// ═══════════════════════════════════════════════════════════════════

public void onWindUpTick(EntityNPCInterface npc, EntityLivingBase target, World world, int tick) {}
public void onInterrupt(EntityNPCInterface npc, DamageSource source, float damage) {}
public void onComplete(EntityNPCInterface npc, EntityLivingBase target) {}
public void onWindUpTick(IAbilityHolder entity, EntityLivingBase target, World world, int tick) {}
public void onInterrupt(IAbilityHolder entity, DamageSource source, float damage) {}
public void onComplete(IAbilityHolder entity, EntityLivingBase target) {}

/**
* Apply damage to an entity with ability hit event support.
* Fires the abilityHit script event, allowing scripts to modify or cancel the damage.
*
* @param npc The NPC executing the ability
* @param holder The entity executing the ability
* @param hitEntity The entity being hit
* @param damage The damage amount
* @param knockback The horizontal knockback
* @param knockbackUp The vertical knockback
* @return true if damage was applied (not cancelled), false if cancelled
*/
protected boolean applyAbilityDamage(EntityNPCInterface npc, EntityLivingBase hitEntity,
protected boolean applyAbilityDamage(IAbilityHolder holder, EntityLivingBase hitEntity,
float damage, float knockback, float knockbackUp) {
DataAbilities dataAbilities = npc.abilities;
DataAbilities dataAbilities = (DataAbilities) holder.getAbilityData();
AbilityEvent.HitEvent event = dataAbilities.fireHitEvent(
this, currentTarget, hitEntity, damage, knockback, knockbackUp);

Expand All @@ -137,13 +139,14 @@ protected boolean applyAbilityDamage(EntityNPCInterface npc, EntityLivingBase hi

// Apply damage
if (finalDamage > 0) {
hitEntity.attackEntityFrom(new NpcDamageSource("mob", npc), finalDamage);
hitEntity.attackEntityFrom(new NpcDamageSource("mob", dataAbilities.getEntity()), finalDamage);
}

// Apply knockback if any
if (finalKnockback > 0 || finalKnockbackUp > 0) {
double dx = hitEntity.posX - npc.posX;
double dz = hitEntity.posZ - npc.posZ;

double dx = hitEntity.posX - dataAbilities.getEntity().posX;
double dz = hitEntity.posZ - dataAbilities.getEntity().posZ;
double len = Math.sqrt(dx * dx + dz * dz);
if (len > 0 && finalKnockback > 0) {
dx /= len;
Expand All @@ -162,7 +165,7 @@ protected boolean applyAbilityDamage(EntityNPCInterface npc, EntityLivingBase hi
* Apply damage to an entity with ability hit event support and custom knockback direction.
* Fires the abilityHit script event, allowing scripts to modify or cancel the damage.
*
* @param npc The NPC executing the ability
* @param holder The entity executing the ability
* @param hitEntity The entity being hit
* @param damage The damage amount
* @param knockback The horizontal knockback
Expand All @@ -171,10 +174,10 @@ protected boolean applyAbilityDamage(EntityNPCInterface npc, EntityLivingBase hi
* @param knockbackDirZ The Z component of knockback direction (normalized)
* @return true if damage was applied (not cancelled), false if cancelled
*/
protected boolean applyAbilityDamageWithDirection(EntityNPCInterface npc, EntityLivingBase hitEntity,
protected boolean applyAbilityDamageWithDirection(IAbilityHolder holder, EntityLivingBase hitEntity,
float damage, float knockback, float knockbackUp,
double knockbackDirX, double knockbackDirZ) {
DataAbilities dataAbilities = npc.abilities;
DataAbilities dataAbilities = (DataAbilities) holder.getAbilityData();
AbilityEvent.HitEvent event = dataAbilities.fireHitEvent(
this, currentTarget, hitEntity, damage, knockback, knockbackUp);

Expand All @@ -189,7 +192,7 @@ protected boolean applyAbilityDamageWithDirection(EntityNPCInterface npc, Entity

// Apply damage
if (finalDamage > 0) {
hitEntity.attackEntityFrom(DamageSource.causeMobDamage(npc), finalDamage);
hitEntity.attackEntityFrom(DamageSource.causeMobDamage(dataAbilities.getEntity()), finalDamage);
}

// Apply knockback in specified direction
Expand Down Expand Up @@ -264,37 +267,38 @@ public SubGuiAbilityConfig createConfigGui(IAbilityConfigCallback callback) {
* Create a telegraph instance for this ability.
* Override for custom telegraph shapes.
*
* @param npc The caster
* @param holder The caster
* @param target The target (for position calculation)
* @return The telegraph instance, or null if no telegraph
*/
public TelegraphInstance createTelegraph(EntityNPCInterface npc, EntityLivingBase target) {
public TelegraphInstance createTelegraph(IAbilityHolder holder, EntityLivingBase target) {
if (!showTelegraph || telegraphType == TelegraphType.NONE) {
return null;
}

Entity entity = (Entity) holder;
Telegraph telegraph;
double x, y, z;
float yaw = npc.rotationYaw;
float yaw = entity.rotationYaw;

// Determine position based on targeting mode
boolean positionAtNpc = targetingMode == TargetingMode.AOE_SELF ||
boolean positionAtEntity = targetingMode == TargetingMode.AOE_SELF ||
targetingMode == TargetingMode.SELF ||
telegraphType == TelegraphType.LINE ||
telegraphType == TelegraphType.CONE;

if (positionAtNpc) {
x = npc.posX;
y = findGroundLevel(npc.worldObj, npc.posX, npc.posY, npc.posZ);
z = npc.posZ;
if (positionAtEntity) {
x = entity.posX;
y = findGroundLevel(entity.worldObj, entity.posX, entity.posY, entity.posZ);
z = entity.posZ;
} else if (target != null) {
x = target.posX;
y = findGroundLevel(npc.worldObj, target.posX, target.posY, target.posZ);
y = findGroundLevel(entity.worldObj, target.posX, target.posY, target.posZ);
z = target.posZ;
} else {
x = npc.posX;
y = findGroundLevel(npc.worldObj, npc.posX, npc.posY, npc.posZ);
z = npc.posZ;
x = entity.posX;
y = findGroundLevel(entity.worldObj, entity.posX, entity.posY, entity.posZ);
z = entity.posZ;
}

// Create telegraph based on type
Expand Down Expand Up @@ -325,12 +329,12 @@ public TelegraphInstance createTelegraph(EntityNPCInterface npc, EntityLivingBas
telegraph.setHeightOffset(telegraphHeightOffset);

TelegraphInstance instance = new TelegraphInstance(telegraph, x, y, z, yaw);
instance.setCasterEntityId(npc.getEntityId());
instance.setCasterEntityId(entity.getEntityId());

// Set entity to follow based on targeting mode
if (positionAtNpc) {
if (positionAtEntity) {
// AOE_SELF abilities: telegraph follows NPC during windup
instance.setEntityIdToFollow(npc.getEntityId());
instance.setEntityIdToFollow(entity.getEntityId());
} else if (target != null) {
// AOE_TARGET abilities: telegraph follows target during windup
instance.setEntityIdToFollow(target.getEntityId());
Expand Down Expand Up @@ -496,23 +500,23 @@ public void reset() {
// CONDITION CHECKING
// ═══════════════════════════════════════════════════════════════════

public boolean checkConditions(EntityNPCInterface npc, EntityLivingBase target) {
public boolean checkConditions(IAbilityHolder holder, EntityLivingBase target) {
for (Condition c : conditions) {
if (!c.check(npc, target)) return false;
if (!c.check(holder, target)) return false;
}
return true;
}

/** Full eligibility check */
public boolean canUse(EntityNPCInterface npc, EntityLivingBase target) {
public boolean canUse(IAbilityHolder holder, EntityLivingBase target) {
if (!enabled) return false;
if (isOnCooldown()) return false;
if (isExecuting()) return false;

float distance = npc.getDistanceToEntity(target);
float distance = ((Entity) holder).getDistanceToEntity(target);
if (distance < minRange || distance > maxRange) return false;

return checkConditions(npc, target);
return checkConditions(holder, target);
}

// ═══════════════════════════════════════════════════════════════════
Expand Down
24 changes: 13 additions & 11 deletions src/main/java/kamkeel/npcs/controllers/data/ability/Condition.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.DataAbilities;
import noppes.npcs.api.ability.IAbilityHolder;
import noppes.npcs.entity.EntityNPCInterface;

/**
Expand All @@ -13,11 +15,11 @@ public interface Condition {
/**
* Check if this condition is met.
*
* @param npc The NPC that would use the ability
* @param holder The entity that would use the ability
* @param target The current target (may be null for self-targeting abilities)
* @return true if the condition is satisfied
*/
boolean check(EntityNPCInterface npc, EntityLivingBase target);
boolean check(IAbilityHolder holder, EntityLivingBase target);

/**
* Get the condition type ID for serialization.
Expand Down Expand Up @@ -87,8 +89,8 @@ public ConditionHPAbove(float threshold) {
}

@Override
public boolean check(EntityNPCInterface npc, EntityLivingBase target) {
return npc.getHealth() / npc.getMaxHealth() > threshold;
public boolean check(IAbilityHolder entity, EntityLivingBase target) {
return ((EntityLivingBase) entity).getHealth() / ((EntityLivingBase) entity).getMaxHealth() > threshold;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fuck, why double cast ?

final EntityLivingBase living = (EntityLivingBase) entity;
return (living.getHealth() / living.getMaxHealth()) > threshold;

}

@Override
Expand Down Expand Up @@ -121,8 +123,8 @@ public ConditionHPBelow(float threshold) {
}

@Override
public boolean check(EntityNPCInterface npc, EntityLivingBase target) {
return npc.getHealth() / npc.getMaxHealth() < threshold;
public boolean check(IAbilityHolder entity, EntityLivingBase target) {
return ((EntityLivingBase) entity).getHealth() / ((EntityLivingBase) entity).getMaxHealth() < threshold;
Copy link
Contributor

@PeTcHeNkA PeTcHeNkA Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wtfffffffffffff why method logic is doubled ????
also:

final EntityLivingBase living = (EntityLivingBase) entity;
return (living.getHealth() / living.getMaxHealth()) < threshold;

}

@Override
Expand Down Expand Up @@ -155,7 +157,7 @@ public ConditionTargetHPAbove(float threshold) {
}

@Override
public boolean check(EntityNPCInterface npc, EntityLivingBase target) {
public boolean check(IAbilityHolder entity, EntityLivingBase target) {
if (target == null) return false;
return target.getHealth() / target.getMaxHealth() > threshold;
}
Expand Down Expand Up @@ -190,7 +192,7 @@ public ConditionTargetHPBelow(float threshold) {
}

@Override
public boolean check(EntityNPCInterface npc, EntityLivingBase target) {
public boolean check(IAbilityHolder entity, EntityLivingBase target) {
if (target == null) return false;
return target.getHealth() / target.getMaxHealth() < threshold;
}
Expand Down Expand Up @@ -228,10 +230,10 @@ public ConditionHitCount(int requiredHits, int withinTicks) {
}

@Override
public boolean check(EntityNPCInterface npc, EntityLivingBase target) {
public boolean check(IAbilityHolder entity, EntityLivingBase target) {
// Check NPC's recent hit count from DataAbilities
if (npc.abilities != null) {
return npc.abilities.getRecentHitCount(withinTicks) >= requiredHits;
if (entity.getAbilityData() != null) {
return entity.getAbilityData().getRecentHitCount(withinTicks) >= requiredHits;
Comment on lines 234 to +236
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wtf and getter is doubled!

}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import cpw.mods.fml.relauncher.SideOnly;

import kamkeel.npcs.controllers.data.ability.Ability;
import noppes.npcs.api.ability.IAbilityHolder;
import noppes.npcs.client.gui.util.IAbilityConfigCallback;
import noppes.npcs.client.gui.advanced.SubGuiAbilityConfig;
import noppes.npcs.client.gui.advanced.ability.SubGuiAbilityBeam;
Expand Down Expand Up @@ -79,7 +80,7 @@ public TargetingMode[] getAllowedTargetingModes() {
}

@Override
public void onExecute(EntityNPCInterface npc, EntityLivingBase target, World world) {
public void onExecute(IAbilityHolder holder, EntityLivingBase target, World world) {
// Initialize beam
currentSweepAngle = -sweepAngle / 2;
sweepingRight = true;
Expand All @@ -88,9 +89,10 @@ public void onExecute(EntityNPCInterface npc, EntityLivingBase target, World wor
}

@Override
public void onActiveTick(EntityNPCInterface npc, EntityLivingBase target, World world, int tick) {
public void onActiveTick(IAbilityHolder holder, EntityLivingBase target, World world, int tick) {
if (world.isRemote) return;

EntityLivingBase entity = (EntityLivingBase) holder;
hitThisTick.clear();
ticksSinceDamage++;

Expand All @@ -117,22 +119,22 @@ public void onActiveTick(EntityNPCInterface npc, EntityLivingBase target, World
}

// Calculate beam direction
float baseYaw = npc.rotationYaw;
float baseYaw = entity.rotationYaw;

// If lock on target, point at target
if (lockOnTarget && target != null) {
double dx = target.posX - npc.posX;
double dz = target.posZ - npc.posZ;
double dx = target.posX - entity.posX;
double dz = target.posZ - entity.posZ;
baseYaw = (float) Math.toDegrees(Math.atan2(-dx, dz));
}

float beamYaw = baseYaw + currentSweepAngle;
float yawRad = (float) Math.toRadians(beamYaw);

// Calculate beam start position
double startX = npc.posX;
double startY = npc.posY + npc.getEyeHeight() * 0.8;
double startZ = npc.posZ;
double startX = entity.posX;
double startY = entity.posY + entity.getEyeHeight() * 0.8;
double startZ = entity.posZ;

double dirX = -Math.sin(yawRad);
double dirZ = Math.cos(yawRad);
Expand All @@ -158,18 +160,18 @@ public void onActiveTick(EntityNPCInterface npc, EntityLivingBase target, World
@SuppressWarnings("unchecked")
List<Entity> entities = world.getEntitiesWithinAABB(EntityLivingBase.class, checkBox);

for (Entity entity : entities) {
if (!(entity instanceof EntityLivingBase)) continue;
if (entity == npc) continue;
if (hitThisTick.contains(entity.getEntityId())) continue;
for (Entity e : entities) {
if (!(e instanceof EntityLivingBase)) continue;
if (e == entity) continue;
Comment on lines +164 to +165
Copy link
Contributor

@PeTcHeNkA PeTcHeNkA Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (e == enity || !(e instanceof EntityLivingBase)) continue

if (hitThisTick.contains(e.getEntityId())) continue;

EntityLivingBase livingEntity = (EntityLivingBase) entity;
EntityLivingBase livingEntity = (EntityLivingBase) e;

// Check if actually in beam path
if (isInBeamPath(livingEntity, startX, startY, startZ, dirX, dirZ)) {
hitThisTick.add(entity.getEntityId());
hitThisTick.add(e.getEntityId());
// Apply damage with scripted event support (no knockback for beam)
applyAbilityDamage(npc, livingEntity, damage, 0, 0);
applyAbilityDamage(holder, livingEntity, damage, 0, 0);

if (!piercing) {
return; // Stop at first hit
Expand Down
Loading