Create Barrier Abilities + Heavy Refactoring

This commit is contained in:
Kam committed 2026-02-17 14:32:13 -05:00
1 parent d1f2c9695c
commit c07e9e7006
53 files changed
+1890 -1460

No files matched your search

+1 -1
Submodule src/api updated: efa8721d36...3d648425fa.
@@ -2,7 +2,6 @@ package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
import kamkeel.npcs.entity.EntityAbilityBeam;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
@@ -24,7 +23,7 @@ import java.util.List;
* Design inspired by LouisXIV's energy rendering system.
*/
@SideOnly(Side.CLIENT)
public class RenderAbilityBeam extends RenderAbilityProjectile {
public class RenderAbilityBeam extends RenderEnergyAbility {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
@@ -117,7 +116,7 @@ public class RenderAbilityBeam extends RenderAbilityProjectile {
// Render lightning during charging if enabled
if (beam.hasLightningEffect()) {
renderAttachedLightning(beam, scale);
renderAttachedLightning(beam, 0.6f * 0.5f, scale);
}
GL11.glPushMatrix();
@@ -545,7 +544,7 @@ public class RenderAbilityBeam extends RenderAbilityProjectile {
// Render lightning BEFORE rotation so it crackles in all directions (only on head)
if (beam.hasLightningEffect()) {
renderAttachedLightning(beam, scale);
renderAttachedLightning(beam, 0.6f * 0.5f, scale);
}
// Rotation
@@ -579,31 +578,4 @@ public class RenderAbilityBeam extends RenderAbilityProjectile {
GL11.glPopMatrix();
}
/**
* Render fading lightning arcs attached to the beam head (in local space).
*/
private void renderAttachedLightning(EntityAbilityBeam beam, float headScale) {
AttachedLightningRenderer.LightningState state = getLightningState(beam);
float density = beam.getLightningDensity();
// Lightning radius extends outward from inner surface (innerScale = 0.6)
float innerRadius = 0.6f * headScale * 0.5f;
float radius = innerRadius + beam.getLightningRadius() * headScale;
int outerColor = beam.getOuterColor();
int innerColor = beam.getInnerColor();
int fadeTime = beam.getLightningFadeTime();
state.update(density, radius, outerColor, innerColor, fadeTime);
state.render();
}
/**
* Get or create the lightning state for an entity.
*/
private AttachedLightningRenderer.LightningState getLightningState(EntityAbilityBeam beam) {
if (beam.lightningState == null) {
beam.lightningState = new AttachedLightningRenderer.LightningState();
}
return (AttachedLightningRenderer.LightningState) beam.lightningState;
}
}
@@ -2,7 +2,6 @@ package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
import kamkeel.npcs.entity.EntityAbilityDisc;
import net.minecraft.entity.Entity;
import org.lwjgl.opengl.GL11;
@@ -14,7 +13,7 @@ import org.lwjgl.opengl.GL11;
* Design inspired by LouisXIV's energy rendering system.
*/
@SideOnly(Side.CLIENT)
public class RenderAbilityDisc extends RenderAbilityProjectile {
public class RenderAbilityDisc extends RenderEnergyAbility {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
@@ -37,7 +36,7 @@ public class RenderAbilityDisc extends RenderAbilityProjectile {
// Render lightning BEFORE rotation so it crackles in all directions
if (disc.hasLightningEffect()) {
renderAttachedLightning(disc, radius * scale);
renderAttachedLightning(disc, 0.6f * 0.5f, radius * scale);
}
GL11.glPushMatrix();
@@ -76,34 +75,6 @@ public class RenderAbilityDisc extends RenderAbilityProjectile {
restoreRenderState();
}
/**
* Render fading lightning arcs attached to the disc (in local space).
*/
private void renderAttachedLightning(EntityAbilityDisc disc, float discSize) {
AttachedLightningRenderer.LightningState state = getLightningState(disc);
float density = disc.getLightningDensity();
// Lightning radius extends outward from inner surface (innerScale = 0.6)
float innerRadius = 0.6f * discSize * 0.5f;
float radius = innerRadius + disc.getLightningRadius() * discSize;
int outerColor = disc.getOuterColor();
int innerColor = disc.getInnerColor();
int fadeTime = disc.getLightningFadeTime();
state.update(density, radius, outerColor, innerColor, fadeTime);
state.render();
}
/**
* Get or create the lightning state for an entity.
*/
private AttachedLightningRenderer.LightningState getLightningState(EntityAbilityDisc disc) {
if (disc.lightningState == null) {
disc.lightningState = new AttachedLightningRenderer.LightningState();
}
return (AttachedLightningRenderer.LightningState) disc.lightningState;
}
/**
* Render a flat square (thin cube) centered at origin.
* The square lies flat on the XZ plane with thin Y height.
@@ -3,7 +3,6 @@ package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
import kamkeel.npcs.entity.EntityAbilityBeam;
import kamkeel.npcs.entity.EntityAbilityLaser;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
@@ -15,7 +14,7 @@ import org.lwjgl.opengl.GL11;
* Design inspired by LouisXIV's energy rendering system.
*/
@SideOnly(Side.CLIENT)
public class RenderAbilityLaser extends RenderAbilityProjectile {
public class RenderAbilityLaser extends RenderEnergyAbility {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
@@ -147,7 +146,7 @@ public class RenderAbilityLaser extends RenderAbilityProjectile {
// Render lightning during charging if enabled
if (laser.hasLightningEffect()) {
renderAttachedLightning(laser, scale);
renderAttachedLightning(laser, 0.6f * 0.5f, scale);
}
GL11.glPushMatrix();
@@ -170,7 +169,7 @@ public class RenderAbilityLaser extends RenderAbilityProjectile {
// Render inner core
GL11.glScalef(innerScale, innerScale, innerScale);
renderCube(innerColor, 1.0f, 0.5f);
renderCube(innerColor, laser.getInnerAlpha(), 0.5f);
GL11.glPopMatrix();
GL11.glPopMatrix();
@@ -215,31 +214,6 @@ public class RenderAbilityLaser extends RenderAbilityProjectile {
}
private void renderAttachedLightning(EntityAbilityLaser laser, float headScale) {
AttachedLightningRenderer.LightningState state = getLightningState(laser);
float density = laser.getLightningDensity();
// Lightning radius extends outward from inner surface (innerScale = 0.6)
float innerRadius = 0.6f * headScale * 0.5f;
float radius = innerRadius + laser.getLightningRadius() * headScale;
int outerColor = laser.getOuterColor();
int innerColor = laser.getInnerColor();
int fadeTime = laser.getLightningFadeTime();
state.update(density, radius, outerColor, innerColor, fadeTime);
state.render();
}
/**
* Get or create the main lightning state for the laser.
*/
private AttachedLightningRenderer.LightningState getLightningState(EntityAbilityLaser laser) {
if (laser.lightningState == null) {
laser.lightningState = new AttachedLightningRenderer.LightningState();
}
return (AttachedLightningRenderer.LightningState) laser.lightningState;
}
/**
* Get or create the tip lightning state for the laser.
* Stored as a secondary object in lightningState2.
@@ -2,7 +2,6 @@ package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
import kamkeel.npcs.entity.EntityAbilityOrb;
import net.minecraft.entity.Entity;
import org.lwjgl.opengl.GL11;
@@ -12,7 +11,7 @@ import org.lwjgl.opengl.GL11;
* Minecraft-style blocky appearance using Tessellator.
*/
@SideOnly(Side.CLIENT)
public class RenderAbilityOrb extends RenderAbilityProjectile {
public class RenderAbilityOrb extends RenderEnergyAbility {
private static final int LIGHTNING_FADE_TICKS = 6;
@@ -35,7 +34,7 @@ public class RenderAbilityOrb extends RenderAbilityProjectile {
// Render lightning BEFORE rotation so it crackles in all directions
if (orb.hasLightningEffect()) {
renderAttachedLightning(orb, scale);
renderAttachedLightning(orb, 0.6f * 0.5f, scale);
}
// Use entity's interpolated rotation for smooth spinning
@@ -63,7 +62,7 @@ public class RenderAbilityOrb extends RenderAbilityProjectile {
// Render inner cube (solid)
GL11.glScalef(innerScale, innerScale, innerScale);
renderCube(orb.getInnerColor(), 1.0f, 0.5f);
renderCube(orb.getInnerColor(), orb.getInnerAlpha(), 0.5f);
GL11.glPopMatrix();
GL11.glPopMatrix();
@@ -71,36 +70,4 @@ public class RenderAbilityOrb extends RenderAbilityProjectile {
restoreRenderState();
}
/**
* Render fading lightning arcs attached to the orb (in local space).
* Arcs persist for a few ticks and fade out, while staying attached to the orb.
*/
private void renderAttachedLightning(EntityAbilityOrb orb, float orbScale) {
// Get or create the lightning state for this entity
AttachedLightningRenderer.LightningState state = getLightningState(orb);
float density = orb.getLightningDensity();
// Lightning radius extends outward from inner surface (innerScale = 0.6)
float innerRadius = 0.6f * orbScale * 0.5f; // Half size of inner cube
float radius = innerRadius + orb.getLightningRadius() * orbScale;
int outerColor = orb.getOuterColor();
int innerColor = orb.getInnerColor();
int fadeTime = orb.getLightningFadeTime();
// Update: age existing arcs, spawn new ones based on density
state.update(density, radius, outerColor, innerColor, fadeTime);
// Render all active arcs in local space (they move with the orb)
state.render();
}
/**
* Get or create the lightning state for an entity.
*/
private AttachedLightningRenderer.LightningState getLightningState(EntityAbilityOrb orb) {
if (orb.lightningState == null) {
orb.lightningState = new AttachedLightningRenderer.LightningState();
}
return (AttachedLightningRenderer.LightningState) orb.lightningState;
}
}
@@ -2,6 +2,8 @@ package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
import kamkeel.npcs.entity.EntityEnergyAbility;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.Render;
@@ -17,11 +19,11 @@ import org.lwjgl.opengl.GL12;
* Design inspired by LouisXIV's energy rendering system.
*/
@SideOnly(Side.CLIENT)
public abstract class RenderAbilityProjectile extends Render {
public abstract class RenderEnergyAbility extends Render {
protected static final ResourceLocation WHITE_TEXTURE = new ResourceLocation("customnpcs", "textures/entity/white.png");
public RenderAbilityProjectile() {
public RenderEnergyAbility() {
this.shadowSize = 0.0f;
}
@@ -252,6 +254,40 @@ public abstract class RenderAbilityProjectile extends Render {
tess.draw();
}
// ==================== SHARED LIGHTNING ====================
/**
* Get or create the lightning state for an energy entity.
* Uses the lightningState field on EntityEnergyAbility (stored as Object
* to avoid client class loading on server).
*/
protected AttachedLightningRenderer.LightningState getLightningState(EntityEnergyAbility entity) {
if (entity.lightningState == null) {
entity.lightningState = new AttachedLightningRenderer.LightningState();
}
return (AttachedLightningRenderer.LightningState) entity.lightningState;
}
/**
* Render attached lightning arcs around an energy entity.
* @param entity the energy entity
* @param innerScale multiplier for inner radius (e.g. 0.3f for projectiles, 0.95f for dome)
* @param baseSize the entity's current render size/scale
*/
protected void renderAttachedLightning(EntityEnergyAbility entity, float innerScale, float baseSize) {
AttachedLightningRenderer.LightningState state = getLightningState(entity);
float density = entity.getLightningDensity();
float innerRadius = innerScale * baseSize;
float radius = innerRadius + entity.getLightningRadius() * baseSize;
int outerColor = entity.getOuterColor();
int innerColor = entity.getInnerColor();
int fadeTime = entity.getLightningFadeTime();
state.update(density, radius, outerColor, innerColor, fadeTime);
state.render();
}
@Override
protected ResourceLocation getEntityTexture(Entity entity) {
return WHITE_TEXTURE;
@@ -0,0 +1,23 @@
package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.entity.EntityEnergyBarrier;
/**
* Intermediate renderer base for energy barrier entities (Dome, Panel).
* Provides shared hit flash alpha computation.
*/
@SideOnly(Side.CLIENT)
public abstract class RenderEnergyBarrier extends RenderEnergyAbility {
/**
* Compute the alpha boost from a hit flash effect.
* Flash value decays from 4 to 0, producing alpha from 0.3 to 0.
*/
protected float computeFlashAlpha(EntityEnergyBarrier barrier) {
byte flash = barrier.getHitFlash();
if (flash <= 0) return 0.0f;
return flash / 4.0f * 0.3f;
}
}
@@ -2,19 +2,18 @@ package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
import kamkeel.npcs.entity.EntityEnergyDome;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
import org.lwjgl.opengl.GL11;
/**
* Renders the Energy Dome entity as a translucent blocky hemisphere.
* Built from cube panels forming a dome shape - voxel-blocky aesthetic.
* Inner layer more opaque, outer layer translucent glow.
* Renders the Dome entity as a translucent sphere.
* Inner layer solid, outer layer translucent glow.
* Optional skybox texture rendered on the interior surface.
*/
@SideOnly(Side.CLIENT)
public class RenderEnergyDome extends RenderAbilityProjectile {
public class RenderEnergyDome extends RenderEnergyBarrier {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
@@ -27,31 +26,32 @@ public class RenderEnergyDome extends RenderAbilityProjectile {
GL11.glPushMatrix();
GL11.glTranslated(x, y, z);
// Lightning
// Lightning BEFORE any rotation (uniform spread)
if (dome.hasLightningEffect()) {
renderAttachedLightning(dome, radius);
renderAttachedLightning(dome, 0.95f, radius);
}
// Hit flash effect
float flashAlpha = 0.0f;
if (dome.getHitFlash() > 0) {
flashAlpha = dome.getHitFlash() / 4.0f * 0.3f;
}
float flashAlpha = computeFlashAlpha(dome);
float innerScale = 0.95f;
// Render outer dome (translucent)
// Render outer dome (translucent glow)
if (dome.isOuterColorEnabled()) {
float outerScale = 1.0f + dome.getOuterColorWidth() * 0.1f;
float outerAlpha = dome.getOuterColorAlpha() * healthPercent;
GL11.glDepthMask(false);
renderBlockyDome(dome.getOuterColor(), outerAlpha + flashAlpha, radius * outerScale, 8);
renderSphere(dome.getOuterColor(), outerAlpha + flashAlpha, radius * outerScale, 8);
GL11.glDepthMask(true);
}
// Render inner dome (more opaque)
float innerAlpha = (0.5f + 0.3f * healthPercent);
renderBlockyDome(dome.getInnerColor(), innerAlpha + flashAlpha, radius * innerScale, 8);
// TODO: Skybox Feature
// if (dome.isSkyboxEnabled() && !dome.getSkyboxTexture().isEmpty()) {
// renderSkyboxSphere(dome.getSkyboxTexture(), radius, radius * 0.94f, 32, x, y, z);
// }
// Render inner dome
renderSphere(dome.getInnerColor(), dome.getInnerAlpha(), radius * innerScale, 8);
GL11.glPopMatrix();
@@ -59,19 +59,19 @@ public class RenderEnergyDome extends RenderAbilityProjectile {
}
/**
* Render a dome made of blocky quad panels - voxel style.
* Uses latitude/longitude segments to form a hemisphere of flat panels.
* Render a sphere made of quad panels.
* Uses latitude/longitude segments to form a full sphere.
*/
private void renderBlockyDome(int color, float alpha, float radius, int segments) {
private void renderSphere(int color, float alpha, float radius, int segments) {
float[] rgb = extractRGB(color);
float r = rgb[0], g = rgb[1], b = rgb[2];
Tessellator tess = Tessellator.instance;
int latSegments = segments / 2; // Half sphere
tess.startDrawingQuads();
tess.setColorRGBA_F(rgb[0], rgb[1], rgb[2], alpha);
for (int lat = 0; lat < latSegments; lat++) {
float theta1 = (float) (Math.PI * 0.5 * lat / latSegments);
float theta2 = (float) (Math.PI * 0.5 * (lat + 1) / latSegments);
for (int lat = 0; lat < segments; lat++) {
float theta1 = (float) (Math.PI * lat / segments - Math.PI * 0.5);
float theta2 = (float) (Math.PI * (lat + 1) / segments - Math.PI * 0.5);
float y1 = (float) Math.sin(theta1) * radius;
float y2 = (float) Math.sin(theta2) * radius;
@@ -91,63 +91,22 @@ public class RenderEnergyDome extends RenderAbilityProjectile {
float x4 = (float) Math.cos(phi1) * r2;
float z4 = (float) Math.sin(phi1) * r2;
// Normal pointing outward
float nx = (x1 + x2 + x3 + x4) * 0.25f;
float ny = (y1 + y1 + y2 + y2) * 0.25f;
float nz = (z1 + z2 + z3 + z4) * 0.25f;
float nLen = (float) Math.sqrt(nx * nx + ny * ny + nz * nz);
if (nLen > 0) { nx /= nLen; ny /= nLen; nz /= nLen; }
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
tess.setNormal(nx, ny, nz);
tess.addVertex(x1, y1, z1);
tess.addVertex(x2, y1, z2);
tess.addVertex(x3, y2, z3);
tess.addVertex(x4, y2, z4);
tess.draw();
}
}
// Bottom ring (ground level circle)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha * 0.5f);
tess.setNormal(0, -1, 0);
for (int lon = 0; lon < segments; lon++) {
float phi1 = (float) (2 * Math.PI * lon / segments);
float phi2 = (float) (2 * Math.PI * (lon + 1) / segments);
float x1 = (float) Math.cos(phi1) * radius;
float z1 = (float) Math.sin(phi1) * radius;
float x2 = (float) Math.cos(phi2) * radius;
float z2 = (float) Math.sin(phi2) * radius;
tess.addVertex(0, 0, 0);
tess.addVertex(x1, 0, z1);
tess.addVertex(x2, 0, z2);
tess.addVertex(0, 0, 0);
}
tess.draw();
}
/**
* Render lightning arcs on the dome surface.
*/
private void renderAttachedLightning(EntityEnergyDome dome, float radius) {
AttachedLightningRenderer.LightningState state = getLightningState(dome);
float density = dome.getLightningDensity();
float lightningRadius = radius + dome.getLightningRadius();
int outerColor = dome.getOuterColor();
int innerColor = dome.getInnerColor();
int fadeTime = dome.getLightningFadeTime();
state.update(density, lightningRadius, outerColor, innerColor, fadeTime);
state.render();
}
private AttachedLightningRenderer.LightningState getLightningState(EntityEnergyDome dome) {
if (dome.lightningState == null) {
dome.lightningState = new AttachedLightningRenderer.LightningState();
}
return (AttachedLightningRenderer.LightningState) dome.lightningState;
}
// TODO: Skybox Feature — renderSkyboxSphere method removed
}
@@ -2,7 +2,6 @@ package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
import kamkeel.npcs.entity.EntityEnergyPanel;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
@@ -14,7 +13,7 @@ import org.lwjgl.opengl.GL11;
* Voxel-blocky style with inner/outer layers.
*/
@SideOnly(Side.CLIENT)
public class RenderEnergyPanel extends RenderAbilityProjectile {
public class RenderEnergyPanel extends RenderEnergyBarrier {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
@@ -30,19 +29,16 @@ public class RenderEnergyPanel extends RenderAbilityProjectile {
GL11.glPushMatrix();
GL11.glTranslated(x, y, z);
// Lightning BEFORE rotation for uniform spread (like Orb pattern)
if (panel.hasLightningEffect()) {
renderAttachedLightning(panel, 0.9f, Math.max(width, height) * 0.5f);
}
// Rotate panel to face the correct direction
GL11.glRotatef(-panelYaw, 0.0f, 1.0f, 0.0f);
// Lightning (before rotation for uniform spread)
if (panel.hasLightningEffect()) {
renderAttachedLightning(panel, Math.max(width, height) * 0.5f);
}
// Hit flash
float flashAlpha = 0.0f;
if (panel.getHitFlash() > 0) {
flashAlpha = panel.getHitFlash() / 4.0f * 0.3f;
}
float flashAlpha = computeFlashAlpha(panel);
// Subtle pulsing
float pulseTime = entity.ticksExisted + partialTicks;
@@ -65,9 +61,8 @@ public class RenderEnergyPanel extends RenderAbilityProjectile {
GL11.glDepthMask(true);
}
// Render inner panel (more opaque core)
float innerAlpha = (0.6f + 0.3f * healthPercent);
renderPanel(panel.getInnerColor(), innerAlpha + flashAlpha,
// Render inner panel (uses configurable innerAlpha for semi-transparency)
renderPanel(panel.getInnerColor(), panel.getInnerAlpha(),
width * 0.5f * innerScale, height * 0.5f * innerScale, panelThickness);
GL11.glPopMatrix();
@@ -79,93 +74,59 @@ public class RenderEnergyPanel extends RenderAbilityProjectile {
/**
* Render a flat panel (thin box) centered at origin.
* Panel faces along the Z axis (width on X, height on Y).
* All 6 faces batched into a single draw call.
*/
private void renderPanel(int color, float alpha, float halfWidth, float halfHeight, float halfThickness) {
float[] rgb = extractRGB(color);
float r = rgb[0], g = rgb[1], b = rgb[2];
Tessellator tess = Tessellator.instance;
// Front face (z+)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
// Front face (z+)
tess.setNormal(0, 0, 1);
tess.addVertex(-halfWidth, -halfHeight, halfThickness);
tess.addVertex(halfWidth, -halfHeight, halfThickness);
tess.addVertex(halfWidth, halfHeight, halfThickness);
tess.addVertex(-halfWidth, halfHeight, halfThickness);
tess.draw();
// Back face (z-)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
tess.setNormal(0, 0, -1);
tess.addVertex(halfWidth, -halfHeight, -halfThickness);
tess.addVertex(-halfWidth, -halfHeight, -halfThickness);
tess.addVertex(-halfWidth, halfHeight, -halfThickness);
tess.addVertex(halfWidth, halfHeight, -halfThickness);
tess.draw();
// Top edge (y+)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
tess.setNormal(0, 1, 0);
tess.addVertex(-halfWidth, halfHeight, halfThickness);
tess.addVertex(halfWidth, halfHeight, halfThickness);
tess.addVertex(halfWidth, halfHeight, -halfThickness);
tess.addVertex(-halfWidth, halfHeight, -halfThickness);
tess.draw();
// Bottom edge (y-)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
tess.setNormal(0, -1, 0);
tess.addVertex(-halfWidth, -halfHeight, -halfThickness);
tess.addVertex(halfWidth, -halfHeight, -halfThickness);
tess.addVertex(halfWidth, -halfHeight, halfThickness);
tess.addVertex(-halfWidth, -halfHeight, halfThickness);
tess.draw();
// Right edge (x+)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
tess.setNormal(1, 0, 0);
tess.addVertex(halfWidth, -halfHeight, halfThickness);
tess.addVertex(halfWidth, -halfHeight, -halfThickness);
tess.addVertex(halfWidth, halfHeight, -halfThickness);
tess.addVertex(halfWidth, halfHeight, halfThickness);
tess.draw();
// Left edge (x-)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
tess.setNormal(-1, 0, 0);
tess.addVertex(-halfWidth, -halfHeight, -halfThickness);
tess.addVertex(-halfWidth, -halfHeight, halfThickness);
tess.addVertex(-halfWidth, halfHeight, halfThickness);
tess.addVertex(-halfWidth, halfHeight, -halfThickness);
tess.draw();
}
/**
* Render lightning arcs on the panel surface.
*/
private void renderAttachedLightning(EntityEnergyPanel panel, float size) {
AttachedLightningRenderer.LightningState state = getLightningState(panel);
float density = panel.getLightningDensity();
float radius = size + panel.getLightningRadius();
int outerColor = panel.getOuterColor();
int innerColor = panel.getInnerColor();
int fadeTime = panel.getLightningFadeTime();
state.update(density, radius, outerColor, innerColor, fadeTime);
state.render();
}
private AttachedLightningRenderer.LightningState getLightningState(EntityEnergyPanel panel) {
if (panel.lightningState == null) {
panel.lightningState = new AttachedLightningRenderer.LightningState();
}
return (AttachedLightningRenderer.LightningState) panel.lightningState;
}
}
@@ -2,7 +2,6 @@ package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.client.renderer.lightning.AttachedLightningRenderer;
import kamkeel.npcs.entity.EntityEnergySlicer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
@@ -14,28 +13,27 @@ import org.lwjgl.opengl.GL11;
* Inner solid core with outer translucent glow.
*/
@SideOnly(Side.CLIENT)
public class RenderEnergySlicer extends RenderAbilityProjectile {
public class RenderEnergySlicer extends RenderEnergyAbility {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
EntityEnergySlicer slicer = (EntityEnergySlicer) entity;
float size = slicer.getInterpolatedSize(partialTicks);
float width = slicer.getSliceWidth();
float thickness = slicer.getSliceThickness();
// Scale with interpolated size for charging grow effect
float sizeRatio = size > 0.01f ? size / Math.max(0.01f, slicer.getSliceWidth()) : 0.01f;
if (sizeRatio > 1.0f) sizeRatio = 1.0f;
// During charging, width/thickness grow from 0.01 to target via updateCharging().
// Use current dimensions directly - no separate sizeRatio needed.
if (width <= 0.01f) return;
setupRenderState();
GL11.glPushMatrix();
GL11.glTranslated(x, y, z);
// Lightning before rotation
// Lightning BEFORE rotation for uniform spread (like Orb pattern)
if (slicer.hasLightningEffect()) {
renderAttachedLightning(slicer, width * 0.5f * sizeRatio);
renderAttachedLightning(slicer, 0.7f, width * 0.5f);
}
// Orient perpendicular to travel direction
@@ -44,9 +42,6 @@ public class RenderEnergySlicer extends RenderAbilityProjectile {
GL11.glRotatef(travelYaw, 0.0f, 1.0f, 0.0f);
GL11.glRotatef(travelPitch, 1.0f, 0.0f, 0.0f);
GL11.glPushMatrix();
GL11.glScalef(sizeRatio, sizeRatio, sizeRatio);
float innerScale = 0.7f;
// Render outer blade (translucent glow)
@@ -58,12 +53,11 @@ public class RenderEnergySlicer extends RenderAbilityProjectile {
GL11.glDepthMask(true);
}
// Render inner blade (solid core)
// Render inner blade (solid core, alpha 1.0 like Orb pattern)
renderBlade(slicer.getInnerColor(), 1.0f,
width * 0.5f * innerScale, thickness, 0.03f);
GL11.glPopMatrix();
GL11.glPopMatrix();
restoreRenderState();
}
@@ -71,93 +65,61 @@ public class RenderEnergySlicer extends RenderAbilityProjectile {
/**
* Render a thin wide blade shape.
* The blade extends along X (width), thin on Y (thickness), and very thin on Z (depth).
* All 6 faces batched into a single draw call.
*/
private void renderBlade(int color, float alpha, float halfWidth, float halfHeight, float halfDepth) {
float[] rgb = extractRGB(color);
float r = rgb[0], g = rgb[1], b = rgb[2];
Tessellator tess = Tessellator.instance;
tess.startDrawingQuads();
// Front face (z+)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
tess.setNormal(0, 0, 1);
tess.addVertex(-halfWidth, -halfHeight, halfDepth);
tess.addVertex(halfWidth, -halfHeight, halfDepth);
tess.addVertex(halfWidth, halfHeight, halfDepth);
tess.addVertex(-halfWidth, halfHeight, halfDepth);
tess.draw();
// Back face (z-)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha);
tess.setNormal(0, 0, -1);
tess.addVertex(halfWidth, -halfHeight, -halfDepth);
tess.addVertex(-halfWidth, -halfHeight, -halfDepth);
tess.addVertex(-halfWidth, halfHeight, -halfDepth);
tess.addVertex(halfWidth, halfHeight, -halfDepth);
tess.draw();
// Top edge (y+)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha * 0.8f);
tess.setNormal(0, 1, 0);
tess.addVertex(-halfWidth, halfHeight, halfDepth);
tess.addVertex(halfWidth, halfHeight, halfDepth);
tess.addVertex(halfWidth, halfHeight, -halfDepth);
tess.addVertex(-halfWidth, halfHeight, -halfDepth);
tess.draw();
// Bottom edge (y-)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha * 0.8f);
tess.setNormal(0, -1, 0);
tess.addVertex(-halfWidth, -halfHeight, -halfDepth);
tess.addVertex(halfWidth, -halfHeight, -halfDepth);
tess.addVertex(halfWidth, -halfHeight, halfDepth);
tess.addVertex(-halfWidth, -halfHeight, halfDepth);
tess.draw();
// Right edge (x+)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha * 0.6f);
tess.setNormal(1, 0, 0);
tess.addVertex(halfWidth, -halfHeight, halfDepth);
tess.addVertex(halfWidth, -halfHeight, -halfDepth);
tess.addVertex(halfWidth, halfHeight, -halfDepth);
tess.addVertex(halfWidth, halfHeight, halfDepth);
tess.draw();
// Left edge (x-)
tess.startDrawingQuads();
tess.setColorRGBA_F(r, g, b, alpha * 0.6f);
tess.setNormal(-1, 0, 0);
tess.addVertex(-halfWidth, -halfHeight, -halfDepth);
tess.addVertex(-halfWidth, -halfHeight, halfDepth);
tess.addVertex(-halfWidth, halfHeight, halfDepth);
tess.addVertex(-halfWidth, halfHeight, -halfDepth);
tess.draw();
}
/**
* Render lightning arcs on the slicer.
*/
private void renderAttachedLightning(EntityEnergySlicer slicer, float size) {
AttachedLightningRenderer.LightningState state = getLightningState(slicer);
float density = slicer.getLightningDensity();
float radius = size + slicer.getLightningRadius();
int outerColor = slicer.getOuterColor();
int innerColor = slicer.getInnerColor();
int fadeTime = slicer.getLightningFadeTime();
state.update(density, radius, outerColor, innerColor, fadeTime);
state.render();
}
private AttachedLightningRenderer.LightningState getLightningState(EntityEnergySlicer slicer) {
if (slicer.lightningState == null) {
slicer.lightningState = new AttachedLightningRenderer.LightningState();
}
return (AttachedLightningRenderer.LightningState) slicer.lightningState;
}
}
@@ -2,6 +2,15 @@ package kamkeel.npcs.controllers.data.ability;
import kamkeel.npcs.controllers.data.ability.type.*;
import kamkeel.npcs.controllers.SyncController;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityBeam;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityDisc;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityDome;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityLaserShot;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityOrb;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityShield;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilitySlicer;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilitySweeper;
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityWall;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.CustomNpcs;
@@ -100,10 +109,10 @@ public class AbilityController implements IAbilityHandler {
registerType("cnpc:hazard", AbilityHazard::new);
registerType("cnpc:trap", AbilityTrap::new);
registerType("cnpc:energy_dome", AbilityEnergyDome::new);
registerType("cnpc:energy_wall", AbilityEnergyWall::new);
registerType("cnpc:energy_shield", AbilityEnergyShield::new);
registerType("cnpc:energy_slicer", AbilityEnergySlicer::new);
registerType("cnpc:dome", AbilityDome::new);
registerType("cnpc:wall", AbilityWall::new);
registerType("cnpc:shield", AbilityShield::new);
registerType("cnpc:slicer", AbilitySlicer::new);
}
// ═══════════════════════════════════════════════════════════════════════════
@@ -10,12 +10,25 @@ import java.util.Map;
* Handles health/durability, duration, and per-projectile-type damage multipliers.
*/
public class EnergyBarrierData {
// Knockback target display keys
private static final String[] KNOCKBACK_TARGET_KEYS = {"gui.both", "gui.players", "gui.npcs"};
public float maxHealth = 100.0f;
public boolean useHealth = true;
public int durationTicks = 200;
public boolean useDuration = true;
public float defaultMultiplier = 1.0f;
// Knockback settings
public boolean knockbackEnabled = false;
public float knockbackStrength = 1.0f;
/** 0=Both, 1=Player only, 2=NPC only */
public int knockbackTarget = 0;
// Melee settings
public boolean meleeEnabled = false;
public float meleeDamageMultiplier = 1.0f;
/**
* Per-projectile-type damage multipliers.
* Key is the ability typeId (e.g., "ability.cnpc.orb", "ability.cnpc.beam").
@@ -72,6 +85,22 @@ public class EnergyBarrierData {
public float getDefaultMultiplier() { return defaultMultiplier; }
public void setDefaultMultiplier(float defaultMultiplier) { this.defaultMultiplier = defaultMultiplier; }
// Knockback target key helpers
public static String[] getKnockbackTargetKeys() { return KNOCKBACK_TARGET_KEYS; }
public String getKnockbackTargetKey() {
return KNOCKBACK_TARGET_KEYS[Math.max(0, Math.min(knockbackTarget, 2))];
}
public void setKnockbackTargetFromKey(String key) {
for (int i = 0; i < KNOCKBACK_TARGET_KEYS.length; i++) {
if (KNOCKBACK_TARGET_KEYS[i].equals(key)) {
knockbackTarget = i;
return;
}
}
}
// ==================== NBT ====================
public void writeNBT(NBTTagCompound nbt) {
@@ -80,6 +109,11 @@ public class EnergyBarrierData {
nbt.setInteger("barrierDuration", durationTicks);
nbt.setBoolean("barrierUseDuration", useDuration);
nbt.setFloat("barrierDefaultMult", defaultMultiplier);
nbt.setBoolean("barrierKnockback", knockbackEnabled);
nbt.setFloat("barrierKnockbackStr", knockbackStrength);
nbt.setInteger("barrierKnockbackTarget", knockbackTarget);
nbt.setBoolean("barrierMelee", meleeEnabled);
nbt.setFloat("barrierMeleeMult", meleeDamageMultiplier);
NBTTagCompound multNbt = new NBTTagCompound();
for (Map.Entry<String, Float> entry : damageMultipliers.entrySet()) {
@@ -89,11 +123,16 @@ public class EnergyBarrierData {
}
public void readNBT(NBTTagCompound nbt) {
maxHealth = nbt.getFloat("barrierMaxHealth");
useHealth = nbt.getBoolean("barrierUseHealth");
durationTicks = nbt.getInteger("barrierDuration");
useDuration = nbt.getBoolean("barrierUseDuration");
maxHealth = nbt.hasKey("barrierMaxHealth") ? nbt.getFloat("barrierMaxHealth") : 100.0f;
useHealth = !nbt.hasKey("barrierUseHealth") || nbt.getBoolean("barrierUseHealth");
durationTicks = nbt.hasKey("barrierDuration") ? nbt.getInteger("barrierDuration") : 200;
useDuration = !nbt.hasKey("barrierUseDuration") || nbt.getBoolean("barrierUseDuration");
defaultMultiplier = nbt.hasKey("barrierDefaultMult") ? nbt.getFloat("barrierDefaultMult") : 1.0f;
knockbackEnabled = nbt.hasKey("barrierKnockback") && nbt.getBoolean("barrierKnockback");
knockbackStrength = nbt.hasKey("barrierKnockbackStr") ? nbt.getFloat("barrierKnockbackStr") : 1.0f;
knockbackTarget = nbt.hasKey("barrierKnockbackTarget") ? nbt.getInteger("barrierKnockbackTarget") : 0;
meleeEnabled = nbt.hasKey("barrierMelee") && nbt.getBoolean("barrierMelee");
meleeDamageMultiplier = nbt.hasKey("barrierMeleeMult") ? nbt.getFloat("barrierMeleeMult") : 1.0f;
damageMultipliers.clear();
if (nbt.hasKey("barrierMultipliers")) {
@@ -109,6 +148,11 @@ public class EnergyBarrierData {
public EnergyBarrierData copy() {
EnergyBarrierData copy = new EnergyBarrierData(maxHealth, useHealth, durationTicks, useDuration);
copy.defaultMultiplier = defaultMultiplier;
copy.knockbackEnabled = knockbackEnabled;
copy.knockbackStrength = knockbackStrength;
copy.knockbackTarget = knockbackTarget;
copy.meleeEnabled = meleeEnabled;
copy.meleeDamageMultiplier = meleeDamageMultiplier;
copy.damageMultipliers = new HashMap<>(damageMultipliers);
return copy;
}
@@ -13,8 +13,13 @@ public class EnergyDisplayData implements IEnergyDisplayData {
public boolean outerColorEnabled = true;
public float outerColorWidth = 0.4f;
public float outerColorAlpha = 0.5f;
public float innerAlpha = 1.0f;
public float rotationSpeed = 4.0f;
// TODO: Skybox Feature
// public boolean skyboxEnabled = false;
// public String skyboxTexture = "";
public EnergyDisplayData() {}
public EnergyDisplayData(int innerColor, int outerColor) {
@@ -32,6 +37,12 @@ public class EnergyDisplayData implements IEnergyDisplayData {
this.rotationSpeed = rotationSpeed;
}
public EnergyDisplayData(int innerColor, int outerColor, boolean outerColorEnabled,
float outerColorWidth, float outerColorAlpha, float innerAlpha, float rotationSpeed) {
this(innerColor, outerColor, outerColorEnabled, outerColorWidth, outerColorAlpha, rotationSpeed);
this.innerAlpha = innerAlpha;
}
@Override
public int getInnerColor() {
return innerColor;
@@ -82,6 +93,16 @@ public class EnergyDisplayData implements IEnergyDisplayData {
this.outerColorAlpha = outerColorAlpha;
}
@Override
public float getInnerAlpha() {
return innerAlpha;
}
@Override
public void setInnerAlpha(float innerAlpha) {
this.innerAlpha = innerAlpha;
}
@Override
public float getRotationSpeed() {
return rotationSpeed;
@@ -98,7 +119,11 @@ public class EnergyDisplayData implements IEnergyDisplayData {
nbt.setBoolean("outerColorEnabled", outerColorEnabled);
nbt.setFloat("outerColorWidth", outerColorWidth);
nbt.setFloat("outerColorAlpha", outerColorAlpha);
nbt.setFloat("innerAlpha", innerAlpha);
nbt.setFloat("rotationSpeed", rotationSpeed);
// TODO: Skybox Feature
// nbt.setBoolean("skyboxEnabled", skyboxEnabled);
// nbt.setString("skyboxTexture", skyboxTexture);
}
public void readNBT(NBTTagCompound nbt) {
@@ -107,10 +132,18 @@ public class EnergyDisplayData implements IEnergyDisplayData {
outerColorEnabled = nbt.getBoolean("outerColorEnabled");
outerColorWidth = nbt.getFloat("outerColorWidth");
outerColorAlpha = nbt.getFloat("outerColorAlpha");
innerAlpha = nbt.hasKey("innerAlpha") ? nbt.getFloat("innerAlpha") : 1.0f;
rotationSpeed = nbt.getFloat("rotationSpeed");
// TODO: Skybox Feature
// skyboxEnabled = nbt.hasKey("skyboxEnabled") && nbt.getBoolean("skyboxEnabled");
// skyboxTexture = nbt.hasKey("skyboxTexture") ? nbt.getString("skyboxTexture") : "";
}
public EnergyDisplayData copy() {
return new EnergyDisplayData(innerColor, outerColor, outerColorEnabled, outerColorWidth, outerColorAlpha, rotationSpeed);
EnergyDisplayData copy = new EnergyDisplayData(innerColor, outerColor, outerColorEnabled, outerColorWidth, outerColorAlpha, innerAlpha, rotationSpeed);
// TODO: Skybox Feature
// copy.skyboxEnabled = skyboxEnabled;
// copy.skyboxTexture = skyboxTexture;
return copy;
}
}
@@ -63,8 +63,8 @@ public class EnergyPanelData {
public void readNBT(NBTTagCompound nbt) {
panelWidth = nbt.hasKey("panelWidth") ? nbt.getFloat("panelWidth") : 3.0f;
panelHeight = nbt.hasKey("panelHeight") ? nbt.getFloat("panelHeight") : 3.0f;
heightOffset = nbt.getFloat("panelHeightOffset");
launching = nbt.getBoolean("panelLaunching");
heightOffset = nbt.hasKey("panelHeightOffset") ? nbt.getFloat("panelHeightOffset") : 0.0f;
launching = nbt.hasKey("panelLaunching") && nbt.getBoolean("panelLaunching");
launchSpeed = nbt.hasKey("panelLaunchSpeed") ? nbt.getFloat("panelLaunchSpeed") : 0.5f;
launchDamage = nbt.hasKey("panelLaunchDamage") ? nbt.getFloat("panelLaunchDamage") : 8.0f;
launchKnockback = nbt.hasKey("panelLaunchKnockback") ? nbt.getFloat("panelLaunchKnockback") : 2.0f;
@@ -1,9 +1,8 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
import kamkeel.npcs.controllers.data.ability.AnchorPoint;
import kamkeel.npcs.controllers.data.ability.LockMovementType;
import kamkeel.npcs.controllers.data.ability.RotationMode;
import kamkeel.npcs.controllers.data.ability.TargetingMode;
@@ -25,7 +24,7 @@ import java.util.List;
* Energy Beam ability: A homing head with a trailing path.
* Supports 1-8 projectiles with shared visuals and per-projectile anchor points.
*/
public class AbilityBeam extends AbstractEnergyProjectileAbility<EntityAbilityBeam> implements IAbilityEnergyBeam {
public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> implements IAbilityEnergyBeam {
private float beamWidth = 0.4f;
private float headSize = 0.6f;
@@ -1,4 +1,4 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -23,7 +23,7 @@ import java.util.List;
* Supports 1-8 projectiles with shared visuals and per-projectile anchor points.
* Has optional boomerang behavior to return to owner.
*/
public class AbilityDisc extends AbstractEnergyProjectileAbility<EntityAbilityDisc> implements IAbilityDisc {
public class AbilityDisc extends AbilityEnergyProjectile<EntityAbilityDisc> implements IAbilityDisc {
private float discRadius = 1.0f;
private float discThickness = 0.2f;
@@ -1,4 +1,4 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -8,6 +8,7 @@ import kamkeel.npcs.controllers.data.ability.TargetingMode;
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
import kamkeel.npcs.entity.EntityEnergyBarrier;
import kamkeel.npcs.entity.EntityEnergyDome;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
@@ -22,38 +23,38 @@ import java.util.List;
* Blocks incoming energy projectiles with configurable damage multipliers.
* Duration and/or HP based.
*/
public class AbilityEnergyDome extends AbstractEnergyBarrierAbility {
public class AbilityDome extends AbilityEnergyBarrier {
private float domeRadius = 5.0f;
private boolean followCaster = false;
public AbilityEnergyDome() {
public AbilityDome() {
super(
new EnergyDisplayData(0x44CCFF, 0x2288FF, true, 0.3f, 0.35f, 0.0f),
new EnergyDisplayData(0x44CCFF, 0x2288FF, true, 0.3f, 0.35f, 0.5f, 0.0f),
new EnergyBarrierData(100.0f, true, 200, true)
);
this.typeId = "ability.cnpc.energy_dome";
this.name = "Energy Dome";
this.typeId = "ability.cnpc.dome";
this.name = "Dome";
this.targetingMode = TargetingMode.SELF;
this.maxRange = 0;
this.minRange = 0;
this.cooldownTicks = 100;
this.windUpTicks = 30;
this.lockMovement = LockMovementType.WINDUP;
this.telegraphType = TelegraphType.RING;
this.showTelegraph = true;
this.windUpAnimationName = "Ability_EnergyDome_Windup";
this.activeAnimationName = "Ability_EnergyDome_Active";
this.windUpAnimationName = "";
this.activeAnimationName = "";
}
// ==================== ABSTRACT IMPLEMENTATIONS ====================
@Override
protected Entity createBarrierEntity(EntityLivingBase caster, EntityLivingBase target) {
protected EntityEnergyBarrier createBarrierEntity(EntityLivingBase caster, EntityLivingBase target) {
EntityEnergyDome dome = new EntityEnergyDome(
caster.worldObj, caster,
caster.posX, caster.posY, caster.posZ,
domeRadius, displayData.copy(), lightningData.copy(), barrierData.copy()
);
dome.setFollowCaster(followCaster);
dome.setSourceAbility(this);
return dome;
}
@@ -79,14 +80,14 @@ public class AbilityEnergyDome extends AbstractEnergyBarrierAbility {
public List<AbilityVariant> getVariants() {
return Arrays.asList(
new AbilityVariant("ability.variant.small", a -> {
AbilityEnergyDome dome = (AbilityEnergyDome) a;
a.setName("Small Energy Dome");
AbilityDome dome = (AbilityDome) a;
a.setName("Small Dome");
dome.setDomeRadius(3.0f);
dome.setBarrierMaxHealth(60.0f);
}),
new AbilityVariant("ability.variant.large", a -> {
AbilityEnergyDome dome = (AbilityEnergyDome) a;
a.setName("Large Energy Dome");
AbilityDome dome = (AbilityDome) a;
a.setName("Large Dome");
dome.setDomeRadius(8.0f);
dome.setBarrierMaxHealth(200.0f);
dome.setBarrierDuration(300);
@@ -99,11 +100,13 @@ public class AbilityEnergyDome extends AbstractEnergyBarrierAbility {
@Override
protected void writeBarrierTypeNBT(NBTTagCompound nbt) {
nbt.setFloat("domeRadius", domeRadius);
nbt.setBoolean("followCaster", followCaster);
}
@Override
protected void readBarrierTypeNBT(NBTTagCompound nbt) {
this.domeRadius = nbt.hasKey("domeRadius") ? nbt.getFloat("domeRadius") : 5.0f;
this.followCaster = nbt.hasKey("followCaster") && nbt.getBoolean("followCaster");
}
// ==================== GETTERS & SETTERS ====================
@@ -111,6 +114,9 @@ public class AbilityEnergyDome extends AbstractEnergyBarrierAbility {
public float getDomeRadius() { return domeRadius; }
public void setDomeRadius(float radius) { this.domeRadius = Math.max(1.0f, radius); }
public boolean isFollowCaster() { return followCaster; }
public void setFollowCaster(boolean follow) { this.followCaster = follow; }
// ==================== TYPE-SPECIFIC GUI ====================
@SideOnly(Side.CLIENT)
@@ -118,5 +124,7 @@ public class AbilityEnergyDome extends AbstractEnergyBarrierAbility {
protected void addBarrierTypeDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.floatField("ability.domeRadius", this::getDomeRadius, this::setDomeRadius)
.range(1.0f, 30.0f));
defs.add(FieldDef.boolField("ability.followCaster", this::isFollowCaster, this::setFollowCaster)
.hover("ability.hover.followCaster"));
}
}
@@ -0,0 +1,134 @@
package kamkeel.npcs.controllers.data.ability.type.energy;
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.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.client.gui.builder.FieldDef;
import java.util.List;
/**
* Abstract base for all energy-based abilities (Projectile, Barrier, Sweeper, etc.).
* Provides shared visual data (colors, lightning) and corresponding API/GUI/NBT.
*/
public abstract class AbilityEnergy extends Ability {
protected EnergyDisplayData displayData;
protected EnergyLightningData lightningData;
protected AbilityEnergy(EnergyDisplayData displayData) {
this.displayData = displayData;
this.lightningData = new EnergyLightningData();
}
// ==================== DISPLAY DATA ====================
public int getInnerColor() { return displayData.innerColor; }
public void setInnerColor(int color) { displayData.innerColor = color; }
public float getInnerAlpha() { return displayData.innerAlpha; }
public void setInnerAlpha(float alpha) { displayData.innerAlpha = alpha; }
public int getOuterColor() { return displayData.outerColor; }
public void setOuterColor(int color) { displayData.outerColor = color; }
public boolean isOuterColorEnabled() { return displayData.outerColorEnabled; }
public void setOuterColorEnabled(boolean enabled) { displayData.outerColorEnabled = enabled; }
public float getOuterColorWidth() { return displayData.outerColorWidth; }
public void setOuterColorWidth(float width) { displayData.outerColorWidth = width; }
public float getOuterColorAlpha() { return displayData.outerColorAlpha; }
public void setOuterColorAlpha(float alpha) { displayData.outerColorAlpha = alpha; }
public float getRotationSpeed() { return displayData.rotationSpeed; }
public void setRotationSpeed(float speed) { displayData.rotationSpeed = speed; }
// ==================== LIGHTNING DATA ====================
public boolean hasLightningEffect() { return lightningData.lightningEffect; }
public void setLightningEffect(boolean enabled) { lightningData.lightningEffect = enabled; }
public float getLightningDensity() { return lightningData.lightningDensity; }
public void setLightningDensity(float density) { lightningData.lightningDensity = density; }
public float getLightningRadius() { return lightningData.lightningRadius; }
public void setLightningRadius(float radius) { lightningData.lightningRadius = radius; }
public int getLightningFadeTime() { return lightningData.lightningFadeTime; }
public void setLightningFadeTime(int fadeTime) { lightningData.lightningFadeTime = fadeTime; }
// ==================== NBT HELPERS ====================
protected void writeEnergyNBT(NBTTagCompound nbt) {
displayData.writeNBT(nbt);
lightningData.writeNBT(nbt);
}
protected void readEnergyNBT(NBTTagCompound nbt) {
displayData.readNBT(nbt);
lightningData.readNBT(nbt);
}
// ==================== GUI HELPERS ====================
/**
* Add complete visual tab definitions (colors section + effects/lightning section).
* Subclasses can call this directly or use the individual methods for more control.
*/
@SideOnly(Side.CLIENT)
protected void addEnergyVisualDefinitions(List<FieldDef> defs) {
addEnergyColorDefinitions(defs);
addEnergyEffectDefinitions(defs);
}
/**
* Add the colors section to the visual tab.
*/
@SideOnly(Side.CLIENT)
protected void addEnergyColorDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.section("ability.section.colors").tab("ability.tab.visual"));
defs.add(FieldDef.row(
FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor),
FieldDef.floatField("ability.innerAlpha", this::getInnerAlpha, this::setInnerAlpha).range(0, 1)
).tab("ability.tab.visual"));
defs.add(FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled)
.tab("ability.tab.visual"));
defs.add(FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor)
.tab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled));
defs.add(FieldDef.row(
FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth)
.visibleWhen(this::isOuterColorEnabled),
FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha)
.range(0, 1).visibleWhen(this::isOuterColorEnabled)
).tab("ability.tab.visual"));
}
/**
* Add the effects section (lightning) to the visual tab.
* Override to insert additional fields before lightning (e.g., rotationSpeed).
*/
@SideOnly(Side.CLIENT)
protected void addEnergyEffectDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.section("ability.section.effects").tab("ability.tab.visual"));
addEnergyLightningDefinitions(defs);
}
/**
* Add just the lightning fields (no section header) to the visual tab.
*/
@SideOnly(Side.CLIENT)
protected void addEnergyLightningDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect)
.tab("ability.tab.visual"));
defs.add(FieldDef.row(
FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity)
.visibleWhen(this::hasLightningEffect).range(0.01f, 100f),
FieldDef.floatField("gui.radius", this::getLightningRadius, this::setLightningRadius)
.range(0.1f, 100f).visibleWhen(this::hasLightningEffect)
).tab("ability.tab.visual"));
}
}
@@ -1,39 +1,31 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
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.ability.data.EnergyBarrierData;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
import net.minecraft.entity.Entity;
import kamkeel.npcs.entity.EntityEnergyBarrier;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.DamageSource;
import noppes.npcs.client.gui.builder.FieldDef;
import java.util.List;
import java.util.Map;
/**
* Abstract base for energy barrier abilities (Dome, Wall, Shield).
* Handles shared visual data, barrier configuration, and lifecycle management.
*/
public abstract class AbstractEnergyBarrierAbility extends Ability {
public abstract class AbilityEnergyBarrier extends AbilityEnergy {
protected EnergyDisplayData displayData;
protected EnergyLightningData lightningData;
protected final EnergyBarrierData barrierData;
// Runtime entity tracking
protected transient Entity barrierEntity;
protected transient EntityEnergyBarrier barrierEntity;
protected AbstractEnergyBarrierAbility(EnergyDisplayData displayData, EnergyBarrierData barrierData) {
this.displayData = displayData;
protected AbilityEnergyBarrier(EnergyDisplayData displayData, EnergyBarrierData barrierData) {
super(displayData);
this.barrierData = barrierData;
this.lightningData = new EnergyLightningData();
}
// ==================== ABSTRACT METHODS ====================
@@ -41,7 +33,7 @@ public abstract class AbstractEnergyBarrierAbility extends Ability {
/**
* Create and spawn the barrier entity during execution.
*/
protected abstract Entity createBarrierEntity(EntityLivingBase caster, EntityLivingBase target);
protected abstract EntityEnergyBarrier createBarrierEntity(EntityLivingBase caster, EntityLivingBase target);
/**
* Add type-specific GUI field definitions.
@@ -71,11 +63,30 @@ public abstract class AbstractEnergyBarrierAbility extends Ability {
return false; // Barriers are defensive
}
@Override
public void onWindUpTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
if (caster.worldObj.isRemote) return;
if (tick == 1) {
barrierEntity = createBarrierEntity(caster, target);
if (barrierEntity != null) {
barrierEntity.setupCharging(getWindUpTicks());
spawnAbilityEntity(barrierEntity);
}
}
}
@Override
public void onExecute(EntityLivingBase caster, EntityLivingBase target) {
barrierEntity = createBarrierEntity(caster, target);
if (barrierEntity != null) {
spawnAbilityEntity(barrierEntity);
if (barrierEntity != null && !barrierEntity.isDead) {
// Barrier was spawned during windup — transition to active
barrierEntity.finishCharging();
} else {
// No windup or entity died — create fresh
barrierEntity = createBarrierEntity(caster, target);
if (barrierEntity != null) {
spawnAbilityEntity(barrierEntity);
}
}
}
@@ -115,49 +126,20 @@ public abstract class AbstractEnergyBarrierAbility extends Ability {
@Override
public final void writeTypeNBT(NBTTagCompound nbt) {
writeBarrierTypeNBT(nbt);
displayData.writeNBT(nbt);
lightningData.writeNBT(nbt);
writeEnergyNBT(nbt);
barrierData.writeNBT(nbt);
}
@Override
public final void readTypeNBT(NBTTagCompound nbt) {
readBarrierTypeNBT(nbt);
displayData.readNBT(nbt);
lightningData.readNBT(nbt);
readEnergyNBT(nbt);
barrierData.readNBT(nbt);
}
// ==================== API GETTERS & SETTERS ====================
// Display data
public int getInnerColor() { return displayData.innerColor; }
public void setInnerColor(int color) { displayData.innerColor = color; }
public int getOuterColor() { return displayData.outerColor; }
public void setOuterColor(int color) { displayData.outerColor = color; }
public boolean isOuterColorEnabled() { return displayData.outerColorEnabled; }
public void setOuterColorEnabled(boolean enabled) { displayData.outerColorEnabled = enabled; }
public float getOuterColorWidth() { return displayData.outerColorWidth; }
public void setOuterColorWidth(float width) { displayData.outerColorWidth = width; }
public float getOuterColorAlpha() { return displayData.outerColorAlpha; }
public void setOuterColorAlpha(float alpha) { displayData.outerColorAlpha = alpha; }
// Lightning data
public boolean hasLightningEffect() { return lightningData.lightningEffect; }
public void setLightningEffect(boolean enabled) { lightningData.lightningEffect = enabled; }
public float getLightningDensity() { return lightningData.lightningDensity; }
public void setLightningDensity(float density) { lightningData.lightningDensity = density; }
public float getLightningRadius() { return lightningData.lightningRadius; }
public void setLightningRadius(float radius) { lightningData.lightningRadius = radius; }
public int getLightningFadeTime() { return lightningData.lightningFadeTime; }
public void setLightningFadeTime(int fadeTime) { lightningData.lightningFadeTime = fadeTime; }
// Display and lightning data inherited from AbilityEnergy
// Barrier data
public float getBarrierMaxHealth() { return barrierData.maxHealth; }
@@ -178,6 +160,20 @@ public abstract class AbstractEnergyBarrierAbility extends Ability {
public void setDamageMultiplier(String typeId, float mult) { barrierData.setMultiplier(typeId, mult); }
public float getDamageMultiplier(String typeId) { return barrierData.getMultiplier(typeId); }
// Knockback data
public boolean isKnockbackEnabled() { return barrierData.knockbackEnabled; }
public void setKnockbackEnabled(boolean enabled) { barrierData.knockbackEnabled = enabled; }
public float getKnockbackStrength() { return barrierData.knockbackStrength; }
public void setKnockbackStrength(float strength) { barrierData.knockbackStrength = strength; }
public String getKnockbackTargetKey() { return barrierData.getKnockbackTargetKey(); }
public void setKnockbackTargetKey(String key) { barrierData.setKnockbackTargetFromKey(key); }
// Melee data
public boolean isMeleeEnabled() { return barrierData.meleeEnabled; }
public void setMeleeEnabled(boolean enabled) { barrierData.meleeEnabled = enabled; }
public float getMeleeDamageMultiplier() { return barrierData.meleeDamageMultiplier; }
public void setMeleeDamageMultiplier(float mult) { barrierData.meleeDamageMultiplier = mult; }
// ==================== GUI ====================
@SideOnly(Side.CLIENT)
@@ -196,29 +192,24 @@ public abstract class AbstractEnergyBarrierAbility extends Ability {
.range(1, 6000).visibleWhen(this::isUseDuration));
defs.add(FieldDef.floatField("ability.defaultMultiplier", this::getDefaultMultiplier, this::setDefaultMultiplier));
// Visual tab
defs.add(FieldDef.section("ability.section.colors").tab("ability.tab.visual"));
defs.add(FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor)
.tab("ability.tab.visual"));
defs.add(FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled)
.tab("ability.tab.visual"));
defs.add(FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor)
.tab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled));
// Knockback section
defs.add(FieldDef.section("ability.section.knockback"));
defs.add(FieldDef.boolField("ability.knockbackEnabled", this::isKnockbackEnabled, this::setKnockbackEnabled));
defs.add(FieldDef.row(
FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth)
.visibleWhen(this::isOuterColorEnabled),
FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha)
.range(0, 1).visibleWhen(this::isOuterColorEnabled)
).tab("ability.tab.visual"));
FieldDef.floatField("ability.knockbackStrength", this::getKnockbackStrength, this::setKnockbackStrength)
.range(0, 10).visibleWhen(this::isKnockbackEnabled),
FieldDef.stringEnumField("ability.knockbackTarget", EnergyBarrierData.getKnockbackTargetKeys(),
this::getKnockbackTargetKey, this::setKnockbackTargetKey)
.visibleWhen(this::isKnockbackEnabled)
));
defs.add(FieldDef.section("ability.section.effects").tab("ability.tab.visual"));
defs.add(FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect)
.tab("ability.tab.visual"));
defs.add(FieldDef.row(
FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity)
.visibleWhen(this::hasLightningEffect).range(0.01f, 100f),
FieldDef.floatField("gui.radius", this::getLightningRadius, this::setLightningRadius)
.range(0.1f, 100f).visibleWhen(this::hasLightningEffect)
).tab("ability.tab.visual"));
// Melee section
defs.add(FieldDef.section("ability.section.melee"));
defs.add(FieldDef.boolField("ability.meleeEnabled", this::isMeleeEnabled, this::setMeleeEnabled));
defs.add(FieldDef.floatField("ability.meleeDamageMultiplier", this::getMeleeDamageMultiplier, this::setMeleeDamageMultiplier)
.range(0, 10).visibleWhen(this::isMeleeEnabled));
// Visual tab - colors + effects (from AbilityEnergy)
addEnergyVisualDefinitions(defs);
}
}
@@ -1,16 +1,14 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.AnchorPoint;
import kamkeel.npcs.controllers.data.ability.TargetingMode;
import kamkeel.npcs.controllers.data.ability.data.*;
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
import kamkeel.npcs.entity.EntityAbilityProjectile;
import kamkeel.npcs.entity.EntityEnergyProjectile;
import kamkeel.npcs.util.AnchorPointHelper;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
@@ -30,8 +28,8 @@ import java.util.List;
*
* @param <E> The entity type this ability spawns
*/
public abstract class AbstractEnergyProjectileAbility<E extends EntityAbilityProjectile>
extends Ability implements IAbilityEnergyProjectile {
public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
extends AbilityEnergy implements IAbilityEnergyProjectile {
protected static final int MAX_PROJECTILES = 8;
@@ -39,9 +37,7 @@ public abstract class AbstractEnergyProjectileAbility<E extends EntityAbilityPro
protected int projectileCount = 1;
protected int fireDelay = 0;
// Shared visual data
protected EnergyDisplayData displayData;
protected EnergyLightningData lightningData;
// Trajectory data
protected EnergyTrajectoryData trajectoryData;
// Shared combat/movement data
@@ -57,17 +53,16 @@ public abstract class AbstractEnergyProjectileAbility<E extends EntityAbilityPro
// ==================== CONSTRUCTOR ====================
protected AbstractEnergyProjectileAbility(
protected AbilityEnergyProjectile(
EnergyDisplayData displayData,
EnergyCombatData combatData,
EnergyHomingData homingData,
EnergyLifespanData lifespanData
) {
this.displayData = displayData;
super(displayData);
this.combatData = combatData;
this.homingData = homingData;
this.lifespanData = lifespanData;
this.lightningData = new EnergyLightningData();
this.trajectoryData = new EnergyTrajectoryData();
initProjectiles(1);
}
@@ -390,8 +385,7 @@ public abstract class AbstractEnergyProjectileAbility<E extends EntityAbilityPro
nbt.setInteger("fireDelay", fireDelay);
// Shared data classes
displayData.writeNBT(nbt);
lightningData.writeNBT(nbt);
writeEnergyNBT(nbt);
combatData.writeNBT(nbt);
homingData.writeNBT(nbt);
lifespanData.writeNBT(nbt);
@@ -415,8 +409,7 @@ public abstract class AbstractEnergyProjectileAbility<E extends EntityAbilityPro
this.fireDelay = nbt.getInteger("fireDelay");
// Shared data classes
displayData.readNBT(nbt);
lightningData.readNBT(nbt);
readEnergyNBT(nbt);
combatData.readNBT(nbt);
homingData.readNBT(nbt);
lifespanData.readNBT(nbt);
@@ -474,37 +467,7 @@ public abstract class AbstractEnergyProjectileAbility<E extends EntityAbilityPro
public float getHomingRange() { return homingData.homingRange; }
public void setHomingRange(float range) { homingData.homingRange = range; }
// Display data - primary colors
@Override public int getInnerColor() { return displayData.innerColor; }
@Override public void setInnerColor(int color) { displayData.innerColor = color; }
@Override public int getOuterColor() { return displayData.outerColor; }
@Override public void setOuterColor(int color) { displayData.outerColor = color; }
@Override public boolean isOuterColorEnabled() { return displayData.outerColorEnabled; }
@Override public void setOuterColorEnabled(boolean enabled) { displayData.outerColorEnabled = enabled; }
@Override public float getOuterColorWidth() { return displayData.outerColorWidth; }
@Override public void setOuterColorWidth(float width) { displayData.outerColorWidth = width; }
@Override public float getOuterColorAlpha() { return displayData.outerColorAlpha; }
@Override public void setOuterColorAlpha(float alpha) { displayData.outerColorAlpha = alpha; }
public float getRotationSpeed() { return displayData.rotationSpeed; }
public void setRotationSpeed(float speed) { displayData.rotationSpeed = speed; }
// Lightning data
@Override public boolean hasLightningEffect() { return lightningData.lightningEffect; }
@Override public void setLightningEffect(boolean enabled) { lightningData.lightningEffect = enabled; }
@Override public float getLightningDensity() { return lightningData.lightningDensity; }
@Override public void setLightningDensity(float density) { lightningData.lightningDensity = density; }
@Override public float getLightningRadius() { return lightningData.lightningRadius; }
@Override public void setLightningRadius(float radius) { lightningData.lightningRadius = radius; }
public int getLightningFadeTime() { return lightningData.lightningFadeTime; }
public void setLightningFadeTime(int fadeTime) { lightningData.lightningFadeTime = fadeTime; }
// Display and lightning data inherited from AbilityEnergy
// Anchor - default to projectile 0
public AnchorPoint getAnchorPointEnum() { return projectiles[0].anchor.anchorPoint; }
@@ -557,33 +520,14 @@ public abstract class AbstractEnergyProjectileAbility<E extends EntityAbilityPro
// Type-specific fields first
addTypeDefinitions(defs);
// Shared visual tab - primary colors
defs.add(FieldDef.section("ability.section.colors").tab("ability.tab.visual"));
defs.add(FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor)
.tab("ability.tab.visual"));
defs.add(FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled)
.tab("ability.tab.visual"));
defs.add(FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor)
.tab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled));
defs.add(FieldDef.row(
FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth)
.visibleWhen(this::isOuterColorEnabled),
FieldDef.floatField("ability.outerAlpha", this::getOuterColorAlpha, this::setOuterColorAlpha)
.range(0, 1).visibleWhen(this::isOuterColorEnabled)
).tab("ability.tab.visual"));
// Shared visual tab - colors + effects (from AbilityEnergy)
addEnergyColorDefinitions(defs);
// Shared visual tab - effects
// Effects section with projectile-specific rotationSpeed before lightning
defs.add(FieldDef.section("ability.section.effects").tab("ability.tab.visual"));
defs.add(FieldDef.floatField("ability.rotationSpeed", this::getRotationSpeed, this::setRotationSpeed)
.tab("ability.tab.visual"));
defs.add(FieldDef.boolField("ability.lightning", this::hasLightningEffect, this::setLightningEffect)
.tab("ability.tab.visual"));
defs.add(FieldDef.row(
FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity)
.visibleWhen(this::hasLightningEffect).range(0.01f, 100f),
FieldDef.floatField("gui.radius", this::getLightningRadius, this::setLightningRadius)
.range(0.1f, 100f).visibleWhen(this::hasLightningEffect)
).tab("ability.tab.visual"));
addEnergyLightningDefinitions(defs);
// Shared visual tab - per-projectile sections
for (int i = 0; i < MAX_PROJECTILES; i++) {
@@ -1,4 +1,4 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -25,7 +25,7 @@ import java.util.List;
* Travels in a straight line from origin, damaging all entities it passes through.
* Single-projectile only (always projectileCount=1).
*/
public class AbilityLaserShot extends AbstractEnergyProjectileAbility<EntityAbilityLaser> implements IAbilityLaserShot {
public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser> implements IAbilityLaserShot {
private float laserWidth = 0.3f;
private float expansionSpeed = 3.0f;
@@ -73,7 +73,7 @@ public class AbilityLaserShot extends AbstractEnergyProjectileAbility<EntityAbil
/**
* LINE telegraph: positioned at caster, aimed toward target.
* Overrides the circle telegraph from AbstractEnergyProjectileAbility.
* Overrides the circle telegraph from AbilityEnergyProjectile.
*/
@Override
public TelegraphInstance createTelegraph(EntityLivingBase caster, EntityLivingBase target) {
@@ -1,4 +1,4 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -22,7 +22,7 @@ import java.util.List;
* Orb ability: Spawns homing projectile sphere(s) that track target.
* Supports 1-8 projectiles with shared visuals and per-projectile anchor points.
*/
public class AbilityOrb extends AbstractEnergyProjectileAbility<EntityAbilityOrb> implements IAbilityOrb {
public class AbilityOrb extends AbilityEnergyProjectile<EntityAbilityOrb> implements IAbilityOrb {
private float orbSize = 1.0f;
@@ -1,4 +1,4 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -10,6 +10,7 @@ import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.EnergyPanelData;
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
import kamkeel.npcs.entity.EntityEnergyBarrier;
import kamkeel.npcs.entity.EntityEnergyPanel;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
@@ -24,35 +25,33 @@ import java.util.List;
* The shield follows the caster's position and rotates with their look direction.
* Cannot be placed — it's always held.
*/
public class AbilityEnergyShield extends AbstractEnergyBarrierAbility {
public class AbilityShield extends AbilityEnergyBarrier {
private float shieldWidth = 2.5f;
private float shieldHeight = 2.5f;
public AbilityEnergyShield() {
public AbilityShield() {
super(
new EnergyDisplayData(0xFFDD44, 0xFFAA00, true, 0.3f, 0.4f, 0.0f),
new EnergyDisplayData(0xFFDD44, 0xFFAA00, true, 0.3f, 0.4f, 0.5f, 0.0f),
new EnergyBarrierData(60.0f, true, 120, true)
);
this.typeId = "ability.cnpc.energy_shield";
this.name = "Energy Shield";
this.typeId = "ability.cnpc.shield";
this.name = "Shield";
this.targetingMode = TargetingMode.SELF;
this.maxRange = 0;
this.minRange = 0;
this.cooldownTicks = 60;
this.windUpTicks = 10;
this.lockMovement = LockMovementType.NO;
this.lockMovement = LockMovementType.WINDUP_AND_ACTIVE;
this.telegraphType = TelegraphType.NONE;
this.showTelegraph = false;
this.windUpAnimationName = "Ability_EnergyShield_Windup";
this.activeAnimationName = "Ability_EnergyShield_Active";
this.windUpAnimationName = "";
this.activeAnimationName = "";
this.allowedBy = UserType.BOTH;
}
// ==================== ABSTRACT IMPLEMENTATIONS ====================
@Override
protected Entity createBarrierEntity(EntityLivingBase caster, EntityLivingBase target) {
protected EntityEnergyBarrier createBarrierEntity(EntityLivingBase caster, EntityLivingBase target) {
EnergyPanelData panelData = new EnergyPanelData(shieldWidth, shieldHeight, 0.0f);
float frontDist = 1.5f;
@@ -92,15 +91,15 @@ public class AbilityEnergyShield extends AbstractEnergyBarrierAbility {
public List<AbilityVariant> getVariants() {
return Arrays.asList(
new AbilityVariant("ability.variant.small", a -> {
AbilityEnergyShield shield = (AbilityEnergyShield) a;
a.setName("Small Energy Shield");
AbilityShield shield = (AbilityShield) a;
a.setName("Small Shield");
shield.setShieldWidth(1.5f);
shield.setShieldHeight(1.5f);
shield.setBarrierMaxHealth(40.0f);
}),
new AbilityVariant("ability.variant.large", a -> {
AbilityEnergyShield shield = (AbilityEnergyShield) a;
a.setName("Large Energy Shield");
AbilityShield shield = (AbilityShield) a;
a.setName("Large Shield");
shield.setShieldWidth(4.0f);
shield.setShieldHeight(4.0f);
shield.setBarrierMaxHealth(120.0f);
@@ -1,4 +1,4 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -20,23 +20,23 @@ import java.util.List;
/**
* Energy Slicer ability: Fires a thin, wide blade projectile in a straight line.
* JJK Cleave/Dismantle inspired. Piercing by default.
* Extends AbstractEnergyProjectileAbility to leverage the existing projectile infrastructure.
* Extends AbilityEnergyProjectile to leverage the existing projectile infrastructure.
*/
public class AbilityEnergySlicer extends AbstractEnergyProjectileAbility<EntityEnergySlicer> {
public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
private float sliceWidth = 3.0f;
private float sliceThickness = 0.15f;
private boolean piercing = true;
public AbilityEnergySlicer() {
public AbilitySlicer() {
super(
new EnergyDisplayData(0xFF4444, 0xFF0000, true, 0.2f, 0.6f, 0.0f),
new EnergyCombatData(12.0f, 0.5f, 0.0f, false, 3.0f, 0.5f),
new EnergyHomingData(0.8f, false, 0.0f, 0.0f),
new EnergyLifespanData(40.0f, 100)
);
this.typeId = "ability.cnpc.energy_slicer";
this.name = "Energy Slicer";
this.typeId = "ability.cnpc.slicer";
this.name = "Slicer";
this.targetingMode = TargetingMode.AGGRO_TARGET;
this.maxRange = 30.0f;
this.minRange = 3.0f;
@@ -45,8 +45,8 @@ public class AbilityEnergySlicer extends AbstractEnergyProjectileAbility<EntityE
this.lockMovement = LockMovementType.WINDUP;
this.telegraphType = TelegraphType.LINE;
this.showTelegraph = true;
this.windUpAnimationName = "Ability_EnergySlicer_Windup";
this.activeAnimationName = "Ability_EnergySlicer_Active";
this.windUpAnimationName = "";
this.activeAnimationName = "";
}
// ==================== ABSTRACT IMPLEMENTATIONS ====================
@@ -99,15 +99,15 @@ public class AbilityEnergySlicer extends AbstractEnergyProjectileAbility<EntityE
public List<AbilityVariant> getVariants() {
return Arrays.asList(
new AbilityVariant("ability.variant.single", a -> {
a.setName("Energy Slicer");
a.setName("Slicer");
}),
new AbilityVariant("ability.variant.dual", a -> {
AbilityEnergySlicer slicer = (AbilityEnergySlicer) a;
AbilitySlicer slicer = (AbilitySlicer) a;
a.setName("Double Slicer");
slicer.setProjectileCount(2);
slicer.setFireDelay(5);
a.setWindUpAnimationName("Ability_EnergySlicerDual_Windup");
a.setActiveAnimationName("Ability_EnergySlicerDual_Active");
a.setWindUpAnimationName("");
a.setActiveAnimationName("");
})
);
}
@@ -1,12 +1,10 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
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.LockMovementType;
import kamkeel.npcs.controllers.data.ability.TargetingMode;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import noppes.npcs.client.gui.builder.FieldDef;
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
@@ -14,8 +12,8 @@ import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
import kamkeel.npcs.entity.EntityAbilitySweeper;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.api.ability.type.IAbilitySweeper;
import noppes.npcs.client.gui.builder.FieldDef;
import java.util.Arrays;
import java.util.List;
@@ -28,7 +26,7 @@ import java.util.List;
* All damage logic is handled by the EntityAbilitySweeper entity to ensure
* visual and damage hitbox alignment.
*/
public class AbilitySweeper extends Ability implements IAbilitySweeper {
public class AbilitySweeper extends AbilityEnergy implements IAbilitySweeper {
// Type-specific parameters
private float beamLength = 10.0f;
@@ -41,13 +39,11 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper {
private int numberOfRotations = 2; // How many full rotations before ability ends
private boolean lockOnTarget = false;
// Visual properties
private EnergyDisplayData colorData = new EnergyDisplayData(0xFF6600, 0xFF0000, true, 1.8f, 0.5f, 0.0f);
// Runtime state (transient)
private transient EntityAbilitySweeper activeEntity = null;
public AbilitySweeper() {
super(new EnergyDisplayData(0xFF6600, 0xFF0000, true, 1.8f, 0.5f, 0.0f));
this.typeId = "ability.cnpc.sweeper";
this.name = "Sweeper";
this.targetingMode = TargetingMode.AGGRO_TARGET;
@@ -83,7 +79,7 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper {
// Player: target is null — sweep rotates around caster's facing direction, lockOnTarget has no effect.
activeEntity = new EntityAbilitySweeper(caster.worldObj, caster, target,
beamLength, beamWidth, beamHeight,
colorData,
displayData,
sweepSpeed, numberOfRotations,
damage, damageInterval, piercing,
lockOnTarget);
@@ -93,9 +89,6 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper {
}
spawnAbilityEntity(activeEntity);
// Ability stays active until entity dies (prevents firing another while projectile is alive)
// Movement locking is handled separately by the base class
}
@Override
@@ -162,7 +155,7 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper {
nbt.setFloat("sweepSpeed", sweepSpeed);
nbt.setInteger("numberOfRotations", numberOfRotations);
nbt.setBoolean("lockOnTarget", lockOnTarget);
colorData.writeNBT(nbt);
writeEnergyNBT(nbt);
}
@Override
@@ -176,10 +169,10 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper {
this.sweepSpeed = nbt.getFloat("sweepSpeed");
this.numberOfRotations = nbt.getInteger("numberOfRotations");
this.lockOnTarget = nbt.getBoolean("lockOnTarget");
colorData.readNBT(nbt);
readEnergyNBT(nbt);
}
// Getters & Setters
// Getters & Setters (type-specific only; color/lightning inherited from AbilityEnergy)
public float getBeamLength() { return beamLength; }
public void setBeamLength(float beamLength) { this.beamLength = beamLength; }
public float getBeamWidth() { return beamWidth; }
@@ -198,14 +191,6 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper {
public void setNumberOfRotations(int numberOfRotations) { this.numberOfRotations = numberOfRotations; }
public boolean isLockOnTarget() { return lockOnTarget; }
public void setLockOnTarget(boolean lockOnTarget) { this.lockOnTarget = lockOnTarget; }
public int getInnerColor() { return colorData.innerColor; }
public void setInnerColor(int innerColor) { colorData.innerColor = innerColor; }
public int getOuterColor() { return colorData.outerColor; }
public void setOuterColor(int outerColor) { colorData.outerColor = outerColor; }
public float getOuterColorWidth() { return colorData.outerColorWidth; }
public void setOuterColorWidth(float outerColorWidth) { colorData.outerColorWidth = outerColorWidth; }
public boolean isOuterColorEnabled() { return colorData.outerColorEnabled; }
public void setOuterColorEnabled(boolean outerColorEnabled) { colorData.outerColorEnabled = outerColorEnabled; }
@Override
public int getMaxPreviewDuration() {
@@ -216,7 +201,6 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper {
@Override
public void getAbilityDefinitions(List<FieldDef> defs) {
defs.addAll(Arrays.asList(
FieldDef.row(
FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage),
FieldDef.intField("ability.damageInterval", this::getDamageInterval, this::setDamageInterval)
@@ -237,18 +221,10 @@ public class AbilitySweeper extends Ability implements IAbilitySweeper {
FieldDef.boolField("ability.lockTarget", this::isLockOnTarget, this::setLockOnTarget)
.hover("ability.hover.lockTarget")
),
AbilityFieldDefs.effectsListField("ability.effects", this::getEffects, this::setEffects),
// Visual tab
FieldDef.section("ability.section.colors").tab("ability.tab.visual"),
FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor)
.tab("ability.tab.visual"),
FieldDef.boolField("ability.outerEnabled", this::isOuterColorEnabled, this::setOuterColorEnabled)
.tab("ability.tab.visual"),
FieldDef.colorSubGui("ability.outerColor", this::getOuterColor, this::setOuterColor)
.tab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled),
FieldDef.floatField("ability.outerWidth", this::getOuterColorWidth, this::setOuterColorWidth)
.tab("ability.tab.visual").visibleWhen(this::isOuterColorEnabled)
AbilityFieldDefs.effectsListField("ability.effects", this::getEffects, this::setEffects)
));
// Visual tab - colors + effects (from AbilityEnergy)
addEnergyVisualDefinitions(defs);
}
}
@@ -1,4 +1,4 @@
package kamkeel.npcs.controllers.data.ability.type;
package kamkeel.npcs.controllers.data.ability.type.energy;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -9,6 +9,7 @@ import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.EnergyPanelData;
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
import kamkeel.npcs.entity.EntityEnergyBarrier;
import kamkeel.npcs.entity.EntityEnergyPanel;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
@@ -23,18 +24,18 @@ import java.util.List;
* Can float above ground (configurable height offset).
* Optional launching mode fires the wall forward with damage/knockback.
*/
public class AbilityEnergyWall extends AbstractEnergyBarrierAbility {
public class AbilityWall extends AbilityEnergyBarrier {
private final EnergyPanelData panelData;
public AbilityEnergyWall() {
public AbilityWall() {
super(
new EnergyDisplayData(0x44FFCC, 0x22CCAA, true, 0.3f, 0.4f, 0.0f),
new EnergyDisplayData(0x44FFCC, 0x22CCAA, true, 0.3f, 0.4f, 0.5f, 0.0f),
new EnergyBarrierData(80.0f, true, 160, true)
);
this.panelData = new EnergyPanelData(4.0f, 3.0f, 0.0f);
this.typeId = "ability.cnpc.energy_wall";
this.name = "Energy Wall";
this.typeId = "ability.cnpc.wall";
this.name = "Wall";
this.targetingMode = TargetingMode.AGGRO_TARGET;
this.maxRange = 15.0f;
this.minRange = 2.0f;
@@ -43,8 +44,8 @@ public class AbilityEnergyWall extends AbstractEnergyBarrierAbility {
this.lockMovement = LockMovementType.WINDUP;
this.telegraphType = TelegraphType.LINE;
this.showTelegraph = true;
this.windUpAnimationName = "Ability_EnergyWall_Windup";
this.activeAnimationName = "Ability_EnergyWall_Active";
this.windUpAnimationName = "";
this.activeAnimationName = "";
}
@Override
@@ -55,7 +56,7 @@ public class AbilityEnergyWall extends AbstractEnergyBarrierAbility {
// ==================== ABSTRACT IMPLEMENTATIONS ====================
@Override
protected Entity createBarrierEntity(EntityLivingBase caster, EntityLivingBase target) {
protected EntityEnergyBarrier createBarrierEntity(EntityLivingBase caster, EntityLivingBase target) {
// Place wall between caster and target
double placeX, placeY, placeZ;
float yaw;
@@ -64,12 +65,21 @@ public class AbilityEnergyWall extends AbstractEnergyBarrierAbility {
double dx = target.posX - caster.posX;
double dz = target.posZ - caster.posZ;
double dist = Math.sqrt(dx * dx + dz * dz);
double placeDist = Math.min(dist * 0.5, 5.0);
placeX = caster.posX + (dx / dist) * placeDist;
placeY = caster.posY;
placeZ = caster.posZ + (dz / dist) * placeDist;
yaw = (float) (Math.atan2(-dx, dz) * 180.0 / Math.PI);
if (dist < 0.01) {
// Target at same position — fall through to look-direction placement
float yawRad = (float) Math.toRadians(caster.rotationYaw);
placeX = caster.posX + (-Math.sin(yawRad) * 3.0);
placeY = caster.posY;
placeZ = caster.posZ + (Math.cos(yawRad) * 3.0);
yaw = caster.rotationYaw;
} else {
double placeDist = Math.min(dist * 0.5, 5.0);
placeX = caster.posX + (dx / dist) * placeDist;
placeY = caster.posY;
placeZ = caster.posZ + (dz / dist) * placeDist;
yaw = (float) (Math.atan2(-dx, dz) * 180.0 / Math.PI);
}
} else {
float yawRad = (float) Math.toRadians(caster.rotationYaw);
placeX = caster.posX + (-Math.sin(yawRad) * 3.0);
@@ -105,7 +115,7 @@ public class AbilityEnergyWall extends AbstractEnergyBarrierAbility {
a.setName("Static Wall");
}),
new AbilityVariant("ability.variant.launched", a -> {
AbilityEnergyWall wall = (AbilityEnergyWall) a;
AbilityWall wall = (AbilityWall) a;
a.setName("Launched Wall");
wall.panelData.launching = true;
wall.panelData.launchSpeed = 0.6f;
@@ -25,7 +25,7 @@ import java.util.List;
*
* Design inspired by LouisXIV's energy attack system.
*/
public class EntityAbilityBeam extends EntityAbilityProjectile {
public class EntityAbilityBeam extends EntityEnergyProjectile {
// Beam shape properties
private float beamWidth = 0.3f;
@@ -19,7 +19,7 @@ import java.util.List;
*
* Design inspired by LouisXIV's energy attack system.
*/
public class EntityAbilityDisc extends EntityAbilityProjectile {
public class EntityAbilityDisc extends EntityEnergyProjectile {
// Disc-specific movement propertie
@@ -21,7 +21,7 @@ import java.util.Set;
*
* Design inspired by LouisXIV's energy attack system.
*/
public class EntityAbilityLaser extends EntityAbilityProjectile {
public class EntityAbilityLaser extends EntityEnergyProjectile {
// Laser-specific properties
private float laserWidth = 0.2f;
@@ -15,11 +15,11 @@ import java.util.List;
/**
* Orb projectile - spherical homing energy ball.
* Extends EntityAbilityProjectile for shared functionality.
* Extends EntityEnergyAbility for shared functionality.
*
* Design inspired by LouisXIV's energy attack system.
*/
public class EntityAbilityOrb extends EntityAbilityProjectile {
public class EntityAbilityOrb extends EntityEnergyProjectile {
public EntityAbilityOrb(World world) {
super(world);
@@ -1,9 +1,7 @@
package kamkeel.npcs.entity;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
@@ -24,19 +22,16 @@ import java.util.Set;
* Sweeper ability entity - a rotating beam attached to the NPC.
* Unlike other projectiles, this handles its own damage logic since it's
* attached to and rotates around the caster.
* Extends EntityEnergyAbility for shared visual/owner/charging state.
*
* Design: Similar to Beam trail visuals but rotating around origin.
*/
public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpawnData {
public class EntityAbilitySweeper extends EntityEnergyAbility {
// Beam properties
private float beamLength = 10.0f;
private float beamWidth = 0.3f; // Thin like beam trail
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;
@@ -52,18 +47,13 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
private float baseYaw = 0; // Starting yaw direction
private boolean lockOnTarget = false;
// Owner tracking
private int ownerEntityId = -1;
// Target tracking
private int targetEntityId = -1;
// Lifetime
private int maxTicks = 400;
private long deathWorldTime = -1;
// Preview mode
private boolean previewMode = false;
private EntityLivingBase previewOwner = null;
// Damage state
private transient int ticksSinceDamage = 0;
private transient Set<Integer> hitThisTick = new HashSet<>();
@@ -72,8 +62,6 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
super(world);
this.setSize(0.1f, 0.1f);
this.noClip = true;
this.isImmuneToFire = true;
this.ignoreFrustumCheck = true;
}
public EntityAbilitySweeper(World world, EntityLivingBase owner, EntityLivingBase target,
@@ -89,10 +77,7 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
this.beamLength = beamLength;
this.beamWidth = beamWidth;
this.beamHeight = beamHeight;
this.innerColor = displayData.innerColor;
this.outerColor = displayData.outerColor;
this.outerColorEnabled = displayData.outerColorEnabled;
this.outerColorWidth = displayData.outerColorWidth;
this.displayData = displayData;
this.sweepSpeed = sweepSpeed;
this.numberOfRotations = numberOfRotations;
this.damage = damage;
@@ -121,7 +106,7 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
@Override
protected void entityInit() {
// No DataWatcher needed
super.entityInit();
}
@Override
@@ -144,7 +129,7 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
return;
}
Entity owner = getOwner();
Entity owner = getOwnerEntity();
if (owner != null && owner.isDead) {
this.setDead();
return;
@@ -297,12 +282,6 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
this.previewOwner = owner;
}
private Entity getOwner() {
if (previewMode && previewOwner != null) return previewOwner;
if (ownerEntityId == -1) return null;
return worldObj.getEntityByID(ownerEntityId);
}
// ==================== RENDERING ====================
@Override
@@ -311,22 +290,6 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
return distance < 16384.0D; // 128 blocks squared
}
@Override
public boolean shouldRenderInPass(int pass) {
return pass == 1; // Translucent pass
}
@Override
public float getBrightness(float partialTicks) {
return 1.0f;
}
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float partialTicks) {
return 0xF000F0;
}
// ==================== GETTERS FOR RENDERER ====================
public float getBeamLength() {
@@ -341,22 +304,6 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
return beamHeight;
}
public int getInnerColor() {
return innerColor;
}
public int getOuterColor() {
return outerColor;
}
public boolean isOuterColorEnabled() {
return outerColorEnabled;
}
public float getOuterColorWidth() {
return outerColorWidth;
}
/**
* Get the full interpolated angle INCLUDING base yaw for rendering.
*/
@@ -382,18 +329,14 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
readEnergyBaseNBT(nbt);
this.beamLength = nbt.getFloat("BeamLength");
this.beamWidth = nbt.getFloat("BeamWidth");
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");
this.maxTicks = nbt.getInteger("MaxTicks");
this.ownerEntityId = nbt.getInteger("OwnerId");
this.targetEntityId = nbt.getInteger("TargetId");
this.currentAngle = nbt.getFloat("CurrentAngle");
this.baseYaw = nbt.getFloat("BaseYaw");
@@ -406,18 +349,14 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
writeEnergyBaseNBT(nbt);
nbt.setFloat("BeamLength", beamLength);
nbt.setFloat("BeamWidth", beamWidth);
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);
nbt.setInteger("MaxTicks", maxTicks);
nbt.setInteger("OwnerId", ownerEntityId);
nbt.setInteger("TargetId", targetEntityId);
nbt.setFloat("CurrentAngle", currentAngle);
nbt.setFloat("BaseYaw", baseYaw);
@@ -427,53 +366,4 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
nbt.setBoolean("Piercing", piercing);
nbt.setBoolean("LockOnTarget", lockOnTarget);
}
// ==================== SPAWN DATA ====================
@Override
public void writeSpawnData(ByteBuf buffer) {
buffer.writeFloat(beamLength);
buffer.writeFloat(beamWidth);
buffer.writeFloat(beamHeight);
buffer.writeInt(innerColor);
buffer.writeInt(outerColor);
buffer.writeBoolean(outerColorEnabled);
buffer.writeFloat(outerColorWidth);
buffer.writeFloat(sweepSpeed);
buffer.writeInt(numberOfRotations);
buffer.writeInt(completedRotations);
buffer.writeInt(maxTicks);
buffer.writeInt(ownerEntityId);
buffer.writeInt(targetEntityId);
buffer.writeFloat(currentAngle);
buffer.writeFloat(baseYaw);
buffer.writeFloat(damage);
buffer.writeInt(damageInterval);
buffer.writeBoolean(piercing);
buffer.writeBoolean(lockOnTarget);
}
@Override
public void readSpawnData(ByteBuf buffer) {
this.beamLength = buffer.readFloat();
this.beamWidth = buffer.readFloat();
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();
this.maxTicks = buffer.readInt();
this.ownerEntityId = buffer.readInt();
this.targetEntityId = buffer.readInt();
this.currentAngle = buffer.readFloat();
this.baseYaw = buffer.readFloat();
this.damage = buffer.readFloat();
this.damageInterval = buffer.readInt();
this.piercing = buffer.readBoolean();
this.lockOnTarget = buffer.readBoolean();
this.prevAngle = this.currentAngle;
}
}
@@ -0,0 +1,241 @@
package kamkeel.npcs.entity;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
/**
* Universal base class for all energy ability entities (Projectiles, Barriers, Sweeper).
* Provides shared visual data, owner tracking, charging state, and common entity properties.
*/
public abstract class EntityEnergyAbility extends Entity implements IEntityAdditionalSpawnData {
// ==================== VISUAL PROPERTIES ====================
protected EnergyDisplayData displayData = new EnergyDisplayData();
protected EnergyLightningData lightningData = new EnergyLightningData();
// Client-side lightning state (not saved to NBT)
@SideOnly(Side.CLIENT)
public transient Object lightningState; // Actually AttachedLightningRenderer.LightningState
// ==================== TRACKING ====================
protected int ownerEntityId = -1;
// ==================== STATE ====================
/** The ability that spawned this entity. Transient, not saved to NBT. */
protected transient Ability sourceAbility = null;
protected boolean previewMode = false;
protected EntityLivingBase previewOwner = null;
// ==================== CHARGING STATE ====================
protected boolean charging = false;
protected int chargeDuration = 0;
protected int chargeTick = 0;
protected static final int DW_CHARGING = 20;
public EntityEnergyAbility(World world) {
super(world);
this.isImmuneToFire = true;
this.ignoreFrustumCheck = true;
}
@Override
protected void entityInit() {
this.dataWatcher.addObject(DW_CHARGING, (byte) 0);
}
// ==================== CHARGING ====================
/**
* Check if entity is in charging state (synced via data watcher).
* In preview mode, uses local field since data watcher isn't synced.
*/
public boolean isCharging() {
if (previewMode) return this.charging;
return this.dataWatcher.getWatchableObjectByte(DW_CHARGING) == 1;
}
/**
* Set charging state (server only, synced to clients via data watcher).
*/
protected void setCharging(boolean value) {
this.charging = value;
if (!worldObj.isRemote) {
this.dataWatcher.updateObject(DW_CHARGING, (byte) (value ? 1 : 0));
}
}
public float getChargeProgress() {
if (chargeDuration <= 0) return 1.0f;
return Math.min(1.0f, (float) chargeTick / chargeDuration);
}
public float getInterpolatedChargeProgress(float partialTicks) {
if (chargeDuration <= 0) return 1.0f;
float prevProgress = Math.max(0, (float) (chargeTick - 1) / chargeDuration);
float currProgress = Math.min(1.0f, (float) chargeTick / chargeDuration);
return prevProgress + (currProgress - prevProgress) * partialTicks;
}
// ==================== OWNER ====================
/**
* Get the owner entity. In preview mode, returns the preview owner directly.
*/
public Entity getOwnerEntity() {
if (previewMode && previewOwner != null) {
return previewOwner;
}
if (ownerEntityId == -1) return null;
return worldObj.getEntityByID(ownerEntityId);
}
public int getOwnerEntityId() { return ownerEntityId; }
public void setOwnerEntityId(int id) { this.ownerEntityId = id; }
// ==================== SOURCE ABILITY ====================
public Ability getSourceAbility() { return sourceAbility; }
public void setSourceAbility(Ability ability) { this.sourceAbility = ability; }
// ==================== PREVIEW MODE ====================
public boolean isPreviewMode() { return previewMode; }
public void setPreviewMode(boolean preview) { this.previewMode = preview; }
public void setPreviewOwner(EntityLivingBase owner) { this.previewOwner = owner; }
// ==================== DISPLAY GETTERS & SETTERS ====================
public EnergyDisplayData getDisplayData() { return displayData; }
public EnergyLightningData getLightningData() { return lightningData; }
public int getInnerColor() { return displayData.getInnerColor(); }
public void setInnerColor(int color) { displayData.setInnerColor(color); }
public float getInnerAlpha() { return displayData.getInnerAlpha(); }
public void setInnerAlpha(float alpha) { displayData.setInnerAlpha(alpha); }
public int getOuterColor() { return displayData.getOuterColor(); }
public void setOuterColor(int color) { displayData.setOuterColor(color); }
public boolean isOuterColorEnabled() { return displayData.isOuterColorEnabled(); }
public void setOuterColorEnabled(boolean enabled) { displayData.setOuterColorEnabled(enabled); }
public float getOuterColorWidth() { return displayData.getOuterColorWidth(); }
public void setOuterColorWidth(float width) { displayData.setOuterColorWidth(width); }
public float getOuterColorAlpha() { return displayData.getOuterColorAlpha(); }
public void setOuterColorAlpha(float alpha) { displayData.setOuterColorAlpha(alpha); }
public float getRotationSpeed() { return displayData.getRotationSpeed(); }
public void setRotationSpeed(float speed) { displayData.setRotationSpeed(speed); }
// ==================== LIGHTNING GETTERS & SETTERS ====================
public boolean hasLightningEffect() { return lightningData.isLightningEffect(); }
public void setLightningEffect(boolean enabled) { lightningData.setLightningEffect(enabled); }
public float getLightningDensity() { return lightningData.getLightningDensity(); }
public void setLightningDensity(float density) { lightningData.setLightningDensity(density); }
public float getLightningRadius() { return lightningData.getLightningRadius(); }
public void setLightningRadius(float radius) { lightningData.setLightningRadius(radius); }
public int getLightningFadeTime() { return lightningData.getLightningFadeTime(); }
public void setLightningFadeTime(int ticks) { lightningData.setLightningFadeTime(ticks); }
// ==================== BRIGHTNESS ====================
@Override
public float getBrightness(float partialTicks) {
return 1.0f;
}
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float partialTicks) {
return 0xF000F0;
}
@Override
public boolean shouldRenderInPass(int pass) {
return pass == 1; // Translucent pass
}
// ==================== COLLISION SETTINGS ====================
@Override
public boolean canBePushed() {
return false;
}
@Override
protected boolean canTriggerWalking() {
return false;
}
@Override
public boolean isBurning() {
return false;
}
@SideOnly(Side.CLIENT)
public float getShadowSize() {
return 0.0f;
}
// ==================== NBT HELPERS ====================
/**
* Write shared energy base fields to NBT (owner, display, lightning).
* Call from subclass NBT writers before writing type-specific fields.
*/
protected void writeEnergyBaseNBT(NBTTagCompound nbt) {
nbt.setInteger("OwnerId", ownerEntityId);
displayData.writeNBT(nbt);
lightningData.writeNBT(nbt);
}
/**
* Read shared energy base fields from NBT (owner, display, lightning).
* Call from subclass NBT readers before reading type-specific fields.
*/
protected void readEnergyBaseNBT(NBTTagCompound nbt) {
this.ownerEntityId = nbt.getInteger("OwnerId");
displayData.readNBT(nbt);
lightningData.readNBT(nbt);
}
// ==================== SPAWN DATA ====================
@Override
public void writeSpawnData(ByteBuf buffer) {
try {
NBTTagCompound compound = new NBTTagCompound();
this.writeEntityToNBT(compound);
cpw.mods.fml.common.network.ByteBufUtils.writeTag(buffer, compound);
} catch (Exception e) {
noppes.npcs.LogWriter.error("Error writing energy ability spawn data", e);
}
}
@Override
public void readSpawnData(ByteBuf buffer) {
try {
NBTTagCompound compound = cpw.mods.fml.common.network.ByteBufUtils.readTag(buffer);
if (compound != null) {
this.readEntityFromNBT(compound);
}
} catch (Exception e) {
noppes.npcs.LogWriter.error("Error reading energy ability spawn data", e);
}
}
}
@@ -0,0 +1,269 @@
package kamkeel.npcs.entity;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import noppes.npcs.EventHooks;
import noppes.npcs.entity.EntityNPCInterface;
/**
* Base class for energy barrier entities (Dome, Panel).
* Provides shared health, damage, hit flash, knockback target,
* and barrier lifecycle management.
* Extends EntityEnergyAbility for shared visual/owner/charging state.
*/
public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
// ==================== BARRIER PROPERTIES ====================
protected EnergyBarrierData barrierData = new EnergyBarrierData();
protected float currentHealth;
protected int ticksAlive = 0;
// ==================== DATA WATCHER INDICES ====================
protected static final int DW_HEALTH_PERCENT = 21;
protected static final int DW_HIT_FLASH = 22;
public EntityEnergyBarrier(World world) {
super(world);
this.noClip = true;
}
@Override
protected void entityInit() {
super.entityInit(); // DW_CHARGING = 20
this.dataWatcher.addObject(DW_HEALTH_PERCENT, 1.0f);
this.dataWatcher.addObject(DW_HIT_FLASH, (byte) 0);
}
// ==================== ABSTRACT METHODS ====================
/**
* Check if a projectile is incoming toward this barrier.
* Dome uses spherical check, Panel uses planar check.
*/
public abstract boolean isIncomingProjectile(EntityEnergyProjectile projectile);
/**
* Setup charging state. Called during windup phase.
*/
public abstract void setupCharging(int duration);
/**
* Finish charging — snap to full size and become active.
*/
public abstract void finishCharging();
/**
* Push entities away from the barrier surface.
*/
protected abstract void knockbackEntities();
// ==================== PROJECTILE HIT ====================
/**
* Apply damage to this barrier from a projectile.
* Returns true if the barrier absorbed the hit (projectile should be destroyed).
*/
public boolean onProjectileHit(EntityEnergyProjectile projectile, float baseDamage) {
// Get damage multiplier for this projectile type
String typeId = "";
if (projectile.getSourceAbility() != null) {
typeId = projectile.getSourceAbility().getTypeId();
}
float multiplier = barrierData.getMultiplier(typeId);
float damage = baseDamage * multiplier;
// Fire hit event (may cancel or modify damage)
if (!worldObj.isRemote) {
float eventDamage = EventHooks.onEnergyBarrierHit(this, ownerEntityId, projectile, damage);
if (eventDamage < 0) return false; // Event cancelled — don't block
damage = eventDamage;
}
triggerHitFlash();
if (!barrierData.useHealth) {
return true; // Duration-only mode: block but don't take damage
}
currentHealth -= damage;
syncHealthPercent();
if (currentHealth <= 0) {
onBarrierDestroyed();
this.setDead();
}
return true;
}
// ==================== MELEE DAMAGE ====================
@Override
public boolean canBeCollidedWith() {
return barrierData.meleeEnabled;
}
@Override
public boolean attackEntityFrom(DamageSource source, float amount) {
if (worldObj.isRemote || !barrierData.meleeEnabled) return false;
if (isCharging()) return false;
float damage = amount * barrierData.meleeDamageMultiplier;
// Fire hit event with null projectile (melee hit)
float eventDamage = EventHooks.onEnergyBarrierHit(this, ownerEntityId, null, damage);
if (eventDamage < 0) return false;
damage = eventDamage;
triggerHitFlash();
if (!barrierData.useHealth) return true;
currentHealth -= damage;
syncHealthPercent();
if (currentHealth <= 0) {
onBarrierDestroyed();
this.setDead();
}
return true;
}
// ==================== SHARED UPDATE LOGIC ====================
/**
* Shared barrier tick logic for onUpdate(). Handles spawned event, owner death,
* duration check, hit flash decay, and tick event.
* Call this after incrementing ticksAlive in subclass onUpdate().
* @return true if barrier died this tick (caller should return early)
*/
protected boolean updateBarrierTick() {
if (!worldObj.isRemote) {
// Fire spawned event on first tick
if (ticksAlive == 1) {
EventHooks.onEnergyBarrierSpawned(this, ownerEntityId);
}
// Check owner death
if (ownerEntityId >= 0 && ticksAlive > 5) {
Entity owner = worldObj.getEntityByID(ownerEntityId);
if (owner != null) {
if (owner.isDead) {
onBarrierDestroyed();
this.setDead();
return true;
}
if (owner instanceof EntityNPCInterface && ((EntityNPCInterface) owner).isKilled()) {
onBarrierDestroyed();
this.setDead();
return true;
}
}
}
// Duration check
if (barrierData.useDuration && ticksAlive >= barrierData.durationTicks) {
onBarrierDestroyed();
this.setDead();
return true;
}
// Reset hit flash
if (getHitFlash() > 0) {
setHitFlash((byte) (getHitFlash() - 1));
}
// Fire tick event
EventHooks.onEnergyBarrierTick(this, ownerEntityId);
}
return false;
}
// ==================== HELPERS ====================
protected void onBarrierDestroyed() {
EventHooks.onEnergyBarrierDestroyed(this, ownerEntityId);
}
protected void syncHealthPercent() {
float percent = barrierData.useHealth && barrierData.maxHealth > 0
? Math.max(0, currentHealth / barrierData.maxHealth)
: 1.0f;
if (!worldObj.isRemote) {
this.dataWatcher.updateObject(DW_HEALTH_PERCENT, percent);
}
}
protected void triggerHitFlash() {
if (!worldObj.isRemote) {
setHitFlash((byte) 4);
}
}
protected void setHitFlash(byte value) {
this.dataWatcher.updateObject(DW_HIT_FLASH, value);
}
public byte getHitFlash() {
return this.dataWatcher.getWatchableObjectByte(DW_HIT_FLASH);
}
public float getHealthPercent() {
return this.dataWatcher.getWatchableObjectFloat(DW_HEALTH_PERCENT);
}
protected boolean isKnockbackTarget(EntityLivingBase entity) {
switch (barrierData.knockbackTarget) {
case 1: return entity instanceof EntityPlayer;
case 2: return entity instanceof EntityNPCInterface;
default: return entity instanceof EntityPlayer || entity instanceof EntityNPCInterface;
}
}
// ==================== GETTERS ====================
public EnergyBarrierData getBarrierData() { return barrierData; }
public float getCurrentHealth() { return currentHealth; }
public void setCurrentHealth(float health) { this.currentHealth = health; syncHealthPercent(); }
public int getTicksAlive() { return ticksAlive; }
// ==================== NBT HELPERS ====================
/**
* Write shared barrier base fields to NBT.
* Call from subclass writeEntityToNBT() before writing type-specific fields.
*/
protected void writeBarrierBaseNBT(NBTTagCompound nbt) {
writeEnergyBaseNBT(nbt);
nbt.setInteger("TicksAlive", ticksAlive);
nbt.setFloat("CurrentHealth", currentHealth);
nbt.setBoolean("Charging", charging);
nbt.setInteger("ChargeTick", chargeTick);
nbt.setInteger("ChargeDuration", chargeDuration);
barrierData.writeNBT(nbt);
}
/**
* Read shared barrier base fields from NBT.
* Call from subclass readEntityFromNBT() before reading type-specific fields.
*/
protected void readBarrierBaseNBT(NBTTagCompound nbt) {
readEnergyBaseNBT(nbt);
this.ticksAlive = nbt.getInteger("TicksAlive");
this.currentHealth = nbt.getFloat("CurrentHealth");
this.charging = nbt.hasKey("Charging") && nbt.getBoolean("Charging");
this.chargeTick = nbt.getInteger("ChargeTick");
this.chargeDuration = nbt.getInteger("ChargeDuration");
this.dataWatcher.updateObject(DW_CHARGING, (byte) (charging ? 1 : 0));
barrierData.readNBT(nbt);
}
}
@@ -1,10 +1,7 @@
package kamkeel.npcs.entity;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
@@ -13,7 +10,6 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.World;
import noppes.npcs.entity.EntityNPCInterface;
import java.util.List;
@@ -21,39 +17,18 @@ import java.util.List;
* Energy Dome entity - a spherical barrier that blocks incoming energy projectiles.
* Centered on the caster's position at time of casting.
* Only blocks incoming attacks (not outgoing from allies inside).
* Extends EntityEnergyBarrier for shared barrier logic.
*/
public class EntityEnergyDome extends Entity implements IEntityAdditionalSpawnData {
public class EntityEnergyDome extends EntityEnergyBarrier {
// ==================== VISUAL PROPERTIES ====================
protected EnergyDisplayData displayData = new EnergyDisplayData();
protected EnergyLightningData lightningData = new EnergyLightningData();
// ==================== BARRIER PROPERTIES ====================
protected EnergyBarrierData barrierData = new EnergyBarrierData();
protected float currentHealth;
// ==================== DOME-SPECIFIC PROPERTIES ====================
protected float domeRadius = 5.0f;
// ==================== TRACKING ====================
protected int ownerEntityId = -1;
protected int ticksAlive = 0;
// ==================== STATE ====================
protected transient Ability sourceAbility = null;
// ==================== DATA WATCHER INDICES ====================
private static final int DW_HEALTH_PERCENT = 20;
private static final int DW_HIT_FLASH = 21;
// ==================== CLIENT STATE ====================
@SideOnly(Side.CLIENT)
public transient Object lightningState;
protected float targetDomeRadius = 5.0f;
protected boolean followCaster = false;
public EntityEnergyDome(World world) {
super(world);
this.setSize(1.0f, 1.0f); // Hitbox doesn't really matter, we use radius checks
this.noClip = true;
this.isImmuneToFire = true;
this.ignoreFrustumCheck = true;
}
public EntityEnergyDome(World world, EntityLivingBase owner, double x, double y, double z,
@@ -69,12 +44,6 @@ public class EntityEnergyDome extends Entity implements IEntityAdditionalSpawnDa
this.currentHealth = barrier.maxHealth;
}
@Override
protected void entityInit() {
this.dataWatcher.addObject(DW_HEALTH_PERCENT, 1.0f);
this.dataWatcher.addObject(DW_HIT_FLASH, (byte) 0);
}
@Override
public void onUpdate() {
this.prevPosX = this.posX;
@@ -82,76 +51,51 @@ public class EntityEnergyDome extends Entity implements IEntityAdditionalSpawnDa
this.prevPosZ = this.posZ;
super.onUpdate();
// Follow caster: both sides for smooth interpolated rendering
if (followCaster) {
Entity owner = ownerEntityId >= 0 ? worldObj.getEntityByID(ownerEntityId) : null;
if (owner != null) {
this.setPosition(owner.posX, owner.posY, owner.posZ);
}
}
// Handle charging animation (both sides for smooth rendering)
if (isCharging()) {
chargeTick++;
float progress = getChargeProgress();
this.domeRadius = targetDomeRadius * progress;
return; // Don't tick duration/death during charging
}
ticksAlive++;
if (!worldObj.isRemote) {
// Check owner death
if (ownerEntityId >= 0 && ticksAlive > 5) {
Entity owner = worldObj.getEntityByID(ownerEntityId);
if (owner != null) {
if (owner.isDead) { this.setDead(); return; }
if (owner instanceof EntityNPCInterface && ((EntityNPCInterface) owner).isKilled()) {
this.setDead(); return;
}
}
}
if (updateBarrierTick()) return;
// Duration check
if (barrierData.useDuration && ticksAlive >= barrierData.durationTicks) {
this.setDead();
return;
}
// Reset hit flash
if (getHitFlash() > 0) {
setHitFlash((byte) (getHitFlash() - 1));
}
// Knockback entities inside the dome
if (!worldObj.isRemote && barrierData.knockbackEnabled) {
knockbackEntities();
}
}
/**
* Apply damage to this dome from a projectile.
* Returns true if the dome absorbed the hit (projectile should be destroyed).
*/
public boolean onProjectileHit(EntityAbilityProjectile projectile, float baseDamage) {
if (!barrierData.useHealth) {
// Duration-only mode: block but don't take damage
triggerHitFlash();
return true;
}
// Get damage multiplier for this projectile type
String typeId = "";
if (projectile.getSourceAbility() != null) {
typeId = projectile.getSourceAbility().getTypeId();
}
float multiplier = barrierData.getMultiplier(typeId);
float damage = baseDamage * multiplier;
currentHealth -= damage;
syncHealthPercent();
triggerHitFlash();
if (currentHealth <= 0) {
this.setDead();
}
return true;
}
// ==================== INCOMING CHECK ====================
/**
* Check if a projectile at the given position is entering this dome from outside.
* Only blocks incoming projectiles (dot product check).
*/
public boolean isIncomingProjectile(EntityAbilityProjectile projectile) {
@Override
public boolean isIncomingProjectile(EntityEnergyProjectile projectile) {
// Don't block during charging phase
if (isCharging()) return false;
// Don't block projectiles from the dome's owner
if (projectile.getOwnerEntityId() == this.ownerEntityId) return false;
// Don't block projectiles from same-faction NPCs
Entity owner = getOwnerEntity();
Entity projOwner = projectile.getOwnerEntity();
if (owner instanceof EntityNPCInterface && projOwner instanceof EntityNPCInterface) {
if (((EntityNPCInterface) owner).faction.id == ((EntityNPCInterface) projOwner).faction.id) {
if (owner instanceof noppes.npcs.entity.EntityNPCInterface && projOwner instanceof noppes.npcs.entity.EntityNPCInterface) {
if (((noppes.npcs.entity.EntityNPCInterface) owner).faction.id == ((noppes.npcs.entity.EntityNPCInterface) projOwner).faction.id) {
return false;
}
}
@@ -170,74 +114,77 @@ public class EntityEnergyDome extends Entity implements IEntityAdditionalSpawnDa
return dot < 0;
}
// ==================== HELPERS ====================
// ==================== CHARGING ====================
private void syncHealthPercent() {
float percent = barrierData.useHealth && barrierData.maxHealth > 0
? Math.max(0, currentHealth / barrierData.maxHealth)
: 1.0f;
if (!worldObj.isRemote) {
this.dataWatcher.updateObject(DW_HEALTH_PERCENT, percent);
@Override
public void setupCharging(int duration) {
this.targetDomeRadius = this.domeRadius;
this.domeRadius = 0.01f;
this.chargeDuration = duration;
this.chargeTick = 0;
setCharging(true);
}
@Override
public void finishCharging() {
this.domeRadius = targetDomeRadius;
setCharging(false);
}
// ==================== KNOCKBACK ====================
/**
* Push entities away from the dome surface.
* Entities outside are pushed outward, entities inside are pushed inward (containment).
*/
@Override
@SuppressWarnings("unchecked")
protected void knockbackEntities() {
float margin = 1.0f;
AxisAlignedBB searchBox = AxisAlignedBB.getBoundingBox(
posX - domeRadius - margin, posY - domeRadius - margin, posZ - domeRadius - margin,
posX + domeRadius + margin, posY + domeRadius + margin, posZ + domeRadius + margin
);
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, searchBox);
for (EntityLivingBase ent : entities) {
if (ent.getEntityId() == ownerEntityId) continue;
if (!isKnockbackTarget(ent)) continue;
double dx = ent.posX - posX;
double dy = (ent.posY + ent.height * 0.5) - posY;
double dz = ent.posZ - posZ;
double dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (dist < 0.01) continue;
// Only affect entities near the dome surface
if (dist < domeRadius + margin && dist > domeRadius - margin) {
double pushStrength = barrierData.knockbackStrength * 0.5;
double pushDir = dist >= domeRadius ? 1.0 : -1.0; // Outside = outward, Inside = inward
ent.addVelocity(
(dx / dist) * pushStrength * pushDir,
0.05 * pushDir,
(dz / dist) * pushStrength * pushDir
);
ent.velocityChanged = true;
}
}
}
private void triggerHitFlash() {
if (!worldObj.isRemote) {
setHitFlash((byte) 4);
// ==================== 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;
}
private void setHitFlash(byte value) {
this.dataWatcher.updateObject(DW_HIT_FLASH, value);
}
public byte getHitFlash() {
return this.dataWatcher.getWatchableObjectByte(DW_HIT_FLASH);
}
public float getHealthPercent() {
return this.dataWatcher.getWatchableObjectFloat(DW_HEALTH_PERCENT);
}
public Entity getOwnerEntity() {
if (ownerEntityId == -1) return null;
return worldObj.getEntityByID(ownerEntityId);
}
public int getOwnerEntityId() { return ownerEntityId; }
public float getDomeRadius() { return domeRadius; }
public EnergyDisplayData getDisplayData() { return displayData; }
public EnergyLightningData getLightningData() { return lightningData; }
public EnergyBarrierData getBarrierData() { return barrierData; }
public float getCurrentHealth() { return currentHealth; }
public void setSourceAbility(Ability ability) { this.sourceAbility = ability; }
public Ability getSourceAbility() { return sourceAbility; }
// ==================== VISUAL GETTERS ====================
public int getInnerColor() { return displayData.innerColor; }
public int getOuterColor() { return displayData.outerColor; }
public boolean isOuterColorEnabled() { return displayData.outerColorEnabled; }
public float getOuterColorWidth() { return displayData.outerColorWidth; }
public float getOuterColorAlpha() { return displayData.outerColorAlpha; }
public boolean hasLightningEffect() { return lightningData.lightningEffect; }
public float getLightningDensity() { return lightningData.lightningDensity; }
public float getLightningRadius() { return lightningData.lightningRadius; }
public int getLightningFadeTime() { return lightningData.lightningFadeTime; }
// ==================== BRIGHTNESS ====================
@Override
public float getBrightness(float partialTicks) { return 1.0f; }
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float partialTicks) { return 0xF000F0; }
@Override
public boolean shouldRenderInPass(int pass) { return pass == 1; }
@Override
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double distance) {
@@ -246,69 +193,28 @@ public class EntityEnergyDome extends Entity implements IEntityAdditionalSpawnDa
return distance < d * d;
}
// ==================== COLLISION SETTINGS ====================
// ==================== GETTERS ====================
@Override
public boolean canBeCollidedWith() { return false; }
@Override
public boolean canBePushed() { return false; }
@Override
protected boolean canTriggerWalking() { return false; }
@Override
public boolean isBurning() { return false; }
@SideOnly(Side.CLIENT)
public float getShadowSize() { return 0.0f; }
public float getDomeRadius() { return domeRadius; }
public void setDomeRadius(float radius) { this.domeRadius = Math.max(0.1f, radius); }
public boolean isFollowCaster() { return followCaster; }
public void setFollowCaster(boolean follow) { this.followCaster = follow; }
// ==================== NBT ====================
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
readBarrierBaseNBT(nbt);
this.domeRadius = nbt.getFloat("DomeRadius");
this.ownerEntityId = nbt.getInteger("OwnerId");
this.ticksAlive = nbt.getInteger("TicksAlive");
this.currentHealth = nbt.getFloat("CurrentHealth");
displayData.readNBT(nbt);
lightningData.readNBT(nbt);
barrierData.readNBT(nbt);
this.targetDomeRadius = nbt.hasKey("TargetDomeRadius") ? nbt.getFloat("TargetDomeRadius") : domeRadius;
this.followCaster = nbt.hasKey("FollowCaster") && nbt.getBoolean("FollowCaster");
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
writeBarrierBaseNBT(nbt);
nbt.setFloat("DomeRadius", domeRadius);
nbt.setInteger("OwnerId", ownerEntityId);
nbt.setInteger("TicksAlive", ticksAlive);
nbt.setFloat("CurrentHealth", currentHealth);
displayData.writeNBT(nbt);
lightningData.writeNBT(nbt);
barrierData.writeNBT(nbt);
}
// ==================== SPAWN DATA ====================
@Override
public void writeSpawnData(ByteBuf buffer) {
try {
NBTTagCompound compound = new NBTTagCompound();
this.writeEntityToNBT(compound);
cpw.mods.fml.common.network.ByteBufUtils.writeTag(buffer, compound);
} catch (Exception e) {
noppes.npcs.LogWriter.error("Error writing energy dome spawn data", e);
}
}
@Override
public void readSpawnData(ByteBuf buffer) {
try {
NBTTagCompound compound = cpw.mods.fml.common.network.ByteBufUtils.readTag(buffer);
if (compound != null) {
this.readEntityFromNBT(compound);
}
} catch (Exception e) {
noppes.npcs.LogWriter.error("Error reading energy dome spawn data", e);
}
nbt.setFloat("TargetDomeRadius", targetDomeRadius);
nbt.setBoolean("FollowCaster", followCaster);
}
}
@@ -1,31 +1,29 @@
package kamkeel.npcs.entity;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
import kamkeel.npcs.controllers.data.ability.data.EnergyPanelData;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import noppes.npcs.NpcDamageSource;
import noppes.npcs.entity.EntityNPCInterface;
import net.minecraft.entity.player.EntityPlayer;
import java.util.List;
/**
* Energy Panel entity - a flat rectangular barrier used by Wall and Shield abilities.
* Supports three modes: PLACED (stationary), HELD (tracks caster), LAUNCHED (moves forward).
* Extends EntityEnergyBarrier for shared barrier logic.
*/
public class EntityEnergyPanel extends Entity implements IEntityAdditionalSpawnData {
public class EntityEnergyPanel extends EntityEnergyBarrier {
public enum PanelMode {
PLACED, // Stationary wall
@@ -33,40 +31,18 @@ public class EntityEnergyPanel extends Entity implements IEntityAdditionalSpawnD
LAUNCHED // Moves forward, deals damage/knockback on contact
}
// ==================== VISUAL PROPERTIES ====================
protected EnergyDisplayData displayData = new EnergyDisplayData();
protected EnergyLightningData lightningData = new EnergyLightningData();
// ==================== BARRIER PROPERTIES ====================
protected EnergyBarrierData barrierData = new EnergyBarrierData();
// ==================== PANEL-SPECIFIC PROPERTIES ====================
protected EnergyPanelData panelData = new EnergyPanelData();
protected float currentHealth;
// ==================== PANEL STATE ====================
protected PanelMode mode = PanelMode.PLACED;
protected float panelYaw = 0.0f; // Rotation of the panel face (degrees)
// ==================== TRACKING ====================
protected int ownerEntityId = -1;
protected int ticksAlive = 0;
// ==================== STATE ====================
protected transient Ability sourceAbility = null;
// ==================== DATA WATCHER INDICES ====================
private static final int DW_HEALTH_PERCENT = 20;
private static final int DW_HIT_FLASH = 21;
// ==================== CLIENT STATE ====================
@SideOnly(Side.CLIENT)
public transient Object lightningState;
// ==================== CHARGING (panel-specific targets) ====================
protected float targetPanelWidth;
protected float targetPanelHeight;
public EntityEnergyPanel(World world) {
super(world);
this.setSize(0.5f, 0.5f);
this.noClip = true;
this.isImmuneToFire = true;
this.ignoreFrustumCheck = true;
}
public EntityEnergyPanel(World world, EntityLivingBase owner, double x, double y, double z,
@@ -93,12 +69,6 @@ public class EntityEnergyPanel extends Entity implements IEntityAdditionalSpawnD
}
}
@Override
protected void entityInit() {
this.dataWatcher.addObject(DW_HEALTH_PERCENT, 1.0f);
this.dataWatcher.addObject(DW_HIT_FLASH, (byte) 0);
}
@Override
public void onUpdate() {
this.prevPosX = this.posX;
@@ -106,30 +76,28 @@ public class EntityEnergyPanel extends Entity implements IEntityAdditionalSpawnD
this.prevPosZ = this.posZ;
super.onUpdate();
// Handle charging animation (both sides for smooth rendering)
if (isCharging()) {
chargeTick++;
float progress = getChargeProgress();
panelData.panelWidth = targetPanelWidth * progress;
panelData.panelHeight = targetPanelHeight * progress;
// During charging, held panels still track owner
if (mode == PanelMode.HELD) {
updateHeld();
}
return; // Don't tick duration/death during charging
}
ticksAlive++;
if (!worldObj.isRemote) {
// Check owner death
if (ownerEntityId >= 0 && ticksAlive > 5) {
Entity owner = worldObj.getEntityByID(ownerEntityId);
if (owner != null) {
if (owner.isDead) { this.setDead(); return; }
if (owner instanceof EntityNPCInterface && ((EntityNPCInterface) owner).isKilled()) {
this.setDead(); return;
}
}
}
if (updateBarrierTick()) return;
// Duration check
if (barrierData.useDuration && ticksAlive >= barrierData.durationTicks) {
this.setDead();
return;
}
// Reset hit flash
if (getHitFlash() > 0) {
setHitFlash((byte) (getHitFlash() - 1));
}
// Knockback (not during launched mode)
if (!worldObj.isRemote && barrierData.knockbackEnabled && mode != PanelMode.LAUNCHED) {
knockbackEntities();
}
// Mode-specific updates
@@ -151,108 +119,90 @@ public class EntityEnergyPanel extends Entity implements IEntityAdditionalSpawnD
Entity owner = getOwnerEntity();
if (owner == null) return;
// Update yaw BEFORE position calc to avoid 1-tick lag
this.panelYaw = owner.rotationYaw;
// Follow owner position
float frontDist = 1.5f;
float yawRad = (float) Math.toRadians(owner.rotationYaw);
float yawRad = (float) Math.toRadians(panelYaw);
double newX = owner.posX + (-Math.sin(yawRad) * frontDist);
double newY = owner.posY + panelData.heightOffset + (owner.height * 0.5f);
double newZ = owner.posZ + (Math.cos(yawRad) * frontDist);
this.setPosition(newX, newY, newZ);
this.panelYaw = owner.rotationYaw;
}
private void updateLaunched() {
if (worldObj.isRemote) return;
// Move forward
// Move forward on both sides for smooth client rendering
this.posX += motionX;
this.posY += motionY;
this.posZ += motionZ;
this.setPosition(posX, posY, posZ);
// Check entity collision for damage
float halfW = panelData.panelWidth * 0.5f;
float halfH = panelData.panelHeight * 0.5f;
AxisAlignedBB hitBox = AxisAlignedBB.getBoundingBox(
posX - halfW, posY - halfH, posZ - halfW,
posX + halfW, posY + halfH, posZ + halfW
);
// Damage and knockback are server-only
if (!worldObj.isRemote) {
// Check entity collision for damage
float halfW = panelData.panelWidth * 0.5f;
float halfH = panelData.panelHeight * 0.5f;
float halfD = 0.5f; // Panel collision depth
float searchRadius = Math.max(halfW, halfD);
AxisAlignedBB hitBox = AxisAlignedBB.getBoundingBox(
posX - searchRadius, posY - halfH, posZ - searchRadius,
posX + searchRadius, posY + halfH, posZ + searchRadius
);
@SuppressWarnings("unchecked")
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, hitBox);
Entity owner = getOwnerEntity();
@SuppressWarnings("unchecked")
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, hitBox);
Entity owner = getOwnerEntity();
for (EntityLivingBase target : entities) {
if (target == owner) continue;
if (owner instanceof EntityNPCInterface && target instanceof EntityNPCInterface) {
if (((EntityNPCInterface) owner).faction.id == ((EntityNPCInterface) target).faction.id) continue;
}
for (EntityLivingBase target : entities) {
if (target == owner) continue;
if (owner instanceof EntityNPCInterface && target instanceof EntityNPCInterface) {
if (((EntityNPCInterface) owner).faction.id == ((EntityNPCInterface) target).faction.id) continue;
}
// Apply damage
if (panelData.launchDamage > 0) {
if (owner instanceof EntityNPCInterface) {
target.attackEntityFrom(new NpcDamageSource("npc_ability", (EntityNPCInterface) owner), panelData.launchDamage);
} else if (owner instanceof EntityPlayer) {
target.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) owner), panelData.launchDamage);
// Apply damage
if (panelData.launchDamage > 0) {
if (owner instanceof EntityNPCInterface) {
target.attackEntityFrom(new NpcDamageSource("npc_ability", (EntityNPCInterface) owner), panelData.launchDamage);
} else if (owner instanceof EntityPlayer) {
target.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) owner), panelData.launchDamage);
}
}
// Apply knockback
if (panelData.launchKnockback > 0) {
double dx = target.posX - posX;
double dz = target.posZ - posZ;
double len = Math.sqrt(dx * dx + dz * dz);
if (len > 0) {
target.addVelocity(
(dx / len) * panelData.launchKnockback * 0.5,
0.1,
(dz / len) * panelData.launchKnockback * 0.5
);
target.velocityChanged = true;
}
}
}
// Apply knockback
if (panelData.launchKnockback > 0) {
double dx = target.posX - posX;
double dz = target.posZ - posZ;
double len = Math.sqrt(dx * dx + dz * dz);
if (len > 0) {
target.addVelocity(
(dx / len) * panelData.launchKnockback * 0.5,
0.1,
(dz / len) * panelData.launchKnockback * 0.5
);
target.velocityChanged = true;
}
// Max lifetime check
if (ticksAlive > 200) {
this.setDead();
}
}
// Max distance check (30 blocks)
double distSq = (posX - prevPosX) * (posX - prevPosX) + (posZ - prevPosZ) * (posZ - prevPosZ);
if (ticksAlive > 200) {
this.setDead();
}
}
/**
* Apply damage to this panel from a projectile.
*/
public boolean onProjectileHit(EntityAbilityProjectile projectile, float baseDamage) {
if (!barrierData.useHealth) {
triggerHitFlash();
return true;
}
String typeId = "";
if (projectile.getSourceAbility() != null) {
typeId = projectile.getSourceAbility().getTypeId();
}
float multiplier = barrierData.getMultiplier(typeId);
float damage = baseDamage * multiplier;
currentHealth -= damage;
syncHealthPercent();
triggerHitFlash();
if (currentHealth <= 0) {
this.setDead();
}
return true;
}
// ==================== INCOMING CHECK ====================
/**
* Check if a projectile hits this panel surface.
* Only blocks incoming projectiles (not from the panel's owner).
*/
public boolean isIncomingProjectile(EntityAbilityProjectile projectile) {
@Override
public boolean isIncomingProjectile(EntityEnergyProjectile projectile) {
// Don't block during charging phase
if (isCharging()) return false;
if (projectile.getOwnerEntityId() == this.ownerEntityId) return false;
// Faction check
@@ -292,85 +242,97 @@ public class EntityEnergyPanel extends Entity implements IEntityAdditionalSpawnD
double dot = projectile.motionX * normalX + projectile.motionZ * normalZ;
// Determine which side of the panel the projectile is on
// If on the front (localForward > 0), it must be moving backward (dot < 0) to be incoming
// If on the back (localForward < 0), it must be moving forward (dot > 0) to be incoming
if (localForward > 0 && dot >= 0) return false; // Moving away from front side
if (localForward < 0 && dot <= 0) return false; // Moving away from back side
return true;
}
// ==================== HELPERS ====================
// ==================== CHARGING ====================
private void syncHealthPercent() {
float percent = barrierData.useHealth && barrierData.maxHealth > 0
? Math.max(0, currentHealth / barrierData.maxHealth)
: 1.0f;
if (!worldObj.isRemote) {
this.dataWatcher.updateObject(DW_HEALTH_PERCENT, percent);
@Override
public void setupCharging(int duration) {
this.targetPanelWidth = panelData.panelWidth;
this.targetPanelHeight = panelData.panelHeight;
panelData.panelWidth = 0.01f;
panelData.panelHeight = 0.01f;
this.chargeDuration = duration;
this.chargeTick = 0;
setCharging(true);
}
@Override
public void finishCharging() {
panelData.panelWidth = targetPanelWidth;
panelData.panelHeight = targetPanelHeight;
setCharging(false);
}
// ==================== KNOCKBACK ====================
/**
* Push entities away from the panel surface.
*/
@Override
@SuppressWarnings("unchecked")
protected void knockbackEntities() {
float halfW = panelData.panelWidth * 0.5f;
float halfH = panelData.panelHeight * 0.5f;
float margin = 1.0f;
AxisAlignedBB searchBox = AxisAlignedBB.getBoundingBox(
posX - halfW - margin, posY - halfH - margin, posZ - halfW - margin,
posX + halfW + margin, posY + halfH + margin, posZ + halfW + margin
);
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, searchBox);
float yawRad = (float) Math.toRadians(panelYaw);
float normalX = -(float) Math.sin(yawRad);
float normalZ = (float) Math.cos(yawRad);
for (EntityLivingBase ent : entities) {
if (ent.getEntityId() == ownerEntityId) continue;
if (!isKnockbackTarget(ent)) continue;
// Transform entity position to panel-local space
double dx = ent.posX - posX;
double dy = (ent.posY + ent.height * 0.5) - posY;
double dz = ent.posZ - posZ;
// Check proximity to panel surface
float localForward = (float) (dx * normalX + dz * normalZ);
float localRight = (float) (dx * (-normalZ) + dz * normalX);
if (Math.abs(localForward) < margin && Math.abs(localRight) < halfW + margin && Math.abs(dy) < halfH + margin) {
// Push entity away from the panel face
float pushDir = localForward >= 0 ? 1.0f : -1.0f;
double pushStrength = barrierData.knockbackStrength * 0.5;
ent.addVelocity(
normalX * pushDir * pushStrength,
0.1,
normalZ * pushDir * pushStrength
);
ent.velocityChanged = true;
}
}
}
private void triggerHitFlash() {
if (!worldObj.isRemote) {
setHitFlash((byte) 4);
// ==================== BOUNDING BOX ====================
@Override
public AxisAlignedBB getBoundingBox() {
if (barrierData.meleeEnabled) {
float halfW = panelData.panelWidth * 0.5f;
float halfH = panelData.panelHeight * 0.5f;
float extent = Math.max(halfW, 0.5f);
return AxisAlignedBB.getBoundingBox(
posX - extent, posY - halfH, posZ - extent,
posX + extent, posY + halfH, posZ + extent
);
}
return null;
}
private void setHitFlash(byte value) {
this.dataWatcher.updateObject(DW_HIT_FLASH, value);
}
public byte getHitFlash() {
return this.dataWatcher.getWatchableObjectByte(DW_HIT_FLASH);
}
public float getHealthPercent() {
return this.dataWatcher.getWatchableObjectFloat(DW_HEALTH_PERCENT);
}
public Entity getOwnerEntity() {
if (ownerEntityId == -1) return null;
return worldObj.getEntityByID(ownerEntityId);
}
// ==================== GETTERS ====================
public int getOwnerEntityId() { return ownerEntityId; }
public PanelMode getMode() { return mode; }
public float getPanelYaw() { return panelYaw; }
public EnergyDisplayData getDisplayData() { return displayData; }
public EnergyLightningData getLightningData() { return lightningData; }
public EnergyBarrierData getBarrierData() { return barrierData; }
public EnergyPanelData getPanelData() { return panelData; }
public float getCurrentHealth() { return currentHealth; }
public void setSourceAbility(Ability ability) { this.sourceAbility = ability; }
public Ability getSourceAbility() { return sourceAbility; }
// Visual getters
public int getInnerColor() { return displayData.innerColor; }
public int getOuterColor() { return displayData.outerColor; }
public boolean isOuterColorEnabled() { return displayData.outerColorEnabled; }
public float getOuterColorWidth() { return displayData.outerColorWidth; }
public float getOuterColorAlpha() { return displayData.outerColorAlpha; }
public boolean hasLightningEffect() { return lightningData.lightningEffect; }
public float getLightningDensity() { return lightningData.lightningDensity; }
public float getLightningRadius() { return lightningData.lightningRadius; }
public int getLightningFadeTime() { return lightningData.lightningFadeTime; }
// ==================== BRIGHTNESS ====================
@Override
public float getBrightness(float partialTicks) { return 1.0f; }
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float partialTicks) { return 0xF000F0; }
@Override
public boolean shouldRenderInPass(int pass) { return pass == 1; }
@Override
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double distance) {
@@ -379,73 +341,33 @@ public class EntityEnergyPanel extends Entity implements IEntityAdditionalSpawnD
return distance < d * d;
}
// ==================== COLLISION SETTINGS ====================
// ==================== GETTERS ====================
@Override
public boolean canBeCollidedWith() { return false; }
@Override
public boolean canBePushed() { return false; }
@Override
protected boolean canTriggerWalking() { return false; }
@Override
public boolean isBurning() { return false; }
@SideOnly(Side.CLIENT)
public float getShadowSize() { return 0.0f; }
public PanelMode getMode() { return mode; }
public float getPanelYaw() { return panelYaw; }
public EnergyPanelData getPanelData() { return panelData; }
// ==================== NBT ====================
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
this.ownerEntityId = nbt.getInteger("OwnerId");
this.ticksAlive = nbt.getInteger("TicksAlive");
this.currentHealth = nbt.getFloat("CurrentHealth");
readBarrierBaseNBT(nbt);
this.panelYaw = nbt.getFloat("PanelYaw");
this.mode = PanelMode.values()[nbt.getInteger("PanelMode")];
displayData.readNBT(nbt);
lightningData.readNBT(nbt);
barrierData.readNBT(nbt);
int modeOrdinal = nbt.getInteger("PanelMode");
this.mode = (modeOrdinal >= 0 && modeOrdinal < PanelMode.values().length)
? PanelMode.values()[modeOrdinal] : PanelMode.PLACED;
panelData.readNBT(nbt);
this.targetPanelWidth = nbt.hasKey("TargetPanelWidth") ? nbt.getFloat("TargetPanelWidth") : panelData.panelWidth;
this.targetPanelHeight = nbt.hasKey("TargetPanelHeight") ? nbt.getFloat("TargetPanelHeight") : panelData.panelHeight;
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
nbt.setInteger("OwnerId", ownerEntityId);
nbt.setInteger("TicksAlive", ticksAlive);
nbt.setFloat("CurrentHealth", currentHealth);
writeBarrierBaseNBT(nbt);
nbt.setFloat("PanelYaw", panelYaw);
nbt.setInteger("PanelMode", mode.ordinal());
displayData.writeNBT(nbt);
lightningData.writeNBT(nbt);
barrierData.writeNBT(nbt);
nbt.setFloat("TargetPanelWidth", targetPanelWidth);
nbt.setFloat("TargetPanelHeight", targetPanelHeight);
panelData.writeNBT(nbt);
}
// ==================== SPAWN DATA ====================
@Override
public void writeSpawnData(ByteBuf buffer) {
try {
NBTTagCompound compound = new NBTTagCompound();
this.writeEntityToNBT(compound);
cpw.mods.fml.common.network.ByteBufUtils.writeTag(buffer, compound);
} catch (Exception e) {
noppes.npcs.LogWriter.error("Error writing energy panel spawn data", e);
}
}
@Override
public void readSpawnData(ByteBuf buffer) {
try {
NBTTagCompound compound = cpw.mods.fml.common.network.ByteBufUtils.readTag(buffer);
if (compound != null) {
this.readEntityFromNBT(compound);
}
} catch (Exception e) {
noppes.npcs.LogWriter.error("Error reading energy panel spawn data", e);
}
}
}
@@ -1,14 +1,10 @@
package kamkeel.npcs.entity;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.AbilityController;
import kamkeel.npcs.controllers.data.ability.AbilityEffect;
import kamkeel.npcs.controllers.data.ability.AnchorPoint;
import kamkeel.npcs.controllers.data.ability.data.*;
import kamkeel.npcs.util.AnchorPointHelper;
import net.minecraft.entity.Entity;
@@ -35,20 +31,21 @@ import java.util.List;
import java.util.Map;
/**
* Base class for all ability projectiles (Orb, Disc, Beam, Laser).
* Provides common functionality for visuals, combat, effects, and interpolation.
* Base class for all ability projectiles (Orb, Disc, Beam, Laser, Slicer).
* Provides common functionality for combat, effects, homing, and interpolation.
* Extends EntityEnergyAbility for shared visual/owner/charging state.
*
* Design inspired by LouisXIV's energy attack system.
*/
public abstract class EntityAbilityProjectile extends Entity implements IEntityAdditionalSpawnData {
public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
// ==================== ACTIVE PROJECTILE TRACKING ====================
private static final Map<Integer, List<WeakReference<EntityAbilityProjectile>>> activeProjectiles = new HashMap<>();
private static final Map<Integer, List<WeakReference<EntityEnergyProjectile>>> activeProjectiles = new HashMap<>();
public static void trackProjectile(EntityAbilityProjectile projectile) {
public static void trackProjectile(EntityEnergyProjectile projectile) {
int ownerId = projectile.getOwnerEntityId();
if (ownerId < 0) return;
List<WeakReference<EntityAbilityProjectile>> refs = activeProjectiles.get(ownerId);
List<WeakReference<EntityEnergyProjectile>> refs = activeProjectiles.get(ownerId);
if (refs == null) {
refs = new ArrayList<>();
activeProjectiles.put(ownerId, refs);
@@ -56,13 +53,13 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
refs.add(new WeakReference<>(projectile));
}
public static List<EntityAbilityProjectile> getActiveProjectiles(int ownerEntityId) {
List<WeakReference<EntityAbilityProjectile>> refs = activeProjectiles.get(ownerEntityId);
public static List<EntityEnergyProjectile> getActiveProjectiles(int ownerEntityId) {
List<WeakReference<EntityEnergyProjectile>> refs = activeProjectiles.get(ownerEntityId);
if (refs == null) return Collections.emptyList();
List<EntityAbilityProjectile> result = new ArrayList<>();
Iterator<WeakReference<EntityAbilityProjectile>> it = refs.iterator();
List<EntityEnergyProjectile> result = new ArrayList<>();
Iterator<WeakReference<EntityEnergyProjectile>> it = refs.iterator();
while (it.hasNext()) {
EntityAbilityProjectile p = it.next().get();
EntityEnergyProjectile p = it.next().get();
if (p == null || p.isDead) {
it.remove();
} else {
@@ -82,16 +79,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
// ==================== EFFECT PROPERTIES ====================
protected List<AbilityEffect> effects = new ArrayList<>();
// ==================== LIGHTNING EFFECT PROPERTIES ====================
protected EnergyLightningData lightningData = new EnergyLightningData();
// Client-side lightning state (not saved to NBT)
@cpw.mods.fml.relauncher.SideOnly(cpw.mods.fml.relauncher.Side.CLIENT)
public transient Object lightningState; // Actually AttachedLightningRenderer.LightningState
// ==================== DISPLAY PROPERTIES ====================
protected EnergyDisplayData displayData = new EnergyDisplayData();
// ==================== ANCHOR PROPERTIES ====================
protected EnergyAnchorData anchorData = new EnergyAnchorData(AnchorPoint.FRONT);
@@ -108,23 +95,13 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
// ==================== TRACKING ====================
protected double startX, startY, startZ;
protected int ownerEntityId = -1;
protected int targetEntityId = -1;
// ==================== STATE ====================
protected boolean hasHit = false;
protected boolean previewMode = false; // Client-side preview mode (no damage/effects)
protected EntityLivingBase previewOwner = null; // Direct reference for GUI preview (no world lookup)
/** The ability that spawned this projectile. Transient, not saved to NBT. */
protected transient Ability sourceAbility = null;
// ==================== CHARGING STATE ====================
protected boolean charging = false;
protected int chargeDuration = 40;
protected int chargeTick = 0;
// ==================== CHARGING STATE (projectile-specific) ====================
protected float targetSize = 1.0f;
protected static final int DW_CHARGING = 20;
// ==================== ROTATION INTERPOLATION ====================
public float prevRotationValX, prevRotationValY, prevRotationValZ;
@@ -139,12 +116,10 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
protected double interpTargetMotionX, interpTargetMotionY, interpTargetMotionZ;
protected int interpSteps;
public EntityAbilityProjectile(World world) {
public EntityEnergyProjectile(World world) {
super(world);
this.setSize(0.5f, 0.5f);
this.noClip = false;
this.isImmuneToFire = true;
this.ignoreFrustumCheck = true;
}
/**
@@ -177,11 +152,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
this.prevRenderSize = size;
}
@Override
protected void entityInit() {
this.dataWatcher.addObject(DW_CHARGING, (byte) 0);
}
@Override
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double distance) {
@@ -190,11 +160,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
return distance < d1 * d1;
}
@Override
public boolean shouldRenderInPass(int pass) {
return pass == 1; // Translucent pass
}
@Override
public void onUpdate() {
// Store previous values for interpolation
@@ -320,39 +285,24 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
// ==================== BARRIER COLLISION ====================
/**
* Check for collision with energy barrier entities (Dome, Panel).
* Check for collision with energy barrier entities.
* Barriers only block incoming projectiles from enemies.
* @return true if projectile was absorbed (caller should stop processing)
*/
protected boolean checkBarrierCollision() {
// Search area for barriers - check a generous range
double searchRange = 10.0;
// Search area for barriers - must cover large domes (up to 30 radius)
double searchRange = 32.0;
AxisAlignedBB searchBox = AxisAlignedBB.getBoundingBox(
posX - searchRange, posY - searchRange, posZ - searchRange,
posX + searchRange, posY + searchRange, posZ + searchRange
);
// Check domes
@SuppressWarnings("unchecked")
List<EntityEnergyDome> domes = worldObj.getEntitiesWithinAABB(EntityEnergyDome.class, searchBox);
for (EntityEnergyDome dome : domes) {
if (dome.isDead) continue;
if (dome.isIncomingProjectile(this)) {
if (dome.onProjectileHit(this, getDamage())) {
hasHit = true;
this.setDead();
return true;
}
}
}
// Check panels
@SuppressWarnings("unchecked")
List<EntityEnergyPanel> panels = worldObj.getEntitiesWithinAABB(EntityEnergyPanel.class, searchBox);
for (EntityEnergyPanel panel : panels) {
if (panel.isDead) continue;
if (panel.isIncomingProjectile(this)) {
if (panel.onProjectileHit(this, getDamage())) {
List<EntityEnergyBarrier> barriers = worldObj.getEntitiesWithinAABB(EntityEnergyBarrier.class, searchBox);
for (EntityEnergyBarrier barrier : barriers) {
if (barrier.isDead) continue;
if (barrier.isIncomingProjectile(this)) {
if (barrier.onProjectileHit(this, getDamage())) {
hasHit = true;
this.setDead();
return true;
@@ -406,23 +356,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
}
}
// ==================== PREVIEW MODE ====================
/**
* Set preview mode for GUI display.
* In preview mode, no damage or world effects are applied.
*/
public void setPreviewMode(boolean preview) {
this.previewMode = preview;
}
/**
* Check if entity is in preview mode.
*/
public boolean isPreviewMode() {
return previewMode;
}
// ==================== DAMAGE & EFFECTS ====================
protected void applyDamage(EntityLivingBase target) {
@@ -495,21 +428,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
this.effects = effects != null ? effects : new ArrayList<>();
}
/**
* Set the source ability that spawned this projectile.
* Used by external damage handlers to access ability-specific data.
*/
public void setSourceAbility(Ability ability) {
this.sourceAbility = ability;
}
/**
* Get the source ability that spawned this projectile.
*/
public Ability getSourceAbility() {
return sourceAbility;
}
protected void doExplosion() {
if (previewMode) return; // Skip explosion in preview mode
worldObj.playSoundEffect(posX, posY, posZ, "random.explode", 1.0f, 1.0f);
@@ -566,38 +484,7 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
prevPosZ = newZ;
}
// ==================== CHARGING METHODS ====================
/**
* Check if entity is in charging state (synced via data watcher).
* In preview mode, uses local field since data watcher isn't synced.
*/
public boolean isCharging() {
if (previewMode) return this.charging;
return this.dataWatcher.getWatchableObjectByte(DW_CHARGING) == 1;
}
/**
* Set charging state (server only, synced to clients via data watcher).
*/
protected void setCharging(boolean value) {
this.charging = value;
if (!worldObj.isRemote) {
this.dataWatcher.updateObject(DW_CHARGING, (byte) (value ? 1 : 0));
}
}
public float getChargeProgress() {
if (chargeDuration <= 0) return 1.0f;
return Math.min(1.0f, (float) chargeTick / chargeDuration);
}
public float getInterpolatedChargeProgress(float partialTicks) {
if (chargeDuration <= 0) return 1.0f;
float prevProgress = Math.max(0, (float) (chargeTick - 1) / chargeDuration);
float currProgress = Math.min(1.0f, (float) chargeTick / chargeDuration);
return prevProgress + (currProgress - prevProgress) * partialTicks;
}
// ==================== CHARGING METHODS (projectile-specific) ====================
/**
* Setup this entity in charging mode (for windup phase).
@@ -730,37 +617,12 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
// ==================== ENTITY HELPERS ====================
public Entity getOwnerEntity() {
// In preview mode, use direct reference (no world lookup)
if (previewMode && previewOwner != null) {
return previewOwner;
}
if (ownerEntityId == -1) return null;
return worldObj.getEntityByID(ownerEntityId);
}
public int getOwnerEntityId() {
return ownerEntityId;
}
public void setOwnerEntityId(int id) {
this.ownerEntityId = id;
}
public IEntity getOwner() {
if (previewMode) return null;
if (getOwnerEntity() == null) return null;
return NpcAPI.Instance().getIEntity(getOwnerEntity());
}
/**
* Set the preview owner for GUI preview mode.
* This allows anchor point calculations without world entity lookup.
*/
public void setPreviewOwner(EntityLivingBase owner) {
this.previewOwner = owner;
}
public int getTargetEntityId() {
return targetEntityId;
}
@@ -794,8 +656,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
}
// Ignore other projectiles from the same caster (e.g. multi-projectile abilities)
if (entity instanceof EntityAbilityProjectile) {
EntityAbilityProjectile other = (EntityAbilityProjectile) entity;
if (entity instanceof EntityEnergyProjectile) {
EntityEnergyProjectile other = (EntityEnergyProjectile) entity;
if (other.ownerEntityId == this.ownerEntityId) return true;
}
@@ -821,24 +683,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
this.startZ = z;
}
public int getInnerColor() { return displayData.getInnerColor(); }
public void setInnerColor(int color) { displayData.setInnerColor(color); }
public int getOuterColor() { return displayData.getOuterColor(); }
public void setOuterColor(int color) { displayData.setOuterColor(color); }
public boolean isOuterColorEnabled() { return displayData.isOuterColorEnabled(); }
public void setOuterColorEnabled(boolean enabled) { displayData.setOuterColorEnabled(enabled); }
public float getOuterColorWidth() { return displayData.getOuterColorWidth(); }
public void setOuterColorWidth(float width) { displayData.setOuterColorWidth(width); }
public float getOuterColorAlpha() { return displayData.getOuterColorAlpha(); }
public void setOuterColorAlpha(float alpha) { displayData.setOuterColorAlpha(alpha); }
public float getRotationSpeed() { return displayData.getRotationSpeed(); }
public void setRotationSpeed(float speed) { displayData.setRotationSpeed(speed); }
// ==================== ROTATION GETTERS ====================
public float getInterpolatedRotationX(float partialTicks) {
@@ -857,20 +701,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
return this.prevRenderSize + (this.renderCurrentSize - this.prevRenderSize) * partialTicks;
}
// ==================== LIGHTNING GETTERS & SETTERS ====================
public boolean hasLightningEffect() { return lightningData.isLightningEffect(); }
public void setLightningEffect(boolean enabled) { lightningData.setLightningEffect(enabled); }
public float getLightningDensity() { return lightningData.getLightningDensity(); }
public void setLightningDensity(float density) { lightningData.setLightningDensity(density); }
public float getLightningRadius() { return lightningData.getLightningRadius(); }
public void setLightningRadius(float radius) { lightningData.setLightningRadius(radius); }
public int getLightningFadeTime() { return lightningData.getLightningFadeTime(); }
public void setLightningFadeTime(int ticks) { lightningData.setLightningFadeTime(ticks); }
// ==================== LIFESPAN GETTERS & SETTERS ====================
public float getMaxDistance() { return lifespanData.getMaxDistance(); }
@@ -932,19 +762,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
public boolean getHasHit() { return hasHit; }
// ==================== BRIGHTNESS ====================
@Override
public float getBrightness(float partialTicks) {
return 1.0f;
}
@Override
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float partialTicks) {
return 0xF000F0;
}
// ==================== NBT ====================
@Override
@@ -963,6 +780,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
* Read base projectile properties from NBT.
*/
protected void readBaseNBT(NBTTagCompound nbt) {
readEnergyBaseNBT(nbt);
this.size = nbt.getFloat("Size");
// Read effects list
@@ -980,7 +799,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
this.startY = nbt.getDouble("StartY");
this.startZ = nbt.getDouble("StartZ");
this.ownerEntityId = nbt.getInteger("OwnerId");
this.targetEntityId = nbt.getInteger("TargetId");
this.motionX = nbt.getDouble("MotionX");
@@ -994,10 +812,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
anchorData.readNBT(nbt);
combatData.readNBT(nbt);
displayData.readNBT(nbt);
homingData.readNBT(nbt);
lifespanData.readNBT(nbt);
lightningData.readNBT(nbt);
trajectoryData.readNBT(nbt);
}
@@ -1005,6 +821,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
* Write base projectile properties to NBT.
*/
protected void writeBaseNBT(NBTTagCompound nbt) {
writeEnergyBaseNBT(nbt);
nbt.setFloat("Size", size);
// Write effects list
@@ -1022,7 +840,6 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
nbt.setDouble("StartY", startY);
nbt.setDouble("StartZ", startZ);
nbt.setInteger("OwnerId", ownerEntityId);
nbt.setInteger("TargetId", targetEntityId);
nbt.setDouble("MotionX", motionX);
@@ -1031,10 +848,8 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
anchorData.writeNBT(nbt);
combatData.writeNBT(nbt);
displayData.writeNBT(nbt);
homingData.writeNBT(nbt);
lifespanData.writeNBT(nbt);
lightningData.writeNBT(nbt);
trajectoryData.writeNBT(nbt);
}
@@ -1048,55 +863,10 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
*/
protected abstract void writeProjectileNBT(NBTTagCompound nbt);
// ==================== SPAWN DATA ====================
@Override
public void writeSpawnData(ByteBuf buffer) {
try {
NBTTagCompound compound = new NBTTagCompound();
this.writeEntityToNBT(compound);
cpw.mods.fml.common.network.ByteBufUtils.writeTag(buffer, compound);
} catch (Exception e) {
noppes.npcs.LogWriter.error("Error writing ability projectile spawn data", e);
}
}
@Override
public void readSpawnData(ByteBuf buffer) {
try {
NBTTagCompound compound = cpw.mods.fml.common.network.ByteBufUtils.readTag(buffer);
if (compound != null) {
this.readEntityFromNBT(compound);
}
} catch (Exception e) {
noppes.npcs.LogWriter.error("Error reading ability projectile spawn data", e);
}
}
// ==================== COLLISION SETTINGS ====================
@Override
public boolean canBeCollidedWith() {
return false;
}
@Override
public boolean canBePushed() {
return false;
}
@Override
protected boolean canTriggerWalking() {
return false;
}
@Override
public boolean isBurning() {
return false;
}
@SideOnly(Side.CLIENT)
public float getShadowSize() {
return 0.0f;
}
}
@@ -16,9 +16,9 @@ import java.util.List;
/**
* Energy Slicer entity - a thin, wide blade projectile that flies in a straight line.
* JJK Cleave/Dismantle inspired. Piercing by default (passes through entities).
* Extends EntityAbilityProjectile to leverage the existing projectile infrastructure.
* Extends EntityEnergyAbility to leverage the existing projectile infrastructure.
*/
public class EntityEnergySlicer extends EntityAbilityProjectile {
public class EntityEnergySlicer extends EntityEnergyProjectile {
private float sliceWidth = 3.0f;
private float sliceThickness = 0.15f;
@@ -50,17 +50,6 @@ public class EntityEnergySlicer extends EntityAbilityProjectile {
calculateInitialVelocity(owner, target, x, y, z);
}
/**
* Setup in charging mode (windup phase).
*/
public void setupCharging(EnergyAnchorData anchor, int chargeDuration, float width, float thickness) {
this.sliceWidth = 0.01f;
this.sliceThickness = 0.01f;
super.setupCharging(anchor, chargeDuration);
// Store target dimensions
this.targetSize = width; // Re-use base field for width
}
// Targets stored during charging for grow animation
private float targetSliceWidth = 3.0f;
private float targetSliceThickness = 0.15f;
+54 -6
View File
@@ -1,7 +1,7 @@
package noppes.npcs;
import cpw.mods.fml.common.eventhandler.Event;
import kamkeel.npcs.entity.EntityAbilityProjectile;
import kamkeel.npcs.entity.EntityEnergyProjectile;
import kamkeel.npcs.network.packets.data.AchievementPacket;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.Entity;
@@ -455,7 +455,7 @@ public class EventHooks {
// ==================== ENERGY PROJECTILE EVENTS ====================
public static void onEnergyProjectileFired(EntityAbilityProjectile projectile) {
public static void onEnergyProjectileFired(EntityEnergyProjectile projectile) {
IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile);
EnergyProjectileEvent.FiredEvent event = new EnergyProjectileEvent.FiredEvent(wrapped);
ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ENERGY_PROJECTILE_FIRED, event);
@@ -473,7 +473,7 @@ public class EventHooks {
NpcAPI.EVENT_BUS.post(event);
}
public static void onEnergyProjectileTick(EntityAbilityProjectile projectile) {
public static void onEnergyProjectileTick(EntityEnergyProjectile projectile) {
IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile);
EnergyProjectileEvent.UpdateEvent event = new EnergyProjectileEvent.UpdateEvent(wrapped);
ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ENERGY_PROJECTILE_TICK, event);
@@ -494,7 +494,7 @@ public class EventHooks {
/**
* Fires the entity impact event. Returns the (possibly modified) damage, or -1 if cancelled.
*/
public static float onEnergyProjectileEntityImpact(EntityAbilityProjectile projectile, EntityLivingBase target, float damage) {
public static float onEnergyProjectileEntityImpact(EntityEnergyProjectile projectile, EntityLivingBase target, float damage) {
IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile);
IEntity wrappedTarget = NpcAPI.Instance().getIEntity(target);
EnergyProjectileEvent.EntityImpactEvent event = new EnergyProjectileEvent.EntityImpactEvent(wrapped, wrappedTarget, damage);
@@ -518,7 +518,7 @@ public class EventHooks {
return event.getDamage();
}
public static void onEnergyProjectileBlockImpact(EntityAbilityProjectile projectile, int blockX, int blockY, int blockZ) {
public static void onEnergyProjectileBlockImpact(EntityEnergyProjectile projectile, int blockX, int blockY, int blockZ) {
IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile);
IPos pos = NpcAPI.Instance().getIPos(blockX, blockY, blockZ);
IWorld world = NpcAPI.Instance().getIWorld(projectile.worldObj);
@@ -539,7 +539,7 @@ public class EventHooks {
NpcAPI.EVENT_BUS.post(event);
}
public static void onEnergyProjectileExpired(EntityAbilityProjectile projectile) {
public static void onEnergyProjectileExpired(EntityEnergyProjectile projectile) {
IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile);
EnergyProjectileEvent.ExpiredEvent event = new EnergyProjectileEvent.ExpiredEvent(wrapped);
ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ENERGY_PROJECTILE_EXPIRED, event);
@@ -557,6 +557,54 @@ public class EventHooks {
NpcAPI.EVENT_BUS.post(event);
}
// ==================== ENERGY BARRIER EVENTS ====================
private static void dispatchBarrierEvent(net.minecraft.entity.Entity barrierEntity, int ownerEntityId,
noppes.npcs.scripted.event.EnergyBarrierEvent event, EnumScriptType type) {
ScriptController.Instance.globalNpcScripts.callScript(type, event);
net.minecraft.entity.Entity owner = barrierEntity.worldObj.getEntityByID(ownerEntityId);
if (owner instanceof EntityNPCInterface) {
((EntityNPCInterface) owner).script.callScript(type, event);
} else if (owner instanceof EntityPlayer) {
PlayerDataScript handler = ScriptController.Instance.getPlayerScripts((EntityPlayer) owner);
if (handler != null) {
handler.callScript(type, event);
}
}
NpcAPI.EVENT_BUS.post(event);
}
public static void onEnergyBarrierSpawned(net.minecraft.entity.Entity barrierEntity, int ownerEntityId) {
noppes.npcs.api.entity.IEnergyBarrier wrapped = (noppes.npcs.api.entity.IEnergyBarrier) NpcAPI.Instance().getIEntity(barrierEntity);
noppes.npcs.scripted.event.EnergyBarrierEvent.SpawnedEvent event = new noppes.npcs.scripted.event.EnergyBarrierEvent.SpawnedEvent(wrapped);
dispatchBarrierEvent(barrierEntity, ownerEntityId, event, EnumScriptType.ENERGY_BARRIER_SPAWNED);
}
public static void onEnergyBarrierTick(net.minecraft.entity.Entity barrierEntity, int ownerEntityId) {
noppes.npcs.api.entity.IEnergyBarrier wrapped = (noppes.npcs.api.entity.IEnergyBarrier) NpcAPI.Instance().getIEntity(barrierEntity);
noppes.npcs.scripted.event.EnergyBarrierEvent.UpdateEvent event = new noppes.npcs.scripted.event.EnergyBarrierEvent.UpdateEvent(wrapped);
dispatchBarrierEvent(barrierEntity, ownerEntityId, event, EnumScriptType.ENERGY_BARRIER_TICK);
}
public static float onEnergyBarrierHit(net.minecraft.entity.Entity barrierEntity, int ownerEntityId,
EntityEnergyProjectile projectile, float damage) {
noppes.npcs.api.entity.IEnergyBarrier wrappedBarrier = (noppes.npcs.api.entity.IEnergyBarrier) NpcAPI.Instance().getIEntity(barrierEntity);
noppes.npcs.api.entity.IEnergyProjectile wrappedProjectile = projectile != null
? (noppes.npcs.api.entity.IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile) : null;
noppes.npcs.scripted.event.EnergyBarrierEvent.HitEvent event = new noppes.npcs.scripted.event.EnergyBarrierEvent.HitEvent(wrappedBarrier, wrappedProjectile, damage);
dispatchBarrierEvent(barrierEntity, ownerEntityId, event, EnumScriptType.ENERGY_BARRIER_HIT);
if (event.isCanceled()) return -1;
return event.getDamage();
}
public static void onEnergyBarrierDestroyed(net.minecraft.entity.Entity barrierEntity, int ownerEntityId) {
noppes.npcs.api.entity.IEnergyBarrier wrapped = (noppes.npcs.api.entity.IEnergyBarrier) NpcAPI.Instance().getIEntity(barrierEntity);
noppes.npcs.scripted.event.EnergyBarrierEvent.DestroyedEvent event = new noppes.npcs.scripted.event.EnergyBarrierEvent.DestroyedEvent(wrapped);
dispatchBarrierEvent(barrierEntity, ownerEntityId, event, EnumScriptType.ENERGY_BARRIER_DESTROYED);
}
public static void onPlayerInit(PlayerDataScript handler, IPlayer player) {
PlayerEvent.InitEvent event = new PlayerEvent.InitEvent(player);
handler.callScript(EnumScriptType.INIT, event);
@@ -284,12 +284,12 @@ public class GuiFieldBuilder {
String btnText = def.getButtonLabel();
int btnW;
if (def.hasClearAction()) {
btnW = contentRight - 22 - fieldX;
GuiNpcButton clearBtn = new GuiNpcButton(clearId, contentRight - 20, y, 20, 20, "X");
btnW = fieldW - 22;
GuiNpcButton clearBtn = new GuiNpcButton(clearId, fieldX + fieldW - 20, y, 20, 20, "X");
sw.addButton(clearBtn);
clearFieldMap.put(clearId, def);
} else {
btnW = contentRight - fieldX;
btnW = fieldW;
}
GuiNpcButton btn = new GuiNpcButton(widgetId, fieldX, y, btnW, 20, btnText);
Integer textColor = def.getButtonTextColor();
@@ -131,7 +131,13 @@ public enum EnumScriptType {
ENERGY_PROJECTILE_TICK("energyProjectileTick"),
ENERGY_PROJECTILE_ENTITY_IMPACT("energyProjectileEntityImpact"),
ENERGY_PROJECTILE_BLOCK_IMPACT("energyProjectileBlockImpact"),
ENERGY_PROJECTILE_EXPIRED("energyProjectileExpired");
ENERGY_PROJECTILE_EXPIRED("energyProjectileExpired"),
// Energy Barrier Events
ENERGY_BARRIER_SPAWNED("energyBarrierSpawned"),
ENERGY_BARRIER_TICK("energyBarrierTick"),
ENERGY_BARRIER_HIT("energyBarrierHit"),
ENERGY_BARRIER_DESTROYED("energyBarrierDestroyed");
public String function;
@@ -11,6 +11,7 @@ import kamkeel.npcs.entity.EntityAbilityBeam;
import kamkeel.npcs.entity.EntityAbilityDisc;
import kamkeel.npcs.entity.EntityAbilityLaser;
import kamkeel.npcs.entity.EntityAbilityOrb;
import kamkeel.npcs.entity.EntityEnergyProjectile;
import net.minecraft.block.Block;
import net.minecraft.command.CommandHandler;
import net.minecraft.entity.Entity;
@@ -127,6 +128,7 @@ import noppes.npcs.scripted.entity.ScriptEnergyBeam;
import noppes.npcs.scripted.entity.ScriptEnergyDisc;
import noppes.npcs.scripted.entity.ScriptEnergyLaser;
import noppes.npcs.scripted.entity.ScriptEnergyOrb;
import noppes.npcs.scripted.entity.ScriptEnergyProjectile;
import noppes.npcs.scripted.entity.ScriptEntity;
import noppes.npcs.scripted.entity.ScriptEntityItem;
import noppes.npcs.scripted.entity.ScriptFishHook;
@@ -470,6 +472,14 @@ public class NpcAPI extends AbstractNpcAPI {
data = new ScriptEntityData(new ScriptEnergyDisc<>((EntityAbilityDisc) entity));
else if (entity instanceof EntityAbilityLaser)
data = new ScriptEntityData(new ScriptEnergyLaser<>((EntityAbilityLaser) entity));
else if (entity instanceof kamkeel.npcs.entity.EntityEnergySlicer)
data = new ScriptEntityData(new noppes.npcs.scripted.entity.ScriptEnergySlicer<>((kamkeel.npcs.entity.EntityEnergySlicer) entity));
else if (entity instanceof EntityEnergyProjectile)
data = new ScriptEntityData(new ScriptEnergyProjectile((EntityEnergyProjectile) entity));
else if (entity instanceof kamkeel.npcs.entity.EntityEnergyDome)
data = new ScriptEntityData(new noppes.npcs.scripted.entity.ScriptEnergyDome<>((kamkeel.npcs.entity.EntityEnergyDome) entity));
else if (entity instanceof kamkeel.npcs.entity.EntityEnergyPanel)
data = new ScriptEntityData(new noppes.npcs.scripted.entity.ScriptEnergyPanel<>((kamkeel.npcs.entity.EntityEnergyPanel) entity));
else
data = new ScriptEntityData(new ScriptEntity<>(entity));
entity.registerExtendedProperties("ScriptedObject", data);
@@ -19,4 +19,8 @@ public class EntityType {
public static final int ENERGY_BEAM = 15;
public static final int ENERGY_DISC = 16;
public static final int ENERGY_LASER = 17;
public static final int ENERGY_SLICER = 18;
public static final int ENERGY_BARRIER = 19;
public static final int ENERGY_DOME = 20;
public static final int ENERGY_PANEL = 21;
}
@@ -0,0 +1,68 @@
package noppes.npcs.scripted.entity;
import kamkeel.npcs.entity.EntityEnergyAbility;
import net.minecraft.entity.Entity;
import noppes.npcs.api.entity.IEnergyAbility;
import noppes.npcs.api.entity.IEntity;
import noppes.npcs.scripted.NpcAPI;
/**
* Base script wrapper for all energy ability entities.
* Implements shared display, lightning, owner, and charging methods.
*/
public abstract class ScriptEnergyAbility<T extends EntityEnergyAbility> extends ScriptEntity<T> implements IEnergyAbility {
public ScriptEnergyAbility(T entity) {
super(entity);
}
// ==================== OWNER ====================
public int getOwnerEntityId() {
return entity.getOwnerEntityId();
}
public IEntity getOwner() {
Entity owner = entity.getOwnerEntity();
return owner != null ? NpcAPI.Instance().getIEntity(owner) : null;
}
// ==================== DISPLAY ====================
public int getInnerColor() { return entity.getInnerColor(); }
public void setInnerColor(int color) { entity.setInnerColor(color); }
public float getInnerAlpha() { return entity.getInnerAlpha(); }
public void setInnerAlpha(float alpha) { entity.setInnerAlpha(alpha); }
public int getOuterColor() { return entity.getOuterColor(); }
public void setOuterColor(int color) { entity.setOuterColor(color); }
public boolean isOuterColorEnabled() { return entity.isOuterColorEnabled(); }
public void setOuterColorEnabled(boolean enabled) { entity.setOuterColorEnabled(enabled); }
public float getOuterColorWidth() { return entity.getOuterColorWidth(); }
public void setOuterColorWidth(float width) { entity.setOuterColorWidth(width); }
public float getOuterColorAlpha() { return entity.getOuterColorAlpha(); }
public void setOuterColorAlpha(float alpha) { entity.setOuterColorAlpha(alpha); }
// ==================== LIGHTNING ====================
public boolean hasLightningEffect() { return entity.hasLightningEffect(); }
public void setLightningEffect(boolean enabled) { entity.setLightningEffect(enabled); }
public float getLightningDensity() { return entity.getLightningDensity(); }
public void setLightningDensity(float density) { entity.setLightningDensity(density); }
public float getLightningRadius() { return entity.getLightningRadius(); }
public void setLightningRadius(float radius) { entity.setLightningRadius(radius); }
public int getLightningFadeTime() { return entity.getLightningFadeTime(); }
public void setLightningFadeTime(int ticks) { entity.setLightningFadeTime(ticks); }
// ==================== STATE ====================
public boolean isCharging() { return entity.isCharging(); }
public float getChargeProgress() { return entity.getChargeProgress(); }
}
@@ -0,0 +1,99 @@
package noppes.npcs.scripted.entity;
import kamkeel.npcs.entity.EntityEnergyBarrier;
import kamkeel.npcs.entity.EntityEnergyDome;
import noppes.npcs.api.entity.IEnergyBarrier;
import noppes.npcs.scripted.constants.EntityType;
/**
* Base script wrapper for energy barrier entities (Dome, Panel).
* All shared methods delegate directly to EntityEnergyBarrier base.
*/
public class ScriptEnergyBarrier<T extends EntityEnergyBarrier> extends ScriptEnergyAbility<T> implements IEnergyBarrier {
public ScriptEnergyBarrier(T entity) {
super(entity);
}
@Override
public int getType() {
return EntityType.ENERGY_BARRIER;
}
@Override
public boolean typeOf(int type) {
return type == EntityType.ENERGY_BARRIER || super.typeOf(type);
}
// ==================== HEALTH ====================
public float getCurrentHealth() {
return entity.getCurrentHealth();
}
public void setCurrentHealth(float health) {
entity.setCurrentHealth(health);
}
public float getHealthPercent() {
return entity.getHealthPercent();
}
public float getMaxHealth() {
return entity.getBarrierData().maxHealth;
}
public void setMaxHealth(float maxHealth) {
entity.getBarrierData().setMaxHealth(maxHealth);
}
public boolean isUseHealth() {
return entity.getBarrierData().useHealth;
}
public void setUseHealth(boolean useHealth) {
entity.getBarrierData().useHealth = useHealth;
}
// ==================== DURATION ====================
public int getDuration() {
return entity.getBarrierData().durationTicks;
}
public void setDuration(int ticks) {
entity.getBarrierData().setDurationTicks(ticks);
}
public boolean isUseDuration() {
return entity.getBarrierData().useDuration;
}
public void setUseDuration(boolean useDuration) {
entity.getBarrierData().useDuration = useDuration;
}
// ==================== STATE (BARRIER-SPECIFIC) ====================
public int getTicksAlive() {
return entity.getTicksAlive();
}
// ==================== BARRIER ====================
public float getDefaultMultiplier() { return entity.getBarrierData().defaultMultiplier; }
public void setDefaultMultiplier(float multiplier) { entity.getBarrierData().defaultMultiplier = multiplier; }
public boolean isKnockbackEnabled() { return entity.getBarrierData().knockbackEnabled; }
public void setKnockbackEnabled(boolean enabled) { entity.getBarrierData().knockbackEnabled = enabled; }
public float getKnockbackStrength() { return entity.getBarrierData().knockbackStrength; }
public void setKnockbackStrength(float strength) { entity.getBarrierData().knockbackStrength = strength; }
public int getKnockbackTarget() { return entity.getBarrierData().knockbackTarget; }
public void setKnockbackTarget(int target) { entity.getBarrierData().knockbackTarget = target; }
public int getBarrierType() {
return entity instanceof EntityEnergyDome ? 0 : 1;
}
}
@@ -0,0 +1,32 @@
package noppes.npcs.scripted.entity;
import kamkeel.npcs.entity.EntityEnergyDome;
import noppes.npcs.api.entity.IEnergyDome;
import noppes.npcs.scripted.constants.EntityType;
public class ScriptEnergyDome<T extends EntityEnergyDome> extends ScriptEnergyBarrier<T> implements IEnergyDome {
public ScriptEnergyDome(T entity) {
super(entity);
}
@Override
public int getType() {
return EntityType.ENERGY_DOME;
}
@Override
public boolean typeOf(int type) {
return type == EntityType.ENERGY_DOME || super.typeOf(type);
}
@Override
public int getBarrierType() {
return 0;
}
// ==================== DOME-SPECIFIC ====================
public float getDomeRadius() { return entity.getDomeRadius(); }
public void setDomeRadius(float radius) { entity.setDomeRadius(radius); }
}
@@ -0,0 +1,41 @@
package noppes.npcs.scripted.entity;
import kamkeel.npcs.entity.EntityEnergyPanel;
import noppes.npcs.api.entity.IEnergyPanel;
import noppes.npcs.scripted.constants.EntityType;
public class ScriptEnergyPanel<T extends EntityEnergyPanel> extends ScriptEnergyBarrier<T> implements IEnergyPanel {
public ScriptEnergyPanel(T entity) {
super(entity);
}
@Override
public int getType() {
return EntityType.ENERGY_PANEL;
}
@Override
public boolean typeOf(int type) {
return type == EntityType.ENERGY_PANEL || super.typeOf(type);
}
@Override
public int getBarrierType() {
return 1;
}
// ==================== PANEL-SPECIFIC ====================
public float getPanelWidth() { return entity.getPanelData().panelWidth; }
public void setPanelWidth(float width) { entity.getPanelData().setPanelWidth(width); }
public float getPanelHeight() { return entity.getPanelData().panelHeight; }
public void setPanelHeight(float height) { entity.getPanelData().setPanelHeight(height); }
public float getPanelYaw() { return entity.getPanelYaw(); }
public int getPanelMode() { return entity.getMode().ordinal(); }
public boolean isLaunched() { return entity.getMode() == EntityEnergyPanel.PanelMode.LAUNCHED; }
}
@@ -1,21 +1,18 @@
package noppes.npcs.scripted.entity;
import kamkeel.npcs.entity.EntityAbilityProjectile;
import kamkeel.npcs.entity.EntityEnergyProjectile;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import noppes.npcs.api.entity.IEnergyProjectile;
import noppes.npcs.api.entity.IEntity;
import noppes.npcs.api.entity.IEntityLivingBase;
import noppes.npcs.EventHooks;
import noppes.npcs.scripted.NpcAPI;
import noppes.npcs.scripted.constants.EntityType;
public class ScriptEnergyProjectile<T extends EntityAbilityProjectile> extends ScriptEntity<T> implements IEnergyProjectile {
protected T entity;
public class ScriptEnergyProjectile<T extends EntityEnergyProjectile> extends ScriptEnergyAbility<T> implements IEnergyProjectile {
public ScriptEnergyProjectile(T entity) {
super(entity);
this.entity = entity;
}
// ==================== TYPE ====================
@@ -36,14 +33,6 @@ public class ScriptEnergyProjectile<T extends EntityAbilityProjectile> extends S
// ==================== OWNER & TARGET ====================
public int getOwnerEntityId() {
return entity.getOwnerEntityId();
}
public IEntity getOwner() {
return entity.getOwner();
}
public void setOwner(IEntity owner) {
if (owner != null) {
entity.setOwnerEntityId(owner.getMCEntity().getEntityId());
@@ -76,22 +65,7 @@ public class ScriptEnergyProjectile<T extends EntityAbilityProjectile> extends S
entity.setProjectileSize(size);
}
// ==================== DISPLAY ====================
public int getInnerColor() { return entity.getInnerColor(); }
public void setInnerColor(int color) { entity.setInnerColor(color); }
public int getOuterColor() { return entity.getOuterColor(); }
public void setOuterColor(int color) { entity.setOuterColor(color); }
public boolean isOuterColorEnabled() { return entity.isOuterColorEnabled(); }
public void setOuterColorEnabled(boolean enabled) { entity.setOuterColorEnabled(enabled); }
public float getOuterColorWidth() { return entity.getOuterColorWidth(); }
public void setOuterColorWidth(float width) { entity.setOuterColorWidth(width); }
public float getOuterColorAlpha() { return entity.getOuterColorAlpha(); }
public void setOuterColorAlpha(float alpha) { entity.setOuterColorAlpha(alpha); }
// ==================== DISPLAY (PROJECTILE-SPECIFIC) ====================
public float getRotationSpeed() { return entity.getRotationSpeed(); }
public void setRotationSpeed(float speed) { entity.setRotationSpeed(speed); }
@@ -103,20 +77,6 @@ public class ScriptEnergyProjectile<T extends EntityAbilityProjectile> extends S
public float getInterpolatedRotationZ(float partialTicks) { return entity.getInterpolatedRotationZ(partialTicks); }
public float getInterpolatedSize(float partialTicks) { return entity.getInterpolatedSize(partialTicks); }
// ==================== LIGHTNING ====================
public boolean hasLightningEffect() { return entity.hasLightningEffect(); }
public void setLightningEffect(boolean enabled) { entity.setLightningEffect(enabled); }
public float getLightningDensity() { return entity.getLightningDensity(); }
public void setLightningDensity(float density) { entity.setLightningDensity(density); }
public float getLightningRadius() { return entity.getLightningRadius(); }
public void setLightningRadius(float radius) { entity.setLightningRadius(radius); }
public int getLightningFadeTime() { return entity.getLightningFadeTime(); }
public void setLightningFadeTime(int ticks) { entity.setLightningFadeTime(ticks); }
// ==================== LIFESPAN ====================
public float getMaxDistance() { return entity.getMaxDistance(); }
@@ -175,8 +135,6 @@ public class ScriptEnergyProjectile<T extends EntityAbilityProjectile> extends S
// ==================== STATE ====================
public boolean hasHit() { return entity.getHasHit(); }
public boolean isCharging() { return entity.isCharging(); }
public float getChargeProgress() { return entity.getChargeProgress(); }
// ==================== FIRE ====================
@@ -0,0 +1,67 @@
package noppes.npcs.scripted.entity;
import kamkeel.npcs.entity.EntityEnergySlicer;
import net.minecraft.entity.EntityLivingBase;
import noppes.npcs.api.entity.IEnergySlicer;
import noppes.npcs.api.entity.IEntity;
import noppes.npcs.scripted.constants.EntityType;
public class ScriptEnergySlicer<T extends EntityEnergySlicer> extends ScriptEnergyProjectile<T> implements IEnergySlicer {
public ScriptEnergySlicer(T entity) {
super(entity);
}
@Override
public int getType() {
return EntityType.ENERGY_SLICER;
}
@Override
public boolean typeOf(int type) {
return type == EntityType.ENERGY_SLICER || super.typeOf(type);
}
@Override
public int getEnergyType() {
return 4;
}
// ==================== SLICER-SPECIFIC ====================
public float getSliceWidth() { return entity.getSliceWidth(); }
public void setSliceWidth(float width) { entity.setSliceWidth(width); }
public float getSliceThickness() { return entity.getSliceThickness(); }
public void setSliceThickness(float thickness) { entity.setSliceThickness(thickness); }
public boolean isPiercing() { return entity.isPiercing(); }
public void setPiercing(boolean piercing) { entity.setPiercing(piercing); }
// ==================== FIRE ====================
@Override
public void fire(IEntity target) {
ensureSpawned();
EntityLivingBase mcTarget = null;
if (target != null && target.getMCEntity() instanceof EntityLivingBase) {
mcTarget = (EntityLivingBase) target.getMCEntity();
}
entity.startMoving(mcTarget);
}
@Override
public void fire(double x, double y, double z) {
ensureSpawned();
double dx = x - entity.posX;
double dy = y - entity.posY;
double dz = z - entity.posZ;
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (len > 0) {
entity.motionX = (dx / len) * entity.getSpeed();
entity.motionY = (dy / len) * entity.getSpeed();
entity.motionZ = (dz / len) * entity.getSpeed();
}
entity.startMoving(null);
}
}
@@ -4,7 +4,7 @@ import kamkeel.npcs.entity.EntityAbilityBeam;
import kamkeel.npcs.entity.EntityAbilityDisc;
import kamkeel.npcs.entity.EntityAbilityLaser;
import kamkeel.npcs.entity.EntityAbilityOrb;
import kamkeel.npcs.entity.EntityAbilityProjectile;
import kamkeel.npcs.entity.EntityEnergyProjectile;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
@@ -532,7 +532,7 @@ public class ScriptEntity<T extends Entity> implements IEntity {
else if (type == EntityType.NPC)
cls = EntityNPCInterface.class;
else if (type == EntityType.ENERGY_PROJECTILE)
cls = EntityAbilityProjectile.class;
cls = EntityEnergyProjectile.class;
else if (type == EntityType.ENERGY_ORB)
cls = EntityAbilityOrb.class;
else if (type == EntityType.ENERGY_BEAM)
@@ -29,7 +29,7 @@ import noppes.npcs.constants.EnumMovingType;
import noppes.npcs.constants.EnumNavType;
import noppes.npcs.constants.EnumRoleType;
import noppes.npcs.constants.EnumStandingType;
import kamkeel.npcs.entity.EntityAbilityProjectile;
import kamkeel.npcs.entity.EntityEnergyProjectile;
import noppes.npcs.controllers.FactionController;
import noppes.npcs.controllers.data.DialogOption;
import noppes.npcs.controllers.data.Line;
@@ -1467,7 +1467,7 @@ public class ScriptNpc<T extends EntityNPCInterface> extends ScriptLiving<T> imp
}
public IEnergyProjectile[] getActiveEnergyProjectiles() {
java.util.List<EntityAbilityProjectile> entities = EntityAbilityProjectile.getActiveProjectiles(npc.getEntityId());
java.util.List<EntityEnergyProjectile> entities = EntityEnergyProjectile.getActiveProjectiles(npc.getEntityId());
IEnergyProjectile[] result = new IEnergyProjectile[entities.size()];
for (int i = 0; i < entities.size(); i++) {
result[i] = (IEnergyProjectile) NpcAPI.Instance().getIEntity(entities.get(i));
@@ -1,7 +1,7 @@
package noppes.npcs.scripted.entity;
import kamkeel.npcs.controllers.AttributeController;
import kamkeel.npcs.entity.EntityAbilityProjectile;
import kamkeel.npcs.entity.EntityEnergyProjectile;
import kamkeel.npcs.network.PacketHandler;
import kamkeel.npcs.network.packets.data.AchievementPacket;
import kamkeel.npcs.network.packets.data.ChatAlertPacket;
@@ -937,7 +937,7 @@ public class ScriptPlayer<T extends EntityPlayerMP> extends ScriptLivingBase<T>
}
public IEnergyProjectile[] getActiveEnergyProjectiles() {
List<EntityAbilityProjectile> entities = EntityAbilityProjectile.getActiveProjectiles(player.getEntityId());
List<EntityEnergyProjectile> entities = EntityEnergyProjectile.getActiveProjectiles(player.getEntityId());
IEnergyProjectile[] result = new IEnergyProjectile[entities.size()];
for (int i = 0; i < entities.size(); i++) {
result[i] = (IEnergyProjectile) NpcAPI.Instance().getIEntity(entities.get(i));
@@ -0,0 +1,98 @@
package noppes.npcs.scripted.event;
import cpw.mods.fml.common.eventhandler.Cancelable;
import noppes.npcs.api.entity.IEnergyBarrier;
import noppes.npcs.api.entity.IEnergyProjectile;
import noppes.npcs.api.entity.IEntity;
import noppes.npcs.api.event.IEnergyBarrierEvent;
import noppes.npcs.constants.EnumScriptType;
public class EnergyBarrierEvent extends CustomNPCsEvent implements IEnergyBarrierEvent {
public final IEnergyBarrier barrier;
public final IEntity owner;
public EnergyBarrierEvent(IEnergyBarrier barrier) {
this.barrier = barrier;
this.owner = barrier.getOwner();
}
@Override
public IEnergyBarrier getBarrier() {
return barrier;
}
@Override
public IEntity getOwner() {
return owner;
}
@Override
public String getHookName() {
return "energyBarrierEvent";
}
public static class SpawnedEvent extends EnergyBarrierEvent implements IEnergyBarrierEvent.SpawnedEvent {
public SpawnedEvent(IEnergyBarrier barrier) {
super(barrier);
}
@Override
public String getHookName() {
return EnumScriptType.ENERGY_BARRIER_SPAWNED.function;
}
}
public static class UpdateEvent extends EnergyBarrierEvent implements IEnergyBarrierEvent.UpdateEvent {
public UpdateEvent(IEnergyBarrier barrier) {
super(barrier);
}
@Override
public String getHookName() {
return EnumScriptType.ENERGY_BARRIER_TICK.function;
}
}
@Cancelable
public static class HitEvent extends EnergyBarrierEvent implements IEnergyBarrierEvent.HitEvent {
private final IEnergyProjectile projectile;
private float damage;
public HitEvent(IEnergyBarrier barrier, IEnergyProjectile projectile, float damage) {
super(barrier);
this.projectile = projectile;
this.damage = damage;
}
@Override
public IEnergyProjectile getProjectile() {
return projectile;
}
@Override
public float getDamage() {
return damage;
}
@Override
public void setDamage(float damage) {
this.damage = damage;
}
@Override
public String getHookName() {
return EnumScriptType.ENERGY_BARRIER_HIT.function;
}
}
public static class DestroyedEvent extends EnergyBarrierEvent implements IEnergyBarrierEvent.DestroyedEvent {
public DestroyedEvent(IEnergyBarrier barrier) {
super(barrier);
}
@Override
public String getHookName() {
return EnumScriptType.ENERGY_BARRIER_DESTROYED.function;
}
}
}
@@ -1987,6 +1987,46 @@ ability.hover.canCounter=Enable counterattack when blocking damage
ability.hover.counterType=Flat = fixed damage, Percent = % of blocked damage
ability.hover.affectsCaster=Hazard can damage the NPC that created it
ability.hover.sweepMode=Swipe = single arc attack, Spin = continuous rotation
ability.hover.launching=When enabled, wall launches forward dealing damage and knockback
# Energy Barrier Abilities
ability.cnpc.dome=Dome
ability.cnpc.wall=Wall
ability.cnpc.shield=Shield
ability.cnpc.slicer=Slicer
ability.section.barrier=Barrier
ability.section.knockback=Knockback
ability.section.panel=Panel
ability.section.launch=Launch
ability.section.slicer=Slicer
ability.domeRadius=Dome Radius
ability.useHealth=Use Health
ability.maxHealth=Max Health
ability.useDuration=Use Duration
ability.defaultMultiplier=Default Multiplier
ability.innerAlpha=Inner Alpha
ability.heightOffset=Height Offset
ability.knockbackEnabled=Knockback
ability.knockbackStrength=Knockback Strength
ability.knockbackTarget=Target
ability.section.melee=Melee
ability.meleeEnabled=Melee Attackable
ability.meleeDamageMultiplier=Melee Multiplier
ability.followCaster=Follow Caster
ability.hover.followCaster=Dome stays centered on the caster as they move
ability.variant.small=Small
ability.variant.large=Large
ability.variant.static=Static
ability.variant.launched=Launched
gui.both=Both
gui.players=Players
# Conditions
condition.edit=Edit Condition