diff --git a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityBeam.java b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityBeam.java
index deced23a0..8f87ff331 100644
--- a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityBeam.java
+++ b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityBeam.java
@@ -262,8 +262,8 @@ public class RenderAbilityBeam extends RenderEnergyAbility {
// Calculate alpha based on age (older = more faded)
float age1 = i < ages.size() ? ages.get(i) : 0;
float age2 = (i + 1) < ages.size() ? ages.get(i + 1) : 0;
- float fade1 = 1.0f - Math.min(1.0f, age1 / fadeTime);
- float fade2 = 1.0f - Math.min(1.0f, age2 / fadeTime);
+ float fade1 = fadeTime > 0 ? (1.0f - Math.min(1.0f, age1 / fadeTime)) : 0.0f;
+ float fade2 = fadeTime > 0 ? (1.0f - Math.min(1.0f, age2 / fadeTime)) : 0.0f;
float alpha1 = baseAlpha * fade1;
float alpha2 = baseAlpha * fade2;
diff --git a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityLaser.java b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityLaser.java
index d24bc1bc9..e38764bfb 100644
--- a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityLaser.java
+++ b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityLaser.java
@@ -8,6 +8,9 @@ import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
import org.lwjgl.opengl.GL11;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Renders the AbilityLaser entity as a rectangular beam with clear inner/outer colors.
*
@@ -16,6 +19,8 @@ import org.lwjgl.opengl.GL11;
@SideOnly(Side.CLIENT)
public class RenderAbilityLaser extends RenderEnergyAbility {
+ private final Map tipLightningStates = new HashMap<>();
+
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
EntityAbilityLaser laser = (EntityAbilityLaser) entity;
@@ -218,13 +223,16 @@ public class RenderAbilityLaser extends RenderEnergyAbility {
/**
* Get or create the tip lightning state for the laser.
- * Stored as a secondary object in lightningState2.
+ * Persisted per entity ID so arcs accumulate and fade properly across frames.
*/
private AttachedLightningRenderer.LightningState getTipLightningState(EntityAbilityLaser laser) {
- // Use a simple approach - store in a transient field
- // Since lightningState is already used, we'll create a new state each time
- // This works because we're updating and rendering immediately
- return new AttachedLightningRenderer.LightningState();
+ int id = laser.getEntityId();
+ AttachedLightningRenderer.LightningState state = tipLightningStates.get(id);
+ if (state == null) {
+ state = new AttachedLightningRenderer.LightningState();
+ tipLightningStates.put(id, state);
+ }
+ return state;
}
/**
diff --git a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilitySweeper.java b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilitySweeper.java
index 2dbeac3c0..b72be7a7c 100644
--- a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilitySweeper.java
+++ b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilitySweeper.java
@@ -70,6 +70,7 @@ public class RenderAbilitySweeper extends Render {
/**
* Render beam using the same style as the Beam entity trail.
* Three layers: outer glow, middle, inner core.
+ * outerColorWidth is an additive offset from inner width (consistent with other renderers).
*/
private void renderBeam(float length, float width, int innerColor, int outerColor,
boolean outerColorEnabled, float outerColorWidth) {
@@ -82,21 +83,28 @@ public class RenderAbilitySweeper extends Render {
float innerG = ((innerColor >> 8) & 0xFF) / 255.0f;
float innerB = (innerColor & 0xFF) / 255.0f;
+ // Inner scale defines the core width (consistent with other renderers)
+ float innerScale = 0.6f;
+ float innerWidth = width * innerScale;
+
// 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 - only if enabled
+ // Outer glow (wider, translucent) - only if enabled
+ // outerColorWidth is additive offset from inner width
if (outerColorEnabled) {
+ float outerWidth = innerWidth + outerColorWidth * width;
GL11.glDepthMask(false);
- renderBeamSegment(0, 0, 0, 0, 0, length, width * outerColorWidth, outerR, outerG, outerB, 0.3f);
+ renderBeamSegment(0, 0, 0, 0, 0, length, outerWidth, 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 (halfway between inner and outer)
+ float midWidth = innerWidth + (outerWidth - innerWidth) * 0.5f;
+ renderBeamSegment(0, 0, 0, 0, 0, length, midWidth, 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);
+ // Inner core (solid)
+ renderBeamSegment(0, 0, 0, 0, 0, length, innerWidth, innerR, innerG, innerB, 1.0f);
}
/**
diff --git a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityZone.java b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityZone.java
index ec21a268a..20d31c56d 100644
--- a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityZone.java
+++ b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityZone.java
@@ -210,7 +210,7 @@ public class RenderAbilityZone extends Render {
if (c.accents) {
float aa = 0.5f * c.brightness * (0.3f + 0.2f * sin(c.pulse * 0.7f));
- AccentStyle style = AccentStyle.values()[Math.min(c.accentStyle, AccentStyle.values().length - 1)];
+ AccentStyle style = AccentStyle.values()[Math.max(0, Math.min(c.accentStyle, AccentStyle.values().length - 1))];
switch (style) {
case SWAYING:
renderSwayingAccents(c, c.ir, c.ig, c.ib, aa, c.height);
@@ -314,7 +314,7 @@ public class RenderAbilityZone extends Render {
private void spawnDefaultParticles(Ctx c) {
int count = Math.round(3 * c.particleDensity);
- ParticleMotion motion = ParticleMotion.values()[Math.min(c.particleMotion, ParticleMotion.values().length - 1)];
+ ParticleMotion motion = ParticleMotion.values()[Math.max(0, Math.min(c.particleMotion, ParticleMotion.values().length - 1))];
for (int i = 0; i < count; i++) {
double[] pos = randomPosInZone(c);
double mx, my, mz;
@@ -350,7 +350,7 @@ public class RenderAbilityZone extends Render {
}
private double[] getMotionForStyle(int motionOrdinal) {
- ParticleMotion motion = ParticleMotion.values()[Math.min(motionOrdinal, ParticleMotion.values().length - 1)];
+ ParticleMotion motion = ParticleMotion.values()[Math.max(0, Math.min(motionOrdinal, ParticleMotion.values().length - 1))];
switch (motion) {
case DRIFTING:
return new double[]{
diff --git a/src/main/java/kamkeel/npcs/client/renderer/RenderEnergyDome.java b/src/main/java/kamkeel/npcs/client/renderer/RenderEnergyDome.java
index 8653bb27d..871d48461 100644
--- a/src/main/java/kamkeel/npcs/client/renderer/RenderEnergyDome.java
+++ b/src/main/java/kamkeel/npcs/client/renderer/RenderEnergyDome.java
@@ -42,7 +42,6 @@ public class RenderEnergyDome extends RenderEnergyBarrier {
float outerAlpha = dome.getOuterColorAlpha() * healthPercent;
GL11.glDepthMask(false);
renderSphere(dome.getOuterColor(), outerAlpha + flashAlpha, radius * outerScale, 8);
- GL11.glDepthMask(true);
}
// TODO: Skybox Feature
@@ -50,9 +49,9 @@ public class RenderEnergyDome extends RenderEnergyBarrier {
// renderSkyboxSphere(dome.getSkyboxTexture(), radius, radius * 0.94f, 32, x, y, z);
// }
- // Render inner dome
+ GL11.glDepthMask(true);
+ // Render inner dome (depth write enabled so inner sphere properly occludes)
renderSphere(dome.getInnerColor(), dome.getInnerAlpha(), radius * innerScale, 8);
-
GL11.glPopMatrix();
restoreRenderState();
diff --git a/src/main/java/kamkeel/npcs/client/renderer/lightning/AttachedLightningRenderer.java b/src/main/java/kamkeel/npcs/client/renderer/lightning/AttachedLightningRenderer.java
index 5e9e2342b..4debb90d6 100644
--- a/src/main/java/kamkeel/npcs/client/renderer/lightning/AttachedLightningRenderer.java
+++ b/src/main/java/kamkeel/npcs/client/renderer/lightning/AttachedLightningRenderer.java
@@ -3,7 +3,6 @@ package kamkeel.npcs.client.renderer.lightning;
import net.minecraft.client.renderer.Tessellator;
import org.lwjgl.opengl.GL11;
-import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -37,6 +36,7 @@ public class AttachedLightningRenderer {
public float getAlphaMultiplier() {
// Fade out over lifetime
+ if (maxAge <= 0) return 0.0f;
return 1.0f - ((float) age / (float) maxAge);
}
@@ -208,10 +208,9 @@ public class AttachedLightningRenderer {
private static void renderBoltPath(Tessellator tess, List points, int color, float width, float alpha) {
if (alpha <= 0.01f) return;
- Color c = new Color(color);
- int r = c.getRed();
- int g = c.getGreen();
- int b = c.getBlue();
+ int r = (color >> 16) & 0xFF;
+ int g = (color >> 8) & 0xFF;
+ int b = color & 0xFF;
int a = Math.min(255, Math.max(0, (int) (alpha * 255)));
for (int i = 0; i < points.size() - 1; i++) {
diff --git a/src/main/java/kamkeel/npcs/client/renderer/lightning/LightningBolt.java b/src/main/java/kamkeel/npcs/client/renderer/lightning/LightningBolt.java
index e077189ab..beea63fd3 100644
--- a/src/main/java/kamkeel/npcs/client/renderer/lightning/LightningBolt.java
+++ b/src/main/java/kamkeel/npcs/client/renderer/lightning/LightningBolt.java
@@ -56,7 +56,7 @@ public class LightningBolt {
this.numSegments0 = 1;
this.length = end.copy().subtract(start).mag();
- this.particleMaxAge = FADE_TIME + rand.nextInt(FADE_TIME) - FADE_TIME / 2;
+ this.particleMaxAge = Math.max(1, FADE_TIME + rand.nextInt(FADE_TIME) - FADE_TIME / 2);
this.particleAge = -(int) (length * speed);
segments.add(new Segment(start, end));
diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyBarrierData.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyBarrierData.java
index f31466329..ef9bcff57 100644
--- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyBarrierData.java
+++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyBarrierData.java
@@ -78,7 +78,7 @@ public class EnergyBarrierData {
}
public void setMaxHealth(float maxHealth) {
- this.maxHealth = Math.max(0, maxHealth);
+ this.maxHealth = Math.max(1.0f, maxHealth);
}
public boolean isUseHealth() {
diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyCombatData.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyCombatData.java
index 2ecbf7cc5..ad3d603e5 100644
--- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyCombatData.java
+++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyCombatData.java
@@ -75,7 +75,7 @@ public class EnergyCombatData implements IEnergyCombatData {
@Override
public void setExplosionRadius(float explosionRadius) {
- this.explosionRadius = explosionRadius;
+ this.explosionRadius = Math.max(0, explosionRadius);
}
@Override
@@ -98,12 +98,17 @@ public class EnergyCombatData implements IEnergyCombatData {
}
public void readNBT(NBTTagCompound nbt) {
- damage = nbt.getFloat("damage");
- knockback = nbt.getFloat("knockback");
- knockbackUp = nbt.getFloat("knockbackUp");
- explosive = nbt.getBoolean("explosive");
- explosionRadius = nbt.getFloat("explosionRadius");
- explosionDamageFalloff = nbt.getFloat("explosionDamageFalloff");
+ damage = nbt.hasKey("damage") ? nbt.getFloat("damage") : 7.0f;
+ knockback = nbt.hasKey("knockback") ? nbt.getFloat("knockback") : 1.0f;
+ knockbackUp = nbt.hasKey("knockbackUp") ? nbt.getFloat("knockbackUp") : 0.1f;
+ explosive = nbt.hasKey("explosive") && nbt.getBoolean("explosive");
+ explosionRadius = nbt.hasKey("explosionRadius") ? nbt.getFloat("explosionRadius") : 3.0f;
+ explosionDamageFalloff = nbt.hasKey("explosionDamageFalloff") ? nbt.getFloat("explosionDamageFalloff") : 0.5f;
+
+ // Sanitize
+ if (Float.isNaN(damage) || Float.isInfinite(damage)) damage = 7.0f;
+ if (Float.isNaN(knockback) || Float.isInfinite(knockback) || knockback < 0) knockback = 1.0f;
+ if (Float.isNaN(explosionRadius) || Float.isInfinite(explosionRadius) || explosionRadius < 0) explosionRadius = 3.0f;
}
public EnergyCombatData copy() {
diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyDisplayData.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyDisplayData.java
index 3b0998602..6363a69cb 100644
--- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyDisplayData.java
+++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyDisplayData.java
@@ -128,13 +128,13 @@ public class EnergyDisplayData implements IEnergyDisplayData {
}
public void readNBT(NBTTagCompound nbt) {
- innerColor = nbt.getInteger("innerColor");
- outerColor = nbt.getInteger("outerColor");
- outerColorEnabled = nbt.getBoolean("outerColorEnabled");
- outerColorWidth = nbt.getFloat("outerColorWidth");
- outerColorAlpha = nbt.getFloat("outerColorAlpha");
+ innerColor = nbt.hasKey("innerColor") ? nbt.getInteger("innerColor") : 0xFFFFFF;
+ outerColor = nbt.hasKey("outerColor") ? nbt.getInteger("outerColor") : 0x8888FF;
+ outerColorEnabled = !nbt.hasKey("outerColorEnabled") || nbt.getBoolean("outerColorEnabled");
+ outerColorWidth = nbt.hasKey("outerColorWidth") ? nbt.getFloat("outerColorWidth") : 0.4f;
+ outerColorAlpha = nbt.hasKey("outerColorAlpha") ? nbt.getFloat("outerColorAlpha") : 0.5f;
innerAlpha = nbt.hasKey("innerAlpha") ? nbt.getFloat("innerAlpha") : 1.0f;
- rotationSpeed = nbt.getFloat("rotationSpeed");
+ rotationSpeed = nbt.hasKey("rotationSpeed") ? nbt.getFloat("rotationSpeed") : 4.0f;
// TODO: Skybox Feature
// skyboxEnabled = nbt.hasKey("skyboxEnabled") && nbt.getBoolean("skyboxEnabled");
// skyboxTexture = nbt.hasKey("skyboxTexture") ? nbt.getString("skyboxTexture") : "";
diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyHomingData.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyHomingData.java
index a03e70525..4f75c0841 100644
--- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyHomingData.java
+++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyHomingData.java
@@ -31,7 +31,7 @@ public class EnergyHomingData implements IEnergyHomingData {
@Override
public void setSpeed(float speed) {
- this.speed = speed;
+ this.speed = Math.max(0.01f, speed);
}
@Override
@@ -72,10 +72,13 @@ public class EnergyHomingData implements IEnergyHomingData {
}
public void readNBT(NBTTagCompound nbt) {
- speed = nbt.getFloat("speed");
- homing = nbt.getBoolean("homing");
- homingStrength = nbt.getFloat("homingStrength");
- homingRange = nbt.getFloat("homingRange");
+ speed = nbt.hasKey("speed") ? nbt.getFloat("speed") : 0.5f;
+ homing = !nbt.hasKey("homing") || nbt.getBoolean("homing");
+ homingStrength = nbt.hasKey("homingStrength") ? nbt.getFloat("homingStrength") : 0.15f;
+ homingRange = nbt.hasKey("homingRange") ? nbt.getFloat("homingRange") : 20.0f;
+
+ // Sanitize speed to prevent stuck projectiles
+ if (Float.isNaN(speed) || Float.isInfinite(speed) || speed <= 0) speed = 0.5f;
}
public EnergyHomingData copy() {
diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyLifespanData.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyLifespanData.java
index a0e6835f6..2c8d1f6a1 100644
--- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyLifespanData.java
+++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyLifespanData.java
@@ -26,7 +26,7 @@ public class EnergyLifespanData implements IEnergyLifespanData {
@Override
public void setMaxDistance(float maxDistance) {
- this.maxDistance = maxDistance;
+ this.maxDistance = Float.isNaN(maxDistance) || maxDistance <= 0 ? 150.0f : maxDistance;
}
@Override
@@ -36,7 +36,7 @@ public class EnergyLifespanData implements IEnergyLifespanData {
@Override
public void setMaxLifetime(int maxLifetime) {
- this.maxLifetime = maxLifetime;
+ this.maxLifetime = maxLifetime <= 0 ? 200 : maxLifetime;
}
public void writeNBT(NBTTagCompound nbt) {
diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyLightningData.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyLightningData.java
index 1eb2b001a..b8423e7aa 100644
--- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyLightningData.java
+++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyLightningData.java
@@ -61,7 +61,7 @@ public class EnergyLightningData implements IEnergyLightningData {
@Override
public void setLightningFadeTime(int lightningFadeTime) {
- this.lightningFadeTime = lightningFadeTime;
+ this.lightningFadeTime = Math.max(1, lightningFadeTime);
}
public void writeNBT(NBTTagCompound nbt) {
@@ -72,10 +72,11 @@ public class EnergyLightningData implements IEnergyLightningData {
}
public void readNBT(NBTTagCompound nbt) {
- lightningEffect = nbt.getBoolean("lightningEffect");
- lightningDensity = nbt.getFloat("lightningDensity");
- lightningRadius = nbt.getFloat("lightningRadius");
- lightningFadeTime = nbt.getInteger("lightningFadeTime");
+ lightningEffect = nbt.hasKey("lightningEffect") && nbt.getBoolean("lightningEffect");
+ lightningDensity = nbt.hasKey("lightningDensity") ? nbt.getFloat("lightningDensity") : 0.15f;
+ lightningRadius = nbt.hasKey("lightningRadius") ? nbt.getFloat("lightningRadius") : 0.5f;
+ lightningFadeTime = nbt.hasKey("lightningFadeTime") ? nbt.getInteger("lightningFadeTime") : 6;
+ if (lightningFadeTime <= 0) lightningFadeTime = 1;
}
public EnergyLightningData copy() {
diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyTrajectoryData.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyTrajectoryData.java
index ea7735e11..10014fb5f 100644
--- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyTrajectoryData.java
+++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/EnergyTrajectoryData.java
@@ -187,6 +187,7 @@ public class EnergyTrajectoryData implements IEnergyTrajectoryData {
public void readNBT(NBTTagCompound nbt) {
this.currentPath = nbt.getInteger("currentPath");
+ trajectory.clear();
int i = 0;
while (nbt.hasKey("Path_" + i, Constants.NBT.TAG_COMPOUND)) {
diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java
index 1f6b8ba0c..d16fc7709 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java
@@ -118,6 +118,8 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
this.discRadius = 0.01f;
this.discThickness = 0.01f;
this.size = 0.01f;
+ this.renderCurrentSize = 0.01f;
+ this.prevRenderSize = 0.01f;
// Initial position at anchor point
Vec3 pos = AnchorPointHelper.calculateAnchorPosition(owner, anchorData);
diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java
index 08aeb24d7..38808cad9 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java
@@ -466,7 +466,8 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
*/
public float getLingerAlpha() {
if (!fullyExtended) return 1.0f;
- return 1.0f - ((float) ticksSinceFullExtension / lingerTicks);
+ if (lingerTicks <= 0) return 0.0f;
+ return Math.max(0.0f, 1.0f - ((float) ticksSinceFullExtension / lingerTicks));
}
// ==================== NBT ====================
@@ -475,8 +476,12 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
protected void readProjectileNBT(NBTTagCompound nbt) {
this.laserWidth = sanitize(nbt.hasKey("LaserWidth") ? nbt.getFloat("LaserWidth") : 0.2f, 0.2f, MAX_ENTITY_SIZE);
this.expansionSpeed = nbt.hasKey("ExpansionSpeed") ? nbt.getFloat("ExpansionSpeed") : 2.0f;
+ if (Float.isNaN(expansionSpeed) || Float.isInfinite(expansionSpeed) || expansionSpeed <= 0) expansionSpeed = 2.0f;
this.lingerTicks = nbt.hasKey("LingerTicks") ? nbt.getInteger("LingerTicks") : 10;
+ if (lingerTicks <= 0) lingerTicks = 1;
this.dieOnImpact = nbt.getBoolean("DieOnImpact");
+ this.lockVerticalDirection = nbt.getBoolean("LockVerticalDir");
+ this.ticksSinceFullExtension = nbt.getInteger("TicksSinceExtended");
this.dirX = nbt.getDouble("DirX");
this.dirY = nbt.getDouble("DirY");
this.dirZ = nbt.getDouble("DirZ");
@@ -495,6 +500,8 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
nbt.setFloat("ExpansionSpeed", expansionSpeed);
nbt.setInteger("LingerTicks", lingerTicks);
nbt.setBoolean("DieOnImpact", dieOnImpact);
+ nbt.setBoolean("LockVerticalDir", lockVerticalDirection);
+ nbt.setInteger("TicksSinceExtended", ticksSinceFullExtension);
nbt.setDouble("DirX", dirX);
nbt.setDouble("DirY", dirY);
nbt.setDouble("DirZ", dirZ);
diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilitySweeper.java b/src/main/java/kamkeel/npcs/entity/EntityAbilitySweeper.java
index af4616654..fd739876d 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityAbilitySweeper.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityAbilitySweeper.java
@@ -12,6 +12,9 @@ import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import noppes.npcs.LogWriter;
import noppes.npcs.NpcDamageSource;
+import noppes.npcs.controllers.PartyController;
+import noppes.npcs.controllers.data.Party;
+import noppes.npcs.controllers.data.PlayerData;
import noppes.npcs.entity.EntityNPCInterface;
import java.util.HashSet;
@@ -160,9 +163,7 @@ public class EntityAbilitySweeper extends EntityEnergyAbility {
if (currentAngle >= 360) {
currentAngle -= 360;
completedRotations++;
- if (!worldObj.isRemote) {
- LogWriter.info("[Sweeper] Completed rotation " + completedRotations + "/" + numberOfRotations);
- }
+ // Rotation completed
}
// Server handles damage
@@ -216,6 +217,7 @@ public class EntityAbilitySweeper extends EntityEnergyAbility {
if (!(entity instanceof EntityLivingBase)) continue;
if (entity == owner) continue;
if (hitThisTick.contains(entity.getEntityId())) continue;
+ if (shouldIgnoreEntity((EntityLivingBase) entity, owner)) continue;
EntityLivingBase livingEntity = (EntityLivingBase) entity;
@@ -264,6 +266,32 @@ public class EntityAbilitySweeper extends EntityEnergyAbility {
return entityFeetY < beamTopY;
}
+ /**
+ * Check if an entity should be ignored for sweep damage.
+ * Same logic as EntityEnergyProjectile.shouldIgnoreEntity but adapted for sweeper.
+ */
+ private boolean shouldIgnoreEntity(EntityLivingBase entity, Entity owner) {
+ if (entity instanceof EntityNPCInterface) {
+ EntityNPCInterface targetNpc = (EntityNPCInterface) entity;
+ if (targetNpc.faction.isPassive) return true;
+ if (owner instanceof EntityNPCInterface) {
+ if (((EntityNPCInterface) owner).faction.id == targetNpc.faction.id) return true;
+ }
+ if (owner instanceof EntityPlayer) {
+ if (targetNpc.faction.isFriendlyToPlayer((EntityPlayer) owner)) return true;
+ }
+ }
+ if (owner instanceof EntityPlayer && entity instanceof EntityPlayer) {
+ PlayerData ownerData = PlayerData.get((EntityPlayer) owner);
+ PlayerData targetData = PlayerData.get((EntityPlayer) entity);
+ if (ownerData.partyUUID != null && ownerData.partyUUID.equals(targetData.partyUUID)) {
+ Party party = PartyController.Instance().getParty(ownerData.partyUUID);
+ if (party != null && !party.friendlyFire()) return true;
+ }
+ }
+ return false;
+ }
+
private void applyDamage(EntityLivingBase target, Entity owner) {
if (owner instanceof EntityNPCInterface) {
target.attackEntityFrom(new NpcDamageSource("npc_ability", (EntityNPCInterface) owner), damage);
@@ -339,17 +367,23 @@ public class EntityAbilitySweeper extends EntityEnergyAbility {
this.beamLength = sanitize(nbt.getFloat("BeamLength"), 10.0f, MAX_ENTITY_SIZE);
this.beamWidth = sanitize(nbt.getFloat("BeamWidth"), 0.3f, MAX_ENTITY_SIZE);
this.beamHeight = sanitize(nbt.getFloat("BeamHeight"), 0.8f, MAX_ENTITY_SIZE);
- this.sweepSpeed = nbt.getFloat("SweepSpeed");
- this.numberOfRotations = nbt.getInteger("NumRotations");
+ this.sweepSpeed = nbt.hasKey("SweepSpeed") ? nbt.getFloat("SweepSpeed") : 3.0f;
+ if (Float.isNaN(sweepSpeed) || Float.isInfinite(sweepSpeed) || sweepSpeed <= 0) sweepSpeed = 3.0f;
+ this.numberOfRotations = nbt.hasKey("NumRotations") ? nbt.getInteger("NumRotations") : 2;
+ if (numberOfRotations <= 0) numberOfRotations = 1;
this.completedRotations = nbt.getInteger("CompletedRotations");
- this.maxTicks = nbt.getInteger("MaxTicks");
+ this.maxTicks = nbt.hasKey("MaxTicks") ? nbt.getInteger("MaxTicks") : 400;
+ if (maxTicks <= 0) maxTicks = 400;
this.targetEntityId = nbt.getInteger("TargetId");
this.currentAngle = nbt.getFloat("CurrentAngle");
+ this.prevAngle = currentAngle;
this.baseYaw = nbt.getFloat("BaseYaw");
this.deathWorldTime = nbt.getLong("DeathWorldTime");
- this.damage = nbt.getFloat("Damage");
- this.damageInterval = nbt.getInteger("DamageInterval");
- this.piercing = nbt.getBoolean("Piercing");
+ this.damage = nbt.hasKey("Damage") ? nbt.getFloat("Damage") : 5.0f;
+ if (Float.isNaN(damage) || Float.isInfinite(damage)) damage = 5.0f;
+ this.damageInterval = nbt.hasKey("DamageInterval") ? nbt.getInteger("DamageInterval") : 5;
+ if (damageInterval <= 0) damageInterval = 1;
+ this.piercing = !nbt.hasKey("Piercing") || nbt.getBoolean("Piercing");
this.lockOnTarget = nbt.getBoolean("LockOnTarget");
}
diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java
index 57445f8fc..a3aa494b4 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java
@@ -716,7 +716,8 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
- this.zoneType = ZoneType.values()[nbt.getInteger("ZoneType")];
+ int zoneTypeOrd = nbt.getInteger("ZoneType");
+ this.zoneType = (zoneTypeOrd >= 0 && zoneTypeOrd < ZoneType.values().length) ? ZoneType.values()[zoneTypeOrd] : ZoneType.TRAP;
try {
this.shape = ZoneShape.valueOf(nbt.getString("ShapeName"));
} catch (Exception e) {
@@ -724,17 +725,17 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
}
this.ownerEntityId = nbt.getInteger("OwnerId");
- this.radius = nbt.getFloat("Radius");
- this.durationTicks = nbt.getInteger("Duration");
- this.maxTicks = nbt.getInteger("MaxTicks");
- this.innerColor = nbt.getInteger("InnerColor");
- this.outerColor = nbt.getInteger("OuterColor");
- this.outerColorEnabled = nbt.getBoolean("OuterColorEnabled");
- this.zoneHeight = nbt.getFloat("ZoneHeight");
- this.particleDensity = nbt.getFloat("ParticleDensity");
- this.particleScale = nbt.getFloat("ParticleScale");
- this.animSpeed = nbt.getFloat("AnimSpeed");
- this.lightningDensity = nbt.getFloat("LightningDensity");
+ this.radius = nbt.hasKey("Radius") ? nbt.getFloat("Radius") : 4.0f;
+ this.durationTicks = nbt.hasKey("Duration") ? nbt.getInteger("Duration") : 200;
+ this.maxTicks = nbt.hasKey("MaxTicks") ? nbt.getInteger("MaxTicks") : 200;
+ this.innerColor = nbt.hasKey("InnerColor") ? nbt.getInteger("InnerColor") : 0xFF6600;
+ this.outerColor = nbt.hasKey("OuterColor") ? nbt.getInteger("OuterColor") : 0xFF0000;
+ this.outerColorEnabled = !nbt.hasKey("OuterColorEnabled") || nbt.getBoolean("OuterColorEnabled");
+ this.zoneHeight = nbt.hasKey("ZoneHeight") ? nbt.getFloat("ZoneHeight") : 2.0f;
+ this.particleDensity = nbt.hasKey("ParticleDensity") ? nbt.getFloat("ParticleDensity") : 1.0f;
+ this.particleScale = nbt.hasKey("ParticleScale") ? nbt.getFloat("ParticleScale") : 1.0f;
+ this.animSpeed = nbt.hasKey("AnimSpeed") ? nbt.getFloat("AnimSpeed") : 1.0f;
+ this.lightningDensity = nbt.hasKey("LightningDensity") ? nbt.getFloat("LightningDensity") : 1.0f;
this.deathWorldTime = nbt.getLong("DeathWorldTime");
// Visual layer fields
@@ -764,8 +765,9 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
this.visible = nbt.getBoolean("Visible");
// Hazard
- this.damagePerSecond = nbt.getFloat("DamagePerSecond");
- this.damageInterval = nbt.getInteger("DamageInterval");
+ this.damagePerSecond = nbt.hasKey("DamagePerSecond") ? nbt.getFloat("DamagePerSecond") : 1.0f;
+ this.damageInterval = nbt.hasKey("DamageInterval") ? nbt.getInteger("DamageInterval") : 20;
+ if (damageInterval <= 0) damageInterval = 1;
this.ignoreInvulnFrames = nbt.getBoolean("IgnoreInvuln");
this.affectsCaster = nbt.getBoolean("AffectsCaster");
@@ -893,7 +895,8 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
@Override
public void readSpawnData(ByteBuf buffer) {
- this.zoneType = ZoneType.values()[buffer.readInt()];
+ int zoneTypeOrd = buffer.readInt();
+ this.zoneType = (zoneTypeOrd >= 0 && zoneTypeOrd < ZoneType.values().length) ? ZoneType.values()[zoneTypeOrd] : ZoneType.TRAP;
String shapeName = ByteBufUtils.readUTF8String(buffer);
try {
this.shape = ZoneShape.valueOf(shapeName);
diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java
index afad7be9b..af6766b15 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java
@@ -279,6 +279,7 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
readEnergyBaseNBT(nbt);
this.ticksAlive = nbt.getInteger("TicksAlive");
this.currentHealth = nbt.getFloat("CurrentHealth");
+ if (Float.isNaN(currentHealth) || Float.isInfinite(currentHealth) || currentHealth < 0) currentHealth = barrierData.maxHealth;
this.charging = nbt.hasKey("Charging") && nbt.getBoolean("Charging");
this.chargeTick = nbt.getInteger("ChargeTick");
this.chargeDuration = nbt.getInteger("ChargeDuration");
diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java
index 9b0c7cb27..782413435 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java
@@ -9,6 +9,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import java.util.List;
@@ -28,20 +29,20 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
public EntityEnergyDome(World world) {
super(world);
- this.setSize(1.0f, 1.0f); // Hitbox doesn't really matter, we use radius checks
+ this.setSize(1.0f, 1.0f);
}
public EntityEnergyDome(World world, EntityLivingBase owner, double x, double y, double z,
float domeRadius, EnergyDisplayData display, EnergyLightningData lightning,
EnergyBarrierData barrier) {
this(world);
- this.setPosition(x, y, z);
this.ownerEntityId = owner.getEntityId();
this.domeRadius = domeRadius;
this.displayData = display;
this.lightningData = lightning;
this.barrierData = barrier;
this.currentHealth = barrier.maxHealth;
+ this.setPosition(x, y, z);
}
@Override
@@ -103,14 +104,25 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
double dx = projectile.posX - this.posX;
double dy = projectile.posY - this.posY;
double dz = projectile.posZ - this.posZ;
- double distSq = dx * dx + dy * dy + dz * dz;
+ double dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
- // Check if projectile is within dome radius
- if (distSq > domeRadius * domeRadius) return false;
+ // Check if projectile is outside dome radius
+ if (dist > domeRadius) return false;
- // Check if projectile is moving inward (dot product of velocity and position relative to center)
+ // Approximate the projectile's position before last tick's movement.
+ // checkBarrierCollision runs BEFORE updateProjectile, so posX is post-last-movement.
+ // posX - motionX gives the pre-movement position (works for moveEntity and setPosition).
+ double prevDx = (projectile.posX - projectile.motionX) - this.posX;
+ double prevDy = (projectile.posY - projectile.motionY) - this.posY;
+ double prevDz = (projectile.posZ - projectile.motionZ) - this.posZ;
+ double prevDist = Math.sqrt(prevDx * prevDx + prevDy * prevDy + prevDz * prevDz);
+
+ // Only block projectiles that crossed the dome boundary from outside.
+ // Projectiles spawned inside (prevDist < radius) pass through freely.
+ if (prevDist < domeRadius) return false;
+
+ // Projectile entered from outside — check it's still moving inward
double dot = dx * projectile.motionX + dy * projectile.motionY + dz * projectile.motionZ;
- // Negative dot = moving toward center = incoming
return dot < 0;
}
@@ -172,17 +184,37 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
}
}
+ // ==================== MELEE (spherical check) ====================
+
+ /**
+ * Reject melee hits that land on the cubic bounding box but are outside the actual sphere.
+ */
+ @Override
+ public boolean attackEntityFrom(DamageSource source, float amount) {
+ if (source.getEntity() != null) {
+ Entity attacker = source.getEntity();
+ double dx = attacker.posX - this.posX;
+ double dy = (attacker.posY + attacker.height * 0.5) - this.posY;
+ double dz = attacker.posZ - this.posZ;
+ double distSq = dx * dx + dy * dy + dz * dz;
+ // Allow hit only if attacker is within sphere + melee reach margin
+ double maxDist = domeRadius + 5.0;
+ if (distSq > maxDist * maxDist) return false;
+ }
+ return super.attackEntityFrom(source, amount);
+ }
+
// ==================== BOUNDING BOX ====================
+ /**
+ * Expands the ray-trace targeting area so players can melee-hit the dome
+ * from anywhere near its surface, not just the tiny 1x1 center.
+ * This is used by EntityRenderer.getMouseOver() to expand the entity BB
+ * for crosshair targeting without modifying the actual bounding box.
+ */
@Override
- public AxisAlignedBB getBoundingBox() {
- if (barrierData.meleeEnabled) {
- return AxisAlignedBB.getBoundingBox(
- posX - domeRadius, posY - domeRadius, posZ - domeRadius,
- posX + domeRadius, posY + domeRadius, posZ + domeRadius
- );
- }
- return null;
+ public float getCollisionBorderSize() {
+ return domeRadius;
}
@Override
@@ -193,6 +225,27 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
return distance < d * d;
}
+ // ==================== DISTANCE (for render sorting) ====================
+
+ /**
+ * Returns squared distance from the given point to the nearest point on the dome sphere surface.
+ * Uses absolute distance to surface: |centerDist - radius|.
+ *
+ * Inside: nearest surface is (radius - centerDist) away.
+ * Outside: nearest surface is (centerDist - radius) away.
+ * This ensures entities inside the dome sort correctly relative to the dome shell.
+ */
+ @Override
+ public double getDistanceSq(double x, double y, double z) {
+ double dx = this.posX - x;
+ double dy = this.posY - y;
+ double dz = this.posZ - z;
+ double centerDist = Math.sqrt(dx * dx + dy * dy + dz * dz);
+
+ double surfaceDist = Math.abs(centerDist - domeRadius);
+ return surfaceDist * surfaceDist;
+ }
+
// ==================== GETTERS ====================
public float getDomeRadius() {
diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java
index 7fd617afb..d3665e319 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java
@@ -167,6 +167,8 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
target.attackEntityFrom(new NpcDamageSource("npc_ability", (EntityNPCInterface) owner), panelData.launchDamage);
} else if (owner instanceof EntityPlayer) {
target.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) owner), panelData.launchDamage);
+ } else {
+ target.attackEntityFrom(DamageSource.generic, panelData.launchDamage);
}
}
@@ -188,6 +190,7 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
// Max lifetime check
if (ticksAlive > 200) {
+ onBarrierDestroyed();
this.setDead();
}
}
@@ -341,6 +344,47 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
return distance < d * d;
}
+ // ==================== DISTANCE (for render sorting) ====================
+
+ /**
+ * Returns squared distance from the given point to the closest point on the panel surface.
+ * Accounts for panel rotation (panelYaw), width, height, and thickness.
+ * This ensures correct transparency render ordering when the camera is near or behind the panel.
+ */
+ @Override
+ public double getDistanceSq(double x, double y, double z) {
+ double dx = x - this.posX;
+ double dy = y - this.posY;
+ double dz = z - this.posZ;
+
+ float yawRad = (float) Math.toRadians(panelYaw);
+ double cos = Math.cos(yawRad);
+ double sin = Math.sin(yawRad);
+
+ // Transform to panel-local space
+ // Panel normal (forward): (-sin, 0, cos)
+ // Panel right (width axis): (cos, 0, sin)
+ double localForward = dx * (-sin) + dz * cos;
+ double localRight = dx * cos + dz * sin;
+ double localUp = dy;
+
+ float halfW = panelData.panelWidth * 0.5f;
+ float halfH = panelData.panelHeight * 0.5f;
+ float halfThickness = 0.25f;
+
+ // Clamp to panel bounds to find closest point on the panel volume
+ double clampedForward = Math.max(-halfThickness, Math.min(halfThickness, localForward));
+ double clampedRight = Math.max(-halfW, Math.min(halfW, localRight));
+ double clampedUp = Math.max(-halfH, Math.min(halfH, localUp));
+
+ // Distance from point to closest point on panel
+ double df = localForward - clampedForward;
+ double dr = localRight - clampedRight;
+ double du = localUp - clampedUp;
+
+ return df * df + dr * dr + du * du;
+ }
+
// ==================== GETTERS ====================
public PanelMode getMode() {
@@ -365,8 +409,8 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
this.mode = (modeOrdinal >= 0 && modeOrdinal < PanelMode.values().length)
? PanelMode.values()[modeOrdinal] : PanelMode.PLACED;
panelData.readNBT(nbt);
- panelData.panelWidth = Math.min(panelData.panelWidth, MAX_ENTITY_SIZE);
- panelData.panelHeight = Math.min(panelData.panelHeight, MAX_ENTITY_SIZE);
+ panelData.panelWidth = sanitize(panelData.panelWidth, 3.0f, MAX_ENTITY_SIZE);
+ panelData.panelHeight = sanitize(panelData.panelHeight, 3.0f, MAX_ENTITY_SIZE);
this.targetPanelWidth = sanitize(nbt.hasKey("TargetPanelWidth") ? nbt.getFloat("TargetPanelWidth") : panelData.panelWidth, 3.0f, MAX_ENTITY_SIZE);
this.targetPanelHeight = sanitize(nbt.hasKey("TargetPanelHeight") ? nbt.getFloat("TargetPanelHeight") : panelData.panelHeight, 3.0f, MAX_ENTITY_SIZE);
}
diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java
index 7ec72f783..4f0c16e50 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java
@@ -318,8 +318,8 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
* @return true if projectile was absorbed (caller should stop processing)
*/
protected boolean checkBarrierCollision() {
- // Search area for barriers - must cover large domes (up to 30 radius)
- double searchRange = 32.0;
+ // Search range must cover the largest possible barrier (dome radius up to MAX_ENTITY_RADIUS)
+ double searchRange = MAX_ENTITY_RADIUS + 5;
AxisAlignedBB searchBox = AxisAlignedBB.getBoundingBox(
posX - searchRange, posY - searchRange, posZ - searchRange,
posX + searchRange, posY + searchRange, posZ + searchRange
@@ -458,13 +458,15 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
protected void doExplosion() {
if (previewMode) return; // Skip explosion in preview mode
+ float explosionRad = getExplosionRadius();
+ if (Float.isNaN(explosionRad) || explosionRad <= 0) return;
worldObj.playSoundEffect(posX, posY, posZ, "random.explode", 1.0f, 1.0f);
Entity owner = getOwnerEntity();
AxisAlignedBB explosionBox = AxisAlignedBB.getBoundingBox(
- posX - getExplosionRadius(), posY - getExplosionRadius(), posZ - getExplosionRadius(),
- posX + getExplosionRadius(), posY + getExplosionRadius(), posZ + getExplosionRadius()
+ posX - explosionRad, posY - explosionRad, posZ - explosionRad,
+ posX + explosionRad, posY + explosionRad, posZ + explosionRad
);
@SuppressWarnings("unchecked")
@@ -479,9 +481,8 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
Math.pow(target.posZ - posZ, 2)
);
- float radius = getExplosionRadius();
- if (radius > 0 && dist <= radius) {
- float falloff = 1.0f - (float) (dist / radius) * getExplosionDamageFalloff();
+ if (dist <= explosionRad) {
+ float falloff = 1.0f - (float) (dist / explosionRad) * getExplosionDamageFalloff();
applyDamage(target, getDamage() * falloff, getKnockback() * falloff);
}
}
@@ -966,14 +967,12 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
nbt.setFloat("Size", size);
- // Write effects list
- if (!effects.isEmpty()) {
- NBTTagList effectsList = new NBTTagList();
- for (AbilityPotionEffect effect : effects) {
- effectsList.appendTag(effect.writeNBT());
- }
- nbt.setTag("Effects", effectsList);
+ // Write effects list (always write, even if empty, so readBaseNBT finds the key)
+ NBTTagList effectsList = new NBTTagList();
+ for (AbilityPotionEffect effect : effects) {
+ effectsList.appendTag(effect.writeNBT());
}
+ nbt.setTag("Effects", effectsList);
nbt.setLong("DeathWorldTime", deathWorldTime);
diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergySlicer.java b/src/main/java/kamkeel/npcs/entity/EntityEnergySlicer.java
index c24a5ca66..29d0ac39a 100644
--- a/src/main/java/kamkeel/npcs/entity/EntityEnergySlicer.java
+++ b/src/main/java/kamkeel/npcs/entity/EntityEnergySlicer.java
@@ -331,8 +331,8 @@ public class EntityEnergySlicer extends EntityEnergyProjectile {
this.sliceWidth = sanitize(nbt.hasKey("SliceWidth") ? nbt.getFloat("SliceWidth") : 3.0f, 3.0f, MAX_ENTITY_SIZE);
this.sliceThickness = sanitize(nbt.hasKey("SliceThickness") ? nbt.getFloat("SliceThickness") : 0.15f, 0.15f, MAX_ENTITY_SIZE);
this.piercing = !nbt.hasKey("Piercing") || nbt.getBoolean("Piercing");
- this.targetSliceWidth = nbt.hasKey("TargetSliceWidth") ? nbt.getFloat("TargetSliceWidth") : sliceWidth;
- this.targetSliceThickness = nbt.hasKey("TargetSliceThickness") ? nbt.getFloat("TargetSliceThickness") : sliceThickness;
+ this.targetSliceWidth = sanitize(nbt.hasKey("TargetSliceWidth") ? nbt.getFloat("TargetSliceWidth") : sliceWidth, sliceWidth, MAX_ENTITY_SIZE);
+ this.targetSliceThickness = sanitize(nbt.hasKey("TargetSliceThickness") ? nbt.getFloat("TargetSliceThickness") : sliceThickness, sliceThickness, MAX_ENTITY_SIZE);
}
@Override