mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 21:54:08 +00:00
More Abilities
This commit is contained in:
1 parent
de043f4a25
commit
651f2d0d84
24 files changed
+560
-220
No files matched your search
@@ -61,10 +61,10 @@ public class RenderAbilityBeam extends RenderAbilityProjectile {
|
||||
GL11.glTranslated(renderOriginX, renderOriginY, renderOriginZ);
|
||||
|
||||
// Render tail orb at origin (0,0,0) - static, no rotation
|
||||
renderTailOrb(headSize * 0.8f, innerColor, outerColor);
|
||||
renderTailOrb(headSize * 0.8f, innerColor, outerColor, beam.isOuterColorEnabled());
|
||||
|
||||
// Render trail segments with smooth connections
|
||||
renderSmoothTrail(trail, beamWidth, innerColor, outerColor);
|
||||
renderSmoothTrail(trail, beamWidth, innerColor, outerColor, beam.isOuterColorEnabled(), beam.getOuterColorWidth());
|
||||
|
||||
// Render head at interpolated offset position
|
||||
renderHead(beam, headOffsetX, headOffsetY, headOffsetZ, headSize, innerColor, outerColor, partialTicks);
|
||||
@@ -78,17 +78,19 @@ public class RenderAbilityBeam extends RenderAbilityProjectile {
|
||||
* Render the tail orb at the origin (where the beam emanates from).
|
||||
* This orb does not rotate - it's a stable anchor point.
|
||||
*/
|
||||
private void renderTailOrb(float size, int innerColor, int outerColor) {
|
||||
private void renderTailOrb(float size, int innerColor, int outerColor, boolean outerColorEnabled) {
|
||||
GL11.glPushMatrix();
|
||||
// Already at origin (0,0,0)
|
||||
|
||||
// Render outer glow
|
||||
GL11.glDepthMask(false);
|
||||
GL11.glPushMatrix();
|
||||
GL11.glScalef(size, size, size);
|
||||
renderCube(outerColor, 0.4f, 0.5f);
|
||||
GL11.glPopMatrix();
|
||||
GL11.glDepthMask(true);
|
||||
// Render outer glow only if enabled
|
||||
if (outerColorEnabled) {
|
||||
GL11.glDepthMask(false);
|
||||
GL11.glPushMatrix();
|
||||
GL11.glScalef(size, size, size);
|
||||
renderCube(outerColor, 0.4f, 0.5f);
|
||||
GL11.glPopMatrix();
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
// Render inner core (smaller, solid)
|
||||
GL11.glPushMatrix();
|
||||
@@ -103,7 +105,8 @@ public class RenderAbilityBeam extends RenderAbilityProjectile {
|
||||
* Render the beam trail with smooth connections between segments.
|
||||
* Uses averaged perpendicular vectors at connection points to prevent gaps.
|
||||
*/
|
||||
private void renderSmoothTrail(List<Vec3> trail, float width, int innerColor, int outerColor) {
|
||||
private void renderSmoothTrail(List<Vec3> trail, float width, int innerColor, int outerColor,
|
||||
boolean outerColorEnabled, float outerColorWidth) {
|
||||
if (trail.size() < 2) return;
|
||||
|
||||
int trailSize = trail.size();
|
||||
@@ -112,10 +115,12 @@ public class RenderAbilityBeam extends RenderAbilityProjectile {
|
||||
// At connection points, average the perpendiculars for smooth transitions
|
||||
List<double[]> perpFrames = computePerpendiculars(trail);
|
||||
|
||||
// Render outer glow trail (wider, translucent)
|
||||
GL11.glDepthMask(false);
|
||||
renderTrailTube(trail, perpFrames, width * 1.8f, outerColor, 0.3f, true);
|
||||
GL11.glDepthMask(true);
|
||||
// Render outer glow trail (wider, translucent) - only if enabled
|
||||
if (outerColorEnabled) {
|
||||
GL11.glDepthMask(false);
|
||||
renderTrailTube(trail, perpFrames, width * outerColorWidth, outerColor, 0.3f, true);
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
// Render inner core trail
|
||||
renderTrailTube(trail, perpFrames, width * 0.6f, innerColor, 0.9f, false);
|
||||
@@ -331,10 +336,12 @@ public class RenderAbilityBeam extends RenderAbilityProjectile {
|
||||
GL11.glPushMatrix();
|
||||
GL11.glScalef(scale, scale, scale);
|
||||
|
||||
// Render outer glow
|
||||
GL11.glDepthMask(false);
|
||||
renderCube(outerColor, 0.5f, 0.5f);
|
||||
GL11.glDepthMask(true);
|
||||
// Render outer glow only if enabled
|
||||
if (beam.isOuterColorEnabled()) {
|
||||
GL11.glDepthMask(false);
|
||||
renderCube(outerColor, 0.5f, 0.5f);
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
// Render inner core
|
||||
float innerScale = 0.6f;
|
||||
|
||||
@@ -39,10 +39,13 @@ public class RenderAbilityDisc extends RenderAbilityProjectile {
|
||||
// Apply ONLY Y-axis rotation for flat spin
|
||||
GL11.glRotatef(disc.getInterpolatedRotationY(partialTicks), 0.0f, 1.0f, 0.0f);
|
||||
|
||||
// Render outer square (translucent)
|
||||
GL11.glDepthMask(false);
|
||||
renderFlatSquare(disc.getOuterColor(), 0.5f, radius, thickness);
|
||||
GL11.glDepthMask(true);
|
||||
// Render outer square (translucent) - only if enabled
|
||||
if (disc.isOuterColorEnabled()) {
|
||||
float outerScale = disc.getOuterColorWidth() / 1.8f; // Normalize to default width
|
||||
GL11.glDepthMask(false);
|
||||
renderFlatSquare(disc.getOuterColor(), 0.5f, radius * outerScale, thickness);
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
// Render inner square (solid, smaller)
|
||||
float innerScale = 0.6f;
|
||||
|
||||
@@ -84,19 +84,22 @@ public class RenderAbilityLaser extends RenderAbilityProjectile {
|
||||
double vertY = dz * horzX - dx * horzZ;
|
||||
double vertZ = dx * horzY - dy * horzX;
|
||||
|
||||
// Render outer rectangle (larger, outer color, translucent)
|
||||
GL11.glDepthMask(false);
|
||||
renderBeamRectangle(renderStartX, renderStartY, renderStartZ,
|
||||
renderEndX, renderEndY, renderEndZ,
|
||||
horzX, horzY, horzZ, vertX, vertY, vertZ,
|
||||
width * 1.8f, laser.getOuterColor(), alpha * 0.4f);
|
||||
// Render outer rectangle (larger, outer color, translucent) - only if enabled
|
||||
if (laser.isOuterColorEnabled()) {
|
||||
float outerScale = laser.getOuterColorWidth();
|
||||
GL11.glDepthMask(false);
|
||||
renderBeamRectangle(renderStartX, renderStartY, renderStartZ,
|
||||
renderEndX, renderEndY, renderEndZ,
|
||||
horzX, horzY, horzZ, vertX, vertY, vertZ,
|
||||
width * outerScale, laser.getOuterColor(), alpha * 0.4f);
|
||||
|
||||
// Render middle layer
|
||||
renderBeamRectangle(renderStartX, renderStartY, renderStartZ,
|
||||
renderEndX, renderEndY, renderEndZ,
|
||||
horzX, horzY, horzZ, vertX, vertY, vertZ,
|
||||
width * 1.3f, laser.getOuterColor(), alpha * 0.7f);
|
||||
GL11.glDepthMask(true);
|
||||
// Render middle layer
|
||||
renderBeamRectangle(renderStartX, renderStartY, renderStartZ,
|
||||
renderEndX, renderEndY, renderEndZ,
|
||||
horzX, horzY, horzZ, vertX, vertY, vertZ,
|
||||
width * 1.3f, laser.getOuterColor(), alpha * 0.7f);
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
// Render inner core (smaller, inner color, solid)
|
||||
renderBeamRectangle(renderStartX, renderStartY, renderStartZ,
|
||||
|
||||
@@ -69,11 +69,17 @@ public class RenderAbilityOrb extends Render {
|
||||
GL11.glRotatef(orb.getInterpolatedRotationY(partialTicks), 0.0f, 1.0f, 0.0f);
|
||||
GL11.glRotatef(orb.getInterpolatedRotationZ(partialTicks), 0.0f, 0.0f, 1.0f);
|
||||
|
||||
// Render outer sphere (translucent)
|
||||
GL11.glDepthMask(false);
|
||||
this.bindTexture(WHITE_TEXTURE);
|
||||
renderSphere(orb.getOuterColor(), 0.6f);
|
||||
GL11.glDepthMask(true);
|
||||
// Render outer sphere (translucent) - only if enabled
|
||||
if (orb.isOuterColorEnabled()) {
|
||||
GL11.glDepthMask(false);
|
||||
this.bindTexture(WHITE_TEXTURE);
|
||||
float outerScale = orb.getOuterColorWidth() / 1.8f; // Normalize to default width
|
||||
GL11.glPushMatrix();
|
||||
GL11.glScalef(outerScale, outerScale, outerScale);
|
||||
renderSphere(orb.getOuterColor(), 0.6f);
|
||||
GL11.glPopMatrix();
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
// Render inner sphere (more opaque)
|
||||
float innerScale = 0.75f;
|
||||
|
||||
@@ -26,6 +26,8 @@ public class RenderAbilitySweeper extends Render {
|
||||
float beamWidth = sweeper.getBeamWidth();
|
||||
int innerColor = sweeper.getInnerColor();
|
||||
int outerColor = sweeper.getOuterColor();
|
||||
boolean outerColorEnabled = sweeper.isOuterColorEnabled();
|
||||
float outerColorWidth = sweeper.getOuterColorWidth();
|
||||
float angle = sweeper.getInterpolatedAngle(partialTicks);
|
||||
|
||||
GL11.glPushMatrix();
|
||||
@@ -35,7 +37,7 @@ public class RenderAbilitySweeper extends Render {
|
||||
GL11.glRotatef(-angle, 0, 1, 0); // Negative for correct direction
|
||||
|
||||
// Render the beam extending in the +Z direction (like beam trail segments)
|
||||
renderBeam(beamLength, beamWidth, innerColor, outerColor);
|
||||
renderBeam(beamLength, beamWidth, innerColor, outerColor, outerColorEnabled, outerColorWidth);
|
||||
|
||||
GL11.glPopMatrix();
|
||||
|
||||
@@ -65,7 +67,8 @@ public class RenderAbilitySweeper extends Render {
|
||||
* Render beam using the same style as the Beam entity trail.
|
||||
* Three layers: outer glow, middle, inner core.
|
||||
*/
|
||||
private void renderBeam(float length, float width, int innerColor, int outerColor) {
|
||||
private void renderBeam(float length, float width, int innerColor, int outerColor,
|
||||
boolean outerColorEnabled, float outerColorWidth) {
|
||||
// Extract colors
|
||||
float outerR = ((outerColor >> 16) & 0xFF) / 255.0f;
|
||||
float outerG = ((outerColor >> 8) & 0xFF) / 255.0f;
|
||||
@@ -78,13 +81,15 @@ public class RenderAbilitySweeper extends Render {
|
||||
// Render from origin (0,0,0) to (0,0,length) in local coords
|
||||
// After rotation this extends in the correct world direction
|
||||
|
||||
// Outer glow (wider, translucent) - like beam trail
|
||||
GL11.glDepthMask(false);
|
||||
renderBeamSegment(0, 0, 0, 0, 0, length, width * 1.8f, outerR, outerG, outerB, 0.3f);
|
||||
GL11.glDepthMask(true);
|
||||
// Outer glow (wider, translucent) - like beam trail - only if enabled
|
||||
if (outerColorEnabled) {
|
||||
GL11.glDepthMask(false);
|
||||
renderBeamSegment(0, 0, 0, 0, 0, length, width * outerColorWidth, outerR, outerG, outerB, 0.3f);
|
||||
GL11.glDepthMask(true);
|
||||
|
||||
// Middle layer
|
||||
renderBeamSegment(0, 0, 0, 0, 0, length, width * 1.3f, outerR, outerG, outerB, 0.6f);
|
||||
// Middle layer
|
||||
renderBeamSegment(0, 0, 0, 0, 0, length, width * 1.3f, outerR, outerG, outerB, 0.6f);
|
||||
}
|
||||
|
||||
// Inner core (solid) - like beam trail inner
|
||||
renderBeamSegment(0, 0, 0, 0, 0, length, width * 0.6f, innerR, innerG, innerB, 1.0f);
|
||||
|
||||
@@ -53,6 +53,10 @@ public abstract class Ability implements IAbility {
|
||||
// Interruption
|
||||
protected boolean interruptible = true;
|
||||
|
||||
// Wait for completion - if true, ability controls when active phase ends (ignores activeTicks)
|
||||
// Used for abilities like Slam that need to wait for landing, or Beam that runs until entity dies
|
||||
protected boolean waitForCompletion = false;
|
||||
|
||||
// Feedback
|
||||
protected boolean lockMovement = true;
|
||||
protected int windUpColor = 0x80FF4400; // Telegraph color during wind up
|
||||
@@ -133,7 +137,9 @@ public abstract class Ability implements IAbility {
|
||||
*/
|
||||
protected boolean applyAbilityDamage(EntityNPCInterface npc, EntityLivingBase hitEntity,
|
||||
float damage, float knockback) {
|
||||
return applyAbilityDamage(npc, hitEntity, damage, knockback, 0.0f);
|
||||
double dx = hitEntity.posX - npc.posX;
|
||||
double dz = hitEntity.posZ - npc.posZ;
|
||||
return applyAbilityDamageInternal(npc, hitEntity, damage, knockback, 0.0f, dx, dz);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,6 +155,36 @@ public abstract class Ability implements IAbility {
|
||||
*/
|
||||
protected boolean applyAbilityDamage(EntityNPCInterface npc, EntityLivingBase hitEntity,
|
||||
float damage, float knockback, float knockbackUp) {
|
||||
double dx = hitEntity.posX - npc.posX;
|
||||
double dz = hitEntity.posZ - npc.posZ;
|
||||
return applyAbilityDamageInternal(npc, hitEntity, damage, knockback, knockbackUp, dx, dz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 hitEntity The entity being hit
|
||||
* @param damage The damage amount
|
||||
* @param knockback The horizontal knockback
|
||||
* @param knockbackDirX The X component of knockback direction
|
||||
* @param knockbackDirZ The Z component of knockback direction
|
||||
* @return true if damage was applied (not cancelled), false if cancelled
|
||||
*/
|
||||
protected boolean applyAbilityDamageWithDirection(EntityNPCInterface npc, EntityLivingBase hitEntity,
|
||||
float damage, float knockback,
|
||||
double knockbackDirX, double knockbackDirZ) {
|
||||
return applyAbilityDamageInternal(npc, hitEntity, damage, knockback, 0.0f, knockbackDirX, knockbackDirZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal unified damage application method.
|
||||
* All public damage methods delegate to this.
|
||||
*/
|
||||
private boolean applyAbilityDamageInternal(EntityNPCInterface npc, EntityLivingBase hitEntity,
|
||||
float damage, float knockback, float knockbackUp,
|
||||
double knockbackDirX, double knockbackDirZ) {
|
||||
DataAbilities dataAbilities = npc.abilities;
|
||||
AbilityEvent.HitEvent event = dataAbilities.fireHitEvent(
|
||||
this, currentTarget, hitEntity, damage, knockback, knockbackUp);
|
||||
@@ -168,49 +204,6 @@ public abstract class Ability implements IAbility {
|
||||
}
|
||||
|
||||
// Apply knockback if any
|
||||
if (finalKnockback > 0 || finalKnockbackUp > 0) {
|
||||
double dx = hitEntity.posX - npc.posX;
|
||||
double dz = hitEntity.posZ - npc.posZ;
|
||||
applyNpcKnockback(hitEntity, dx, dz, finalKnockback, finalKnockbackUp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 hitEntity The entity being hit
|
||||
* @param damage The damage amount
|
||||
* @param knockback The horizontal knockback
|
||||
* @param knockbackDirX The X component of knockback direction (normalized)
|
||||
* @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,
|
||||
float damage, float knockback,
|
||||
double knockbackDirX, double knockbackDirZ) {
|
||||
DataAbilities dataAbilities = npc.abilities;
|
||||
AbilityEvent.HitEvent event = dataAbilities.fireHitEvent(
|
||||
this, currentTarget, hitEntity, damage, knockback, 0.0f);
|
||||
|
||||
if (event == null) {
|
||||
return false; // Cancelled
|
||||
}
|
||||
|
||||
// Apply damage with potentially modified values
|
||||
float finalDamage = event.getDamage();
|
||||
float finalKnockback = event.getKnockback();
|
||||
float finalKnockbackUp = event.getKnockbackUp();
|
||||
|
||||
// Apply damage - use NpcDamageSource for consistency
|
||||
if (finalDamage > 0) {
|
||||
hitEntity.attackEntityFrom(new NpcDamageSource("mob", npc), finalDamage);
|
||||
}
|
||||
|
||||
// Apply knockback in specified direction
|
||||
if (finalKnockback > 0 || finalKnockbackUp > 0) {
|
||||
applyNpcKnockback(hitEntity, knockbackDirX, knockbackDirZ, finalKnockback, finalKnockbackUp);
|
||||
}
|
||||
@@ -383,6 +376,11 @@ public abstract class Ability implements IAbility {
|
||||
if (positionAtNpc) {
|
||||
// AOE_SELF abilities: telegraph follows NPC during windup
|
||||
instance.setEntityIdToFollow(npc.getEntityId());
|
||||
|
||||
// For LINE/CONE telegraphs: track target direction during windup
|
||||
if ((telegraphType == TelegraphType.LINE || telegraphType == TelegraphType.CONE) && target != null) {
|
||||
instance.setTargetEntityId(target.getEntityId());
|
||||
}
|
||||
} else if (target != null) {
|
||||
// AOE_TARGET abilities: telegraph follows target during windup
|
||||
instance.setEntityIdToFollow(target.getEntityId());
|
||||
@@ -487,8 +485,15 @@ public abstract class Ability implements IAbility {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tick the ability. Returns true if phase changed.
|
||||
* Note: Cooldown is managed by DataAbilities when the ability completes.
|
||||
* Tick the ability forward. Called every game tick while executing.
|
||||
* <p>
|
||||
* Normal flow: WINDUP -> ACTIVE -> IDLE (no recovery in normal completion)
|
||||
* Interrupted flow: WINDUP -> RECOVERY -> IDLE (recovery only on interrupt)
|
||||
* <p>
|
||||
* If waitForCompletion is true, the ability controls when active ends
|
||||
* by calling signalCompletion() instead of using activeTicks.
|
||||
*
|
||||
* @return true if phase changed this tick
|
||||
*/
|
||||
public boolean tick() {
|
||||
if (phase == AbilityPhase.IDLE) return false;
|
||||
@@ -504,16 +509,20 @@ public abstract class Ability implements IAbility {
|
||||
}
|
||||
break;
|
||||
case ACTIVE:
|
||||
if (currentTick >= activeTicks) {
|
||||
phase = AbilityPhase.RECOVERY;
|
||||
// If waitForCompletion is true, ability must call signalCompletion()
|
||||
// Otherwise, auto-complete when activeTicks is reached
|
||||
if (!waitForCompletion && currentTick >= activeTicks) {
|
||||
phase = AbilityPhase.IDLE;
|
||||
currentTick = 0;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case RECOVERY:
|
||||
// Recovery phase is only entered on interrupt
|
||||
// Count down recoveryTicks then go to IDLE
|
||||
if (currentTick >= recoveryTicks) {
|
||||
phase = AbilityPhase.IDLE;
|
||||
// Cooldown is managed by DataAbilities.onAbilityComplete()
|
||||
currentTick = 0;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@@ -522,11 +531,35 @@ public abstract class Ability implements IAbility {
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupt this ability
|
||||
* Signal that the ability has completed its active phase.
|
||||
* Called by abilities with waitForCompletion=true when they are done.
|
||||
* For example, AbilitySlam calls this when the NPC lands.
|
||||
*
|
||||
* @return true if phase changed to IDLE
|
||||
*/
|
||||
public boolean signalCompletion() {
|
||||
if (phase == AbilityPhase.ACTIVE) {
|
||||
phase = AbilityPhase.IDLE;
|
||||
currentTick = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupt this ability. Transitions to RECOVERY phase if interruptible.
|
||||
* Called when NPC takes direct damage during WINDUP.
|
||||
*/
|
||||
public void interrupt() {
|
||||
phase = AbilityPhase.IDLE;
|
||||
currentTick = 0;
|
||||
if (interruptible && phase == AbilityPhase.WINDUP) {
|
||||
// Interrupted during windup - go to recovery (dazed state)
|
||||
phase = AbilityPhase.RECOVERY;
|
||||
currentTick = 0;
|
||||
} else {
|
||||
// Force stop - go directly to IDLE
|
||||
phase = AbilityPhase.IDLE;
|
||||
currentTick = 0;
|
||||
}
|
||||
currentTarget = null;
|
||||
telegraphInstance = null;
|
||||
}
|
||||
@@ -611,6 +644,7 @@ public abstract class Ability implements IAbility {
|
||||
nbt.setInteger("active", activeTicks);
|
||||
nbt.setInteger("recovery", recoveryTicks);
|
||||
nbt.setBoolean("interruptible", interruptible);
|
||||
nbt.setBoolean("waitForCompletion", waitForCompletion);
|
||||
nbt.setBoolean("lockMovement", lockMovement);
|
||||
nbt.setInteger("windUpColor", windUpColor);
|
||||
nbt.setInteger("activeColor", activeColor);
|
||||
@@ -650,6 +684,7 @@ public abstract class Ability implements IAbility {
|
||||
activeTicks = nbt.getInteger("active");
|
||||
recoveryTicks = nbt.getInteger("recovery");
|
||||
interruptible = nbt.getBoolean("interruptible");
|
||||
waitForCompletion = nbt.getBoolean("waitForCompletion");
|
||||
lockMovement = nbt.getBoolean("lockMovement");
|
||||
// Support old telegraphColor key for backwards compatibility
|
||||
if (nbt.hasKey("windUpColor")) {
|
||||
@@ -807,6 +842,14 @@ public abstract class Ability implements IAbility {
|
||||
this.interruptible = interruptible;
|
||||
}
|
||||
|
||||
public boolean isWaitForCompletion() {
|
||||
return waitForCompletion;
|
||||
}
|
||||
|
||||
public void setWaitForCompletion(boolean waitForCompletion) {
|
||||
this.waitForCompletion = waitForCompletion;
|
||||
}
|
||||
|
||||
public boolean isLockMovement() {
|
||||
return lockMovement;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ 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.TargetingMode;
|
||||
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.EntityAbilityDisc;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -69,8 +71,8 @@ public class AbilityDisc extends Ability {
|
||||
this.windUpTicks = 20;
|
||||
this.activeTicks = 1;
|
||||
this.recoveryTicks = 10;
|
||||
this.telegraphType = TelegraphType.POINT;
|
||||
this.showTelegraph = false;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
this.showTelegraph = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,7 +107,7 @@ public class AbilityDisc extends Ability {
|
||||
EntityAbilityDisc disc = new EntityAbilityDisc(
|
||||
world, npc, target,
|
||||
spawnX, spawnY, spawnZ,
|
||||
discRadius, discThickness, innerColor, outerColor, rotationSpeed,
|
||||
discRadius, discThickness, innerColor, outerColor, outerColorEnabled, outerColorWidth, rotationSpeed,
|
||||
damage, knockback, knockbackUp,
|
||||
speed, homing, homingStrength, homingRange,
|
||||
boomerang, boomerangDelay,
|
||||
@@ -129,6 +131,33 @@ public class AbilityDisc extends Ability {
|
||||
public void onInterrupt(EntityNPCInterface npc, DamageSource source, float damage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelegraphInstance createTelegraph(EntityNPCInterface npc, EntityLivingBase target) {
|
||||
if (!showTelegraph || telegraphType == TelegraphType.NONE || target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create circle telegraph at target position
|
||||
Telegraph telegraph = Telegraph.circle(discRadius * 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, npc.rotationYaw);
|
||||
instance.setCasterEntityId(npc.getEntityId());
|
||||
instance.setEntityIdToFollow(target.getEntityId());
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTelegraphRadius() {
|
||||
return discRadius * 1.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setFloat("speed", speed);
|
||||
@@ -182,7 +211,7 @@ public class AbilityDisc extends Ability {
|
||||
this.outerColor = nbt.hasKey("outerColor") ? nbt.getInteger("outerColor") : 0xFF8800;
|
||||
this.outerColorWidth = nbt.hasKey("outerColorWidth") ? nbt.getFloat("outerColorWidth") : 1.8f;
|
||||
this.outerColorEnabled = !nbt.hasKey("outerColorEnabled") || nbt.getBoolean("outerColorEnabled");
|
||||
this.rotationSpeed = nbt.hasKey("rotationSpeed") ? nbt.getFloat("rotationSpeed") : 8.0f;
|
||||
this.rotationSpeed = nbt.hasKey("rotationSpeed") ? nbt.getFloat("rotationSpeed") : 5.0f;
|
||||
}
|
||||
|
||||
// Getters & Setters
|
||||
|
||||
@@ -4,6 +4,8 @@ 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.TargetingMode;
|
||||
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.EntityAbilityBeam;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -68,8 +70,8 @@ public class AbilityEnergyBeam extends Ability {
|
||||
this.windUpTicks = 40;
|
||||
this.activeTicks = 200; // Match maxLifetime to keep NPC locked while beam is active
|
||||
this.recoveryTicks = 5;
|
||||
this.telegraphType = TelegraphType.POINT;
|
||||
this.showTelegraph = false;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
this.showTelegraph = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,7 +106,7 @@ public class AbilityEnergyBeam extends Ability {
|
||||
EntityAbilityBeam beam = new EntityAbilityBeam(
|
||||
world, npc, target,
|
||||
spawnX, spawnY, spawnZ,
|
||||
beamWidth, headSize, innerColor, outerColor, rotationSpeed,
|
||||
beamWidth, headSize, innerColor, outerColor, outerColorEnabled, outerColorWidth, rotationSpeed,
|
||||
damage, knockback, knockbackUp,
|
||||
speed, homing, homingStrength, homingRange,
|
||||
explosive, explosionRadius, explosionDamageFalloff,
|
||||
@@ -127,6 +129,33 @@ public class AbilityEnergyBeam extends Ability {
|
||||
public void onInterrupt(EntityNPCInterface npc, DamageSource source, float damage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelegraphInstance createTelegraph(EntityNPCInterface npc, EntityLivingBase target) {
|
||||
if (!showTelegraph || telegraphType == TelegraphType.NONE || target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create circle telegraph at target position
|
||||
Telegraph telegraph = Telegraph.circle(headSize * 2.0f);
|
||||
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, npc.rotationYaw);
|
||||
instance.setCasterEntityId(npc.getEntityId());
|
||||
instance.setEntityIdToFollow(target.getEntityId());
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTelegraphRadius() {
|
||||
return headSize * 2.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setFloat("speed", speed);
|
||||
|
||||
@@ -128,17 +128,8 @@ public class AbilityGuard extends Ability {
|
||||
if (wasHit && counterSound != null && !counterSound.isEmpty()) {
|
||||
world.playSoundAtEntity(npc, counterSound, 1.0f, 1.2f);
|
||||
}
|
||||
if (counterAnimationId >= 0) {
|
||||
if (noppes.npcs.controllers.AnimationController.Instance != null) {
|
||||
noppes.npcs.controllers.data.Animation animation =
|
||||
noppes.npcs.controllers.AnimationController.Instance.animations.get(counterAnimationId);
|
||||
if (animation != null) {
|
||||
npc.display.animationData.setEnabled(true);
|
||||
npc.display.animationData.setAnimation(animation);
|
||||
npc.display.animationData.updateClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Use shared animation utility
|
||||
npc.abilities.playAbilityAnimation(counterAnimationId);
|
||||
|
||||
counterTriggered = false;
|
||||
lastAttacker = null;
|
||||
|
||||
@@ -184,6 +184,11 @@ public class AbilityHeal extends Ability {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt(EntityNPCInterface npc, net.minecraft.util.DamageSource source, float damage) {
|
||||
healedAllies.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
|
||||
@@ -46,6 +46,8 @@ public class AbilityLaserShot extends Ability {
|
||||
// Visual properties
|
||||
private int innerColor = 0xFFFFFF;
|
||||
private int outerColor = 0xFF0000;
|
||||
private float outerColorWidth = 1.8f;
|
||||
private boolean outerColorEnabled = true;
|
||||
|
||||
public AbilityLaserShot() {
|
||||
this.typeId = "ability.cnpc.laser_shot";
|
||||
@@ -103,7 +105,7 @@ public class AbilityLaserShot extends Ability {
|
||||
EntityAbilityLaser laser = new EntityAbilityLaser(
|
||||
world, npc, target,
|
||||
spawnX, spawnY, spawnZ,
|
||||
laserWidth, innerColor, outerColor,
|
||||
laserWidth, innerColor, outerColor, outerColorEnabled, outerColorWidth,
|
||||
damage, knockback, knockbackUp,
|
||||
expansionSpeed, lingerTicks,
|
||||
explosive, explosionRadius, explosionDamageFalloff,
|
||||
@@ -144,6 +146,8 @@ public class AbilityLaserShot extends Ability {
|
||||
nbt.setInteger("slowLevel", slowLevel);
|
||||
nbt.setInteger("innerColor", innerColor);
|
||||
nbt.setInteger("outerColor", outerColor);
|
||||
nbt.setFloat("outerColorWidth", outerColorWidth);
|
||||
nbt.setBoolean("outerColorEnabled", outerColorEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,6 +168,8 @@ public class AbilityLaserShot extends Ability {
|
||||
this.slowLevel = nbt.hasKey("slowLevel") ? nbt.getInteger("slowLevel") : 0;
|
||||
this.innerColor = nbt.hasKey("innerColor") ? nbt.getInteger("innerColor") : 0xFFFFFF;
|
||||
this.outerColor = nbt.hasKey("outerColor") ? nbt.getInteger("outerColor") : 0xFF0000;
|
||||
this.outerColorWidth = nbt.hasKey("outerColorWidth") ? nbt.getFloat("outerColorWidth") : 1.8f;
|
||||
this.outerColorEnabled = !nbt.hasKey("outerColorEnabled") || nbt.getBoolean("outerColorEnabled");
|
||||
}
|
||||
|
||||
// Getters & Setters
|
||||
@@ -199,4 +205,8 @@ public class AbilityLaserShot extends Ability {
|
||||
public void setInnerColor(int innerColor) { this.innerColor = innerColor; }
|
||||
public int getOuterColor() { return outerColor; }
|
||||
public void setOuterColor(int outerColor) { this.outerColor = outerColor; }
|
||||
public float getOuterColorWidth() { return outerColorWidth; }
|
||||
public void setOuterColorWidth(float outerColorWidth) { this.outerColorWidth = outerColorWidth; }
|
||||
public boolean isOuterColorEnabled() { return outerColorEnabled; }
|
||||
public void setOuterColorEnabled(boolean outerColorEnabled) { this.outerColorEnabled = outerColorEnabled; }
|
||||
}
|
||||
@@ -4,6 +4,8 @@ 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.TargetingMode;
|
||||
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 net.minecraft.entity.EntityLivingBase;
|
||||
@@ -65,8 +67,8 @@ public class AbilityOrb extends Ability {
|
||||
this.windUpTicks = 30;
|
||||
this.activeTicks = 1; // Minimal - just spawn and done
|
||||
this.recoveryTicks = 10;
|
||||
this.telegraphType = TelegraphType.POINT;
|
||||
this.showTelegraph = false;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
this.showTelegraph = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,7 +105,7 @@ public class AbilityOrb extends Ability {
|
||||
EntityAbilityOrb orb = new EntityAbilityOrb(
|
||||
world, npc, target,
|
||||
spawnX, spawnY, spawnZ,
|
||||
orbSize, innerColor, outerColor, rotationSpeed,
|
||||
orbSize, innerColor, outerColor, outerColorEnabled, outerColorWidth, rotationSpeed,
|
||||
damage, knockback, knockbackUp,
|
||||
orbSpeed, homing, homingStrength, homingRange,
|
||||
explosive, explosionRadius, explosionDamageFalloff,
|
||||
@@ -129,6 +131,33 @@ public class AbilityOrb extends Ability {
|
||||
// Nothing to clean up - entity manages itself
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelegraphInstance createTelegraph(EntityNPCInterface npc, 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, npc.rotationYaw);
|
||||
instance.setCasterEntityId(npc.getEntityId());
|
||||
instance.setEntityIdToFollow(target.getEntityId());
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTelegraphRadius() {
|
||||
return orbSize * 1.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTypeNBT(NBTTagCompound nbt) {
|
||||
nbt.setFloat("orbSpeed", orbSpeed);
|
||||
|
||||
@@ -49,10 +49,11 @@ public class AbilitySlam extends Ability {
|
||||
this.name = "Slam";
|
||||
this.targetingMode = TargetingMode.AOE_SELF; // Can also be AOE_TARGET to leap to target
|
||||
this.windUpTicks = 30;
|
||||
this.activeTicks = 60; // Max time in air + landing
|
||||
this.activeTicks = 60; // Max time in air (used as timeout if waitForCompletion fails)
|
||||
this.recoveryTicks = 10;
|
||||
this.cooldownTicks = 100;
|
||||
this.lockMovement = true;
|
||||
this.waitForCompletion = true; // Ability completes when NPC lands, not by activeTicks
|
||||
this.minRange = 2.0f;
|
||||
this.maxRange = 15.0f;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
@@ -258,12 +259,15 @@ public class AbilitySlam extends Ability {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when NPC lands - deal AOE damage.
|
||||
* Called when NPC lands - deal AOE damage and signal completion.
|
||||
*/
|
||||
private void onLanding(EntityNPCInterface npc, World world) {
|
||||
hasLanded = true;
|
||||
npc.setNpcJumpingState(false);
|
||||
|
||||
// Signal that the ability has completed its active phase
|
||||
signalCompletion();
|
||||
|
||||
// Reset fall distance to prevent fall damage on landing
|
||||
npc.fallDistance = 0;
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ public class AbilitySweeper extends Ability {
|
||||
// Spawn the entity that handles BOTH visuals AND damage
|
||||
activeEntity = new EntityAbilitySweeper(world, npc, target,
|
||||
beamLength, beamWidth, beamHeight,
|
||||
innerColor, outerColor,
|
||||
innerColor, outerColor, outerColorEnabled, outerColorWidth,
|
||||
sweepSpeed, numberOfRotations,
|
||||
damage, damageInterval, piercing,
|
||||
lockOnTarget);
|
||||
|
||||
@@ -26,6 +26,7 @@ public class TelegraphInstance {
|
||||
// Entity tracking
|
||||
private int entityIdToFollow = -1;
|
||||
private int casterEntityId = -1;
|
||||
private int targetEntityId = -1; // For LINE telegraphs: entity to face (yaw tracking)
|
||||
|
||||
// Timing
|
||||
private int remainingTicks;
|
||||
@@ -81,6 +82,16 @@ public class TelegraphInstance {
|
||||
}
|
||||
}
|
||||
|
||||
// Update yaw to face target entity (for LINE telegraphs)
|
||||
if (targetEntityId >= 0 && world != null) {
|
||||
Entity target = world.getEntityByID(targetEntityId);
|
||||
if (target != null) {
|
||||
double dx = target.posX - this.x;
|
||||
double dz = target.posZ - this.z;
|
||||
this.yaw = (float) (Math.atan2(-dx, dz) * 180.0 / Math.PI);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for warning phase transition
|
||||
if (!isWarning && telegraph != null) {
|
||||
if (remainingTicks <= telegraph.getWarningStartTick()) {
|
||||
@@ -108,7 +119,7 @@ public class TelegraphInstance {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alpha-modulated color for pulsing animation.
|
||||
* Get alpha-modulated color for breathing animation.
|
||||
*/
|
||||
public int getAnimatedColor(float partialTicks) {
|
||||
int baseColor = getCurrentColor();
|
||||
@@ -116,9 +127,12 @@ public class TelegraphInstance {
|
||||
return baseColor;
|
||||
}
|
||||
|
||||
// Pulsing animation: modulate alpha based on time
|
||||
// Breathing animation: slow, smooth modulation of alpha
|
||||
float time = (totalTicks - remainingTicks) + partialTicks;
|
||||
float pulse = (float) (Math.sin(time * 0.3) * 0.3 + 0.7); // 0.4 to 1.0 range
|
||||
// Slower breathing: 0.08 for ~4 second cycle, smoother sine curve
|
||||
float breathPhase = (float) Math.sin(time * 0.08);
|
||||
// Smooth easing: square the sine for more organic feel, range 0.5 to 1.0
|
||||
float pulse = 0.5f + (breathPhase * breathPhase * 0.5f * Math.signum(breathPhase));
|
||||
|
||||
int alpha = (baseColor >> 24) & 0xFF;
|
||||
int r = (baseColor >> 16) & 0xFF;
|
||||
@@ -149,6 +163,7 @@ public class TelegraphInstance {
|
||||
|
||||
nbt.setInteger("entityIdToFollow", entityIdToFollow);
|
||||
nbt.setInteger("casterEntityId", casterEntityId);
|
||||
nbt.setInteger("targetEntityId", targetEntityId);
|
||||
|
||||
nbt.setInteger("remainingTicks", remainingTicks);
|
||||
nbt.setInteger("totalTicks", totalTicks);
|
||||
@@ -173,6 +188,7 @@ public class TelegraphInstance {
|
||||
|
||||
this.entityIdToFollow = nbt.getInteger("entityIdToFollow");
|
||||
this.casterEntityId = nbt.getInteger("casterEntityId");
|
||||
this.targetEntityId = nbt.getInteger("targetEntityId");
|
||||
|
||||
this.remainingTicks = nbt.getInteger("remainingTicks");
|
||||
this.totalTicks = nbt.getInteger("totalTicks");
|
||||
@@ -241,11 +257,12 @@ public class TelegraphInstance {
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the telegraph at its current position.
|
||||
* Locks the telegraph at its current position and direction.
|
||||
* Stops following any entity - used when ability commits to action.
|
||||
*/
|
||||
public void lockPosition() {
|
||||
this.entityIdToFollow = -1;
|
||||
this.targetEntityId = -1;
|
||||
}
|
||||
|
||||
public int getCasterEntityId() {
|
||||
@@ -256,6 +273,14 @@ public class TelegraphInstance {
|
||||
this.casterEntityId = casterEntityId;
|
||||
}
|
||||
|
||||
public int getTargetEntityId() {
|
||||
return targetEntityId;
|
||||
}
|
||||
|
||||
public void setTargetEntityId(int targetEntityId) {
|
||||
this.targetEntityId = targetEntityId;
|
||||
}
|
||||
|
||||
public int getRemainingTicks() {
|
||||
return remainingTicks;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ public class EntityAbilityBeam extends EntityAbilityProjectile {
|
||||
*/
|
||||
public EntityAbilityBeam(World world, EntityNPCInterface owner, EntityLivingBase target,
|
||||
double x, double y, double z,
|
||||
float beamWidth, float headSize, int innerColor, int outerColor, float rotationSpeed,
|
||||
float beamWidth, float headSize, int innerColor, int outerColor,
|
||||
boolean outerColorEnabled, float outerColorWidth, float rotationSpeed,
|
||||
float damage, float knockback, float knockbackUp,
|
||||
float speed, boolean homing, float homingStrength, float homingRange,
|
||||
boolean explosive, float explosionRadius, float explosionDamageFalloff,
|
||||
@@ -69,7 +70,7 @@ public class EntityAbilityBeam extends EntityAbilityProjectile {
|
||||
|
||||
// Initialize base properties
|
||||
initProjectile(owner, target, x, y, z,
|
||||
headSize, innerColor, outerColor, rotationSpeed,
|
||||
headSize, innerColor, outerColor, outerColorEnabled, outerColorWidth, rotationSpeed,
|
||||
damage, knockback, knockbackUp,
|
||||
explosive, explosionRadius, explosionDamageFalloff,
|
||||
stunDuration, slowDuration, slowLevel,
|
||||
|
||||
@@ -44,7 +44,8 @@ public class EntityAbilityDisc extends EntityAbilityProjectile {
|
||||
*/
|
||||
public EntityAbilityDisc(World world, EntityNPCInterface owner, EntityLivingBase target,
|
||||
double x, double y, double z,
|
||||
float discRadius, float discThickness, int innerColor, int outerColor, float rotationSpeed,
|
||||
float discRadius, float discThickness, int innerColor, int outerColor,
|
||||
boolean outerColorEnabled, float outerColorWidth, float rotationSpeed,
|
||||
float damage, float knockback, float knockbackUp,
|
||||
float speed, boolean homing, float homingStrength, float homingRange,
|
||||
boolean boomerang, int boomerangDelay,
|
||||
@@ -55,7 +56,7 @@ public class EntityAbilityDisc extends EntityAbilityProjectile {
|
||||
|
||||
// Initialize base properties (use discRadius as size for base)
|
||||
initProjectile(owner, target, x, y, z,
|
||||
discRadius, innerColor, outerColor, rotationSpeed,
|
||||
discRadius, innerColor, outerColor, outerColorEnabled, outerColorWidth, rotationSpeed,
|
||||
damage, knockback, knockbackUp,
|
||||
explosive, explosionRadius, explosionDamageFalloff,
|
||||
stunDuration, slowDuration, slowLevel,
|
||||
|
||||
@@ -50,6 +50,7 @@ public class EntityAbilityLaser extends EntityAbilityProjectile {
|
||||
public EntityAbilityLaser(World world, EntityNPCInterface owner, EntityLivingBase target,
|
||||
double x, double y, double z,
|
||||
float laserWidth, int innerColor, int outerColor,
|
||||
boolean outerColorEnabled, float outerColorWidth,
|
||||
float damage, float knockback, float knockbackUp,
|
||||
float expansionSpeed, int lingerTicks,
|
||||
boolean explosive, float explosionRadius, float explosionDamageFalloff,
|
||||
@@ -59,7 +60,7 @@ public class EntityAbilityLaser extends EntityAbilityProjectile {
|
||||
|
||||
// Initialize base properties
|
||||
initProjectile(owner, target, x, y, z,
|
||||
laserWidth, innerColor, outerColor, 0.0f, // No rotation for laser
|
||||
laserWidth, innerColor, outerColor, outerColorEnabled, outerColorWidth, 0.0f, // No rotation for laser
|
||||
damage, knockback, knockbackUp,
|
||||
explosive, explosionRadius, explosionDamageFalloff,
|
||||
stunDuration, slowDuration, slowLevel,
|
||||
|
||||
@@ -34,7 +34,8 @@ public class EntityAbilityOrb extends EntityAbilityProjectile {
|
||||
*/
|
||||
public EntityAbilityOrb(World world, EntityNPCInterface owner, EntityLivingBase target,
|
||||
double x, double y, double z,
|
||||
float orbSize, int innerColor, int outerColor, float rotationSpeed,
|
||||
float orbSize, int innerColor, int outerColor,
|
||||
boolean outerColorEnabled, float outerColorWidth, float rotationSpeed,
|
||||
float damage, float knockback, float knockbackUp,
|
||||
float speed, boolean homing, float homingStrength, float homingRange,
|
||||
boolean explosive, float explosionRadius, float explosionDamageFalloff,
|
||||
@@ -44,7 +45,7 @@ public class EntityAbilityOrb extends EntityAbilityProjectile {
|
||||
|
||||
// Initialize base properties
|
||||
initProjectile(owner, target, x, y, z,
|
||||
orbSize, innerColor, outerColor, rotationSpeed,
|
||||
orbSize, innerColor, outerColor, outerColorEnabled, outerColorWidth, rotationSpeed,
|
||||
damage, knockback, knockbackUp,
|
||||
explosive, explosionRadius, explosionDamageFalloff,
|
||||
stunDuration, slowDuration, slowLevel,
|
||||
|
||||
@@ -29,6 +29,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
|
||||
protected float size = 1.0f;
|
||||
protected int innerColor = 0xFFFFFF;
|
||||
protected int outerColor = 0x8888FF;
|
||||
protected boolean outerColorEnabled = true;
|
||||
protected float outerColorWidth = 1.8f;
|
||||
protected float rotationSpeed = 4.0f;
|
||||
|
||||
// ==================== COMBAT PROPERTIES ====================
|
||||
@@ -85,7 +87,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
|
||||
*/
|
||||
protected void initProjectile(EntityNPCInterface owner, EntityLivingBase target,
|
||||
double x, double y, double z,
|
||||
float size, int innerColor, int outerColor, float rotationSpeed,
|
||||
float size, int innerColor, int outerColor,
|
||||
boolean outerColorEnabled, float outerColorWidth, float rotationSpeed,
|
||||
float damage, float knockback, float knockbackUp,
|
||||
boolean explosive, float explosionRadius, float explosionDamageFalloff,
|
||||
int stunDuration, int slowDuration, int slowLevel,
|
||||
@@ -102,6 +105,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
|
||||
this.size = size;
|
||||
this.innerColor = innerColor;
|
||||
this.outerColor = outerColor;
|
||||
this.outerColorEnabled = outerColorEnabled;
|
||||
this.outerColorWidth = outerColorWidth;
|
||||
this.rotationSpeed = rotationSpeed;
|
||||
|
||||
// Combat
|
||||
@@ -384,6 +389,14 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
|
||||
return outerColor;
|
||||
}
|
||||
|
||||
public boolean isOuterColorEnabled() {
|
||||
return outerColorEnabled;
|
||||
}
|
||||
|
||||
public float getOuterColorWidth() {
|
||||
return outerColorWidth;
|
||||
}
|
||||
|
||||
public float getRotationSpeed() {
|
||||
return rotationSpeed;
|
||||
}
|
||||
@@ -438,6 +451,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
|
||||
this.size = nbt.getFloat("Size");
|
||||
this.innerColor = nbt.getInteger("InnerColor");
|
||||
this.outerColor = nbt.getInteger("OuterColor");
|
||||
this.outerColorEnabled = !nbt.hasKey("OuterColorEnabled") || nbt.getBoolean("OuterColorEnabled");
|
||||
this.outerColorWidth = nbt.hasKey("OuterColorWidth") ? nbt.getFloat("OuterColorWidth") : 1.8f;
|
||||
this.rotationSpeed = nbt.hasKey("RotationSpeed") ? nbt.getFloat("RotationSpeed") : 4.0f;
|
||||
|
||||
this.damage = nbt.getFloat("Damage");
|
||||
@@ -480,6 +495,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
|
||||
nbt.setFloat("Size", size);
|
||||
nbt.setInteger("InnerColor", innerColor);
|
||||
nbt.setInteger("OuterColor", outerColor);
|
||||
nbt.setBoolean("OuterColorEnabled", outerColorEnabled);
|
||||
nbt.setFloat("OuterColorWidth", outerColorWidth);
|
||||
nbt.setFloat("RotationSpeed", rotationSpeed);
|
||||
|
||||
nbt.setFloat("Damage", damage);
|
||||
|
||||
@@ -32,6 +32,8 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
private float beamHeight = 0.8f;
|
||||
private int innerColor = 0xFF6600;
|
||||
private int outerColor = 0xFF0000;
|
||||
private boolean outerColorEnabled = true;
|
||||
private float outerColorWidth = 1.8f;
|
||||
|
||||
// Combat properties
|
||||
private float damage = 5.0f;
|
||||
@@ -70,6 +72,7 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
public EntityAbilitySweeper(World world, EntityNPCInterface owner, EntityLivingBase target,
|
||||
float beamLength, float beamWidth, float beamHeight,
|
||||
int innerColor, int outerColor,
|
||||
boolean outerColorEnabled, float outerColorWidth,
|
||||
float sweepSpeed, int numberOfRotations,
|
||||
float damage, int damageInterval, boolean piercing,
|
||||
boolean lockOnTarget) {
|
||||
@@ -82,6 +85,8 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
this.beamHeight = beamHeight;
|
||||
this.innerColor = innerColor;
|
||||
this.outerColor = outerColor;
|
||||
this.outerColorEnabled = outerColorEnabled;
|
||||
this.outerColorWidth = outerColorWidth;
|
||||
this.sweepSpeed = sweepSpeed;
|
||||
this.numberOfRotations = numberOfRotations;
|
||||
this.damage = damage;
|
||||
@@ -323,6 +328,14 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
return outerColor;
|
||||
}
|
||||
|
||||
public boolean isOuterColorEnabled() {
|
||||
return outerColorEnabled;
|
||||
}
|
||||
|
||||
public float getOuterColorWidth() {
|
||||
return outerColorWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full interpolated angle INCLUDING base yaw for rendering.
|
||||
*/
|
||||
@@ -353,6 +366,8 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
this.beamHeight = nbt.getFloat("BeamHeight");
|
||||
this.innerColor = nbt.getInteger("InnerColor");
|
||||
this.outerColor = nbt.getInteger("OuterColor");
|
||||
this.outerColorEnabled = !nbt.hasKey("OuterColorEnabled") || nbt.getBoolean("OuterColorEnabled");
|
||||
this.outerColorWidth = nbt.hasKey("OuterColorWidth") ? nbt.getFloat("OuterColorWidth") : 1.8f;
|
||||
this.sweepSpeed = nbt.getFloat("SweepSpeed");
|
||||
this.numberOfRotations = nbt.getInteger("NumRotations");
|
||||
this.completedRotations = nbt.getInteger("CompletedRotations");
|
||||
@@ -375,6 +390,8 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
nbt.setFloat("BeamHeight", beamHeight);
|
||||
nbt.setInteger("InnerColor", innerColor);
|
||||
nbt.setInteger("OuterColor", outerColor);
|
||||
nbt.setBoolean("OuterColorEnabled", outerColorEnabled);
|
||||
nbt.setFloat("OuterColorWidth", outerColorWidth);
|
||||
nbt.setFloat("SweepSpeed", sweepSpeed);
|
||||
nbt.setInteger("NumRotations", numberOfRotations);
|
||||
nbt.setInteger("CompletedRotations", completedRotations);
|
||||
@@ -399,6 +416,8 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
buffer.writeFloat(beamHeight);
|
||||
buffer.writeInt(innerColor);
|
||||
buffer.writeInt(outerColor);
|
||||
buffer.writeBoolean(outerColorEnabled);
|
||||
buffer.writeFloat(outerColorWidth);
|
||||
buffer.writeFloat(sweepSpeed);
|
||||
buffer.writeInt(numberOfRotations);
|
||||
buffer.writeInt(completedRotations);
|
||||
@@ -420,6 +439,8 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
this.beamHeight = buffer.readFloat();
|
||||
this.innerColor = buffer.readInt();
|
||||
this.outerColor = buffer.readInt();
|
||||
this.outerColorEnabled = buffer.readBoolean();
|
||||
this.outerColorWidth = buffer.readFloat();
|
||||
this.sweepSpeed = buffer.readFloat();
|
||||
this.numberOfRotations = buffer.readInt();
|
||||
this.completedRotations = buffer.readInt();
|
||||
|
||||
@@ -18,9 +18,7 @@ import noppes.npcs.scripted.NpcAPI;
|
||||
import noppes.npcs.scripted.event.AbilityEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
@@ -47,9 +45,14 @@ public class DataAbilities {
|
||||
public boolean enabled = false;
|
||||
|
||||
/**
|
||||
* Minimum ticks between ability selections (global cooldown)
|
||||
* Minimum cooldown ticks between abilities (global minimum)
|
||||
*/
|
||||
public int globalCooldown = 20;
|
||||
public int minCooldown = 80;
|
||||
|
||||
/**
|
||||
* Maximum cooldown ticks between abilities (global maximum)
|
||||
*/
|
||||
public int maxCooldown = 200;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// RUNTIME STATE (not saved)
|
||||
@@ -61,14 +64,9 @@ public class DataAbilities {
|
||||
private transient Ability currentAbility;
|
||||
|
||||
/**
|
||||
* Per-ability cooldown timers (ability ID -> world time when cooldown ends)
|
||||
* World time when NPC cooldown ends (can select next ability)
|
||||
*/
|
||||
private transient Map<String, Long> cooldowns = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Ticks until next ability can be selected (global cooldown timer)
|
||||
*/
|
||||
private transient int globalCooldownTimer = 0;
|
||||
private transient long cooldownEndTime = 0;
|
||||
|
||||
/**
|
||||
* Last target used for ability execution
|
||||
@@ -101,11 +99,6 @@ public class DataAbilities {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tick global cooldown
|
||||
if (globalCooldownTimer > 0) {
|
||||
globalCooldownTimer--;
|
||||
}
|
||||
|
||||
// Tick current ability if executing
|
||||
if (currentAbility != null && currentAbility.isExecuting()) {
|
||||
tickCurrentAbility();
|
||||
@@ -192,24 +185,33 @@ public class DataAbilities {
|
||||
npc.motionZ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock look direction at target during ACTIVE phase only
|
||||
if (shouldLockLookDirection() && target != null && target.isEntityAlive()) {
|
||||
npc.getLookHelper().setLookPositionWithEntity(target, 30.0F, 30.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an ability completes.
|
||||
* Calculates cooldown as: random(minCooldown, maxCooldown) + ability's cooldown offset
|
||||
*/
|
||||
private void onAbilityComplete() {
|
||||
if (currentAbility != null) {
|
||||
// Stop any ability animation
|
||||
stopAbilityAnimation();
|
||||
|
||||
// Start per-ability cooldown
|
||||
startCooldown(currentAbility);
|
||||
// Calculate cooldown: random(min, max) + ability offset
|
||||
int baseCooldown = minCooldown;
|
||||
if (maxCooldown > minCooldown) {
|
||||
baseCooldown = minCooldown + random.nextInt(maxCooldown - minCooldown + 1);
|
||||
}
|
||||
int totalCooldown = baseCooldown + currentAbility.getCooldownTicks();
|
||||
cooldownEndTime = npc.worldObj.getTotalWorldTime() + totalCooldown;
|
||||
|
||||
currentAbility = null;
|
||||
lastTarget = null;
|
||||
|
||||
// Start global cooldown
|
||||
globalCooldownTimer = globalCooldown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +249,8 @@ public class DataAbilities {
|
||||
if (currentAbility != null && currentAbility.isExecuting()) {
|
||||
return false;
|
||||
}
|
||||
if (globalCooldownTimer > 0) {
|
||||
// Check if still on cooldown
|
||||
if (npc.worldObj.getTotalWorldTime() < cooldownEndTime) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -293,11 +296,6 @@ public class DataAbilities {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
if (isOnCooldown(ability)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if already executing
|
||||
if (ability.isExecuting()) {
|
||||
return false;
|
||||
@@ -358,8 +356,11 @@ public class DataAbilities {
|
||||
|
||||
/**
|
||||
* Play an animation on the NPC by ID.
|
||||
* Public so abilities can trigger animations directly if needed.
|
||||
*
|
||||
* @param animationId The global animation ID to play, or -1 for none
|
||||
*/
|
||||
private void playAbilityAnimation(int animationId) {
|
||||
public void playAbilityAnimation(int animationId) {
|
||||
if (animationId < 0) return;
|
||||
if (AnimationController.Instance == null) return;
|
||||
|
||||
@@ -407,37 +408,25 @@ public class DataAbilities {
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Check if an ability is on cooldown.
|
||||
* Check if NPC is on cooldown (cannot use any ability).
|
||||
*/
|
||||
public boolean isOnCooldown(Ability ability) {
|
||||
Long endTime = cooldowns.get(ability.getId());
|
||||
if (endTime == null) {
|
||||
return false;
|
||||
}
|
||||
return npc.worldObj.getTotalWorldTime() < endTime;
|
||||
public boolean isOnCooldown() {
|
||||
return npc.worldObj.getTotalWorldTime() < cooldownEndTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start cooldown for an ability.
|
||||
* Get remaining cooldown ticks.
|
||||
*/
|
||||
private void startCooldown(Ability ability) {
|
||||
long endTime = npc.worldObj.getTotalWorldTime() + ability.getCooldownTicks();
|
||||
cooldowns.put(ability.getId(), endTime);
|
||||
public long getRemainingCooldown() {
|
||||
long remaining = cooldownEndTime - npc.worldObj.getTotalWorldTime();
|
||||
return remaining > 0 ? remaining : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset cooldown for a specific ability.
|
||||
* Reset cooldown (allow immediate ability use).
|
||||
*/
|
||||
public void resetCooldown(String abilityId) {
|
||||
cooldowns.remove(abilityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all cooldowns.
|
||||
*/
|
||||
public void resetAllCooldowns() {
|
||||
cooldowns.clear();
|
||||
globalCooldownTimer = 0;
|
||||
public void resetCooldown() {
|
||||
cooldownEndTime = 0;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
@@ -489,9 +478,18 @@ public class DataAbilities {
|
||||
|
||||
/**
|
||||
* Record a hit for the hit count condition.
|
||||
* Includes periodic cleanup to prevent memory leak.
|
||||
*/
|
||||
private void recordHit() {
|
||||
recentHitTimes.add(npc.worldObj.getTotalWorldTime());
|
||||
long currentTime = npc.worldObj.getTotalWorldTime();
|
||||
recentHitTimes.add(currentTime);
|
||||
|
||||
// Cleanup old entries periodically to prevent unbounded growth
|
||||
// Keep only hits from last 5 minutes (6000 ticks) max
|
||||
if (recentHitTimes.size() > 50) {
|
||||
long cutoff = currentTime - 6000;
|
||||
recentHitTimes.removeIf(time -> time < cutoff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,6 +515,8 @@ public class DataAbilities {
|
||||
|
||||
/**
|
||||
* Interrupt the currently executing ability.
|
||||
* The ability will transition to RECOVERY phase (dazed state) and continue ticking.
|
||||
* When RECOVERY completes, the ability will move to IDLE and onAbilityComplete() is called.
|
||||
*/
|
||||
public void interruptCurrentAbility(DamageSource source, float damage) {
|
||||
if (currentAbility != null) {
|
||||
@@ -532,9 +532,10 @@ public class DataAbilities {
|
||||
NpcAPI.EVENT_BUS.post(interruptEvent);
|
||||
|
||||
currentAbility.onInterrupt(npc, source, damage);
|
||||
currentAbility.interrupt();
|
||||
currentAbility = null;
|
||||
lastTarget = null;
|
||||
currentAbility.interrupt(); // This now transitions to RECOVERY, not IDLE
|
||||
|
||||
// Don't clear currentAbility - let it tick through RECOVERY phase
|
||||
// When RECOVERY ends and phase becomes IDLE, onAbilityComplete() will be called
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,7 +606,7 @@ public class DataAbilities {
|
||||
*/
|
||||
public void reset() {
|
||||
stopCurrentAbility();
|
||||
resetAllCooldowns();
|
||||
resetCooldown();
|
||||
|
||||
// Reset execution state on all abilities
|
||||
for (Ability ability : abilities) {
|
||||
@@ -707,6 +708,24 @@ public class DataAbilities {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if NPC's look direction should be locked to target.
|
||||
* Only locks during ACTIVE phase when lockMovement is true.
|
||||
* This allows the NPC to freely track target during WINDUP and RECOVERY.
|
||||
*/
|
||||
public boolean shouldLockLookDirection() {
|
||||
if (currentAbility == null || !currentAbility.isExecuting()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only lock look direction during ACTIVE phase
|
||||
if (currentAbility.getPhase() != AbilityPhase.ACTIVE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return currentAbility.isLockMovement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if NPC should skip normal attacks during ability execution.
|
||||
*/
|
||||
@@ -724,7 +743,8 @@ public class DataAbilities {
|
||||
|
||||
public NBTTagCompound writeToNBT(NBTTagCompound compound) {
|
||||
compound.setBoolean("AbilitiesEnabled", enabled);
|
||||
compound.setInteger("AbilityGlobalCooldown", globalCooldown);
|
||||
compound.setInteger("AbilityMinCooldown", minCooldown);
|
||||
compound.setInteger("AbilityMaxCooldown", maxCooldown);
|
||||
|
||||
NBTTagList abilityList = new NBTTagList();
|
||||
for (Ability ability : abilities) {
|
||||
@@ -737,8 +757,20 @@ public class DataAbilities {
|
||||
|
||||
public void readFromNBT(NBTTagCompound compound) {
|
||||
enabled = compound.getBoolean("AbilitiesEnabled");
|
||||
globalCooldown = compound.hasKey("AbilityGlobalCooldown") ?
|
||||
compound.getInteger("AbilityGlobalCooldown") : 20;
|
||||
|
||||
// Support old globalCooldown for backwards compatibility
|
||||
if (compound.hasKey("AbilityMinCooldown")) {
|
||||
minCooldown = compound.getInteger("AbilityMinCooldown");
|
||||
maxCooldown = compound.getInteger("AbilityMaxCooldown");
|
||||
} else if (compound.hasKey("AbilityGlobalCooldown")) {
|
||||
// Migrate old single cooldown to min/max range
|
||||
int oldCooldown = compound.getInteger("AbilityGlobalCooldown");
|
||||
minCooldown = oldCooldown;
|
||||
maxCooldown = oldCooldown * 2; // Give some range
|
||||
} else {
|
||||
minCooldown = 20;
|
||||
maxCooldown = 60;
|
||||
}
|
||||
|
||||
abilities.clear();
|
||||
NBTTagList abilityList = compound.getTagList("Abilities", 10);
|
||||
|
||||
@@ -51,7 +51,8 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
|
||||
// Settings
|
||||
private boolean abilitiesEnabled = false;
|
||||
private int globalCooldown = 20;
|
||||
private int minCooldown = 20;
|
||||
private int maxCooldown = 60;
|
||||
|
||||
private String search = "";
|
||||
private int selectedAbilityIndex = -1;
|
||||
@@ -72,12 +73,18 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
addLabel(new GuiNpcLabel(100, "gui.enabled", guiLeft + 5, y + 5));
|
||||
addButton(new GuiNpcButton(100, guiLeft + 55, y, 50, 20, new String[]{"gui.no", "gui.yes"}, abilitiesEnabled ? 1 : 0));
|
||||
|
||||
// Global cooldown (spaced out since combat only is removed)
|
||||
addLabel(new GuiNpcLabel(101, "ability.globalCooldown", guiLeft + 140, y + 5));
|
||||
GuiNpcTextField cooldownField = new GuiNpcTextField(101, this, fontRendererObj, guiLeft + 230, y, 50, 20, String.valueOf(globalCooldown));
|
||||
cooldownField.setIntegersOnly();
|
||||
cooldownField.setMinMaxDefault(0, 10000, 20);
|
||||
addTextField(cooldownField);
|
||||
// Cooldown range: Min Cooldown and Max Cooldown
|
||||
addLabel(new GuiNpcLabel(101, "ability.minCooldown", guiLeft + 115, y + 5));
|
||||
GuiNpcTextField minField = new GuiNpcTextField(101, this, fontRendererObj, guiLeft + 185, y, 40, 20, String.valueOf(minCooldown));
|
||||
minField.setIntegersOnly();
|
||||
minField.setMinMaxDefault(0, 10000, 20);
|
||||
addTextField(minField);
|
||||
|
||||
addLabel(new GuiNpcLabel(102, "ability.maxCooldown", guiLeft + 235, y + 5));
|
||||
GuiNpcTextField maxField = new GuiNpcTextField(102, this, fontRendererObj, guiLeft + 305, y, 40, 20, String.valueOf(maxCooldown));
|
||||
maxField.setIntegersOnly();
|
||||
maxField.setMinMaxDefault(0, 10000, 60);
|
||||
addTextField(maxField);
|
||||
|
||||
y += 28;
|
||||
|
||||
@@ -254,7 +261,19 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
@Override
|
||||
public void setGuiData(NBTTagCompound compound) {
|
||||
abilitiesEnabled = compound.getBoolean("AbilitiesEnabled");
|
||||
globalCooldown = compound.hasKey("AbilityGlobalCooldown") ? compound.getInteger("AbilityGlobalCooldown") : 20;
|
||||
|
||||
// Support old globalCooldown for backwards compatibility
|
||||
if (compound.hasKey("AbilityMinCooldown")) {
|
||||
minCooldown = compound.getInteger("AbilityMinCooldown");
|
||||
maxCooldown = compound.getInteger("AbilityMaxCooldown");
|
||||
} else if (compound.hasKey("AbilityGlobalCooldown")) {
|
||||
int oldCooldown = compound.getInteger("AbilityGlobalCooldown");
|
||||
minCooldown = oldCooldown;
|
||||
maxCooldown = oldCooldown * 2;
|
||||
} else {
|
||||
minCooldown = 20;
|
||||
maxCooldown = 60;
|
||||
}
|
||||
|
||||
npcAbilities.clear();
|
||||
NBTTagList abilityList = compound.getTagList("Abilities", 10);
|
||||
@@ -352,7 +371,8 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
public void save() {
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
compound.setBoolean("AbilitiesEnabled", abilitiesEnabled);
|
||||
compound.setInteger("AbilityGlobalCooldown", globalCooldown);
|
||||
compound.setInteger("AbilityMinCooldown", minCooldown);
|
||||
compound.setInteger("AbilityMaxCooldown", maxCooldown);
|
||||
|
||||
NBTTagList abilityList = new NBTTagList();
|
||||
for (Ability ability : npcAbilities) {
|
||||
@@ -421,7 +441,20 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
|
||||
@Override
|
||||
public void unFocused(GuiNpcTextField textField) {
|
||||
if (textField.id == 101) {
|
||||
globalCooldown = textField.getInteger();
|
||||
minCooldown = textField.getInteger();
|
||||
// Ensure min <= max
|
||||
if (minCooldown > maxCooldown) {
|
||||
maxCooldown = minCooldown;
|
||||
initGui();
|
||||
}
|
||||
save();
|
||||
} else if (textField.id == 102) {
|
||||
maxCooldown = textField.getInteger();
|
||||
// Ensure min <= max
|
||||
if (maxCooldown < minCooldown) {
|
||||
minCooldown = maxCooldown;
|
||||
initGui();
|
||||
}
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,18 @@ import noppes.npcs.client.gui.util.SubGuiInterface;
|
||||
public class SubGuiAbilityLaserShot extends SubGuiAbilityConfig {
|
||||
|
||||
private final AbilityLaserShot laser;
|
||||
private int editingTypeColorId = 0;
|
||||
private int editingVisualColorId = 0;
|
||||
|
||||
public SubGuiAbilityLaserShot(AbilityLaserShot ability, IAbilityConfigCallback callback) {
|
||||
super(ability, callback);
|
||||
this.laser = ability;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasVisualSettings() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initTypeTab(int startY) {
|
||||
int y = startY;
|
||||
@@ -72,21 +77,40 @@ public class SubGuiAbilityLaserShot extends SubGuiAbilityConfig {
|
||||
|
||||
addLabel(new GuiNpcLabel(109, "ability.knockbackUp", col2LabelX, y + 5));
|
||||
addTextField(createFloatField(109, col2FieldX, y, 50, laser.getKnockbackUp()));
|
||||
}
|
||||
|
||||
y += 24;
|
||||
@Override
|
||||
protected void initVisualTab(int startY) {
|
||||
int y = startY;
|
||||
int labelX = guiLeft + 8;
|
||||
int fieldX = guiLeft + 85;
|
||||
int col2LabelX = guiLeft + 180;
|
||||
int col2FieldX = guiLeft + 260;
|
||||
|
||||
// Row 6: Inner Color + Outer Color
|
||||
addLabel(new GuiNpcLabel(120, "ability.innerColor", labelX, y + 5));
|
||||
// Row 1: Inner Color + Outer Color
|
||||
addLabel(new GuiNpcLabel(200, "ability.innerColor", labelX, y + 5));
|
||||
String innerHex = String.format("%06X", laser.getInnerColor() & 0xFFFFFF);
|
||||
GuiNpcButton innerColorBtn = new GuiNpcButton(120, fieldX, y, 50, 20, innerHex);
|
||||
GuiNpcButton innerColorBtn = new GuiNpcButton(200, fieldX, y, 55, 20, innerHex);
|
||||
innerColorBtn.setTextColor(laser.getInnerColor() & 0xFFFFFF);
|
||||
addButton(innerColorBtn);
|
||||
|
||||
addLabel(new GuiNpcLabel(121, "ability.outerColor", col2LabelX, y + 5));
|
||||
addLabel(new GuiNpcLabel(201, "ability.outerColor", col2LabelX, y + 5));
|
||||
String outerHex = String.format("%06X", laser.getOuterColor() & 0xFFFFFF);
|
||||
GuiNpcButton outerColorBtn = new GuiNpcButton(121, col2FieldX, y, 50, 20, outerHex);
|
||||
GuiNpcButton outerColorBtn = new GuiNpcButton(201, col2FieldX, y, 55, 20, outerHex);
|
||||
outerColorBtn.setTextColor(laser.getOuterColor() & 0xFFFFFF);
|
||||
outerColorBtn.setEnabled(laser.isOuterColorEnabled());
|
||||
addButton(outerColorBtn);
|
||||
|
||||
y += 24;
|
||||
|
||||
// Row 2: Outer Color Enabled + Outer Color Width
|
||||
addLabel(new GuiNpcLabel(202, "ability.outerEnabled", labelX, y + 5));
|
||||
addButton(new GuiNpcButton(202, fieldX, y, 50, 20, new String[]{"gui.no", "gui.yes"}, laser.isOuterColorEnabled() ? 1 : 0));
|
||||
|
||||
addLabel(new GuiNpcLabel(203, "ability.outerWidth", col2LabelX, y + 5));
|
||||
GuiNpcTextField widthField = createFloatField(203, col2FieldX, y, 55, laser.getOuterColorWidth());
|
||||
widthField.setEnabled(laser.isOuterColorEnabled());
|
||||
addTextField(widthField);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,34 +120,54 @@ public class SubGuiAbilityLaserShot extends SubGuiAbilityConfig {
|
||||
case 106:
|
||||
laser.setExplosive(value == 1);
|
||||
break;
|
||||
case 120:
|
||||
editingTypeColorId = 120;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleVisualButton(int id, GuiNpcButton button) {
|
||||
int value = button.getValue();
|
||||
switch (id) {
|
||||
case 200:
|
||||
editingVisualColorId = 200;
|
||||
setSubGui(new SubGuiColorSelector(laser.getInnerColor()));
|
||||
break;
|
||||
case 121:
|
||||
editingTypeColorId = 121;
|
||||
case 201:
|
||||
editingVisualColorId = 201;
|
||||
setSubGui(new SubGuiColorSelector(laser.getOuterColor()));
|
||||
break;
|
||||
case 202:
|
||||
laser.setOuterColorEnabled(value == 1);
|
||||
initGui();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subGuiClosed(SubGuiInterface subgui) {
|
||||
if (subgui instanceof SubGuiColorSelector && editingTypeColorId != 0) {
|
||||
if (subgui instanceof SubGuiColorSelector && editingVisualColorId != 0) {
|
||||
SubGuiColorSelector colorSelector = (SubGuiColorSelector) subgui;
|
||||
int rgb = colorSelector.color & 0x00FFFFFF;
|
||||
if (editingTypeColorId == 120) {
|
||||
if (editingVisualColorId == 200) {
|
||||
laser.setInnerColor(rgb);
|
||||
} else if (editingTypeColorId == 121) {
|
||||
} else if (editingVisualColorId == 201) {
|
||||
laser.setOuterColor(rgb);
|
||||
}
|
||||
editingTypeColorId = 0;
|
||||
editingVisualColorId = 0;
|
||||
initGui();
|
||||
} else {
|
||||
super.subGuiClosed(subgui);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleVisualTextField(int id, GuiNpcTextField field) {
|
||||
switch (id) {
|
||||
case 203:
|
||||
laser.setOuterColorWidth(parseFloat(field, laser.getOuterColorWidth()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTypeTextField(int id, GuiNpcTextField field) {
|
||||
switch (id) {
|
||||
|
||||
Reference in new issue
Block a user