Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vidal committed 2026-02-17 19:38:52 -03:00
commit 52269705b5
62 files changed
+3847 -713

No files matched your search

@@ -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;
}
}
@@ -0,0 +1,112 @@
package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.entity.EntityEnergyDome;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
import org.lwjgl.opengl.GL11;
/**
* 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 RenderEnergyBarrier {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
EntityEnergyDome dome = (EntityEnergyDome) entity;
float radius = dome.getDomeRadius();
float healthPercent = dome.getHealthPercent();
setupRenderState();
GL11.glPushMatrix();
GL11.glTranslated(x, y, z);
// Lightning BEFORE any rotation (uniform spread)
if (dome.hasLightningEffect()) {
renderAttachedLightning(dome, 0.95f, radius);
}
// Hit flash effect
float flashAlpha = computeFlashAlpha(dome);
float innerScale = 0.95f;
// Render outer dome (translucent glow)
if (dome.isOuterColorEnabled()) {
float outerScale = 1.0f + dome.getOuterColorWidth() * 0.1f;
float outerAlpha = dome.getOuterColorAlpha() * healthPercent;
GL11.glDepthMask(false);
renderSphere(dome.getOuterColor(), outerAlpha + flashAlpha, radius * outerScale, 8);
GL11.glDepthMask(true);
}
// 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();
restoreRenderState();
}
/**
* Render a sphere made of quad panels.
* Uses latitude/longitude segments to form a full sphere.
*/
private void renderSphere(int color, float alpha, float radius, int segments) {
float[] rgb = extractRGB(color);
Tessellator tess = Tessellator.instance;
tess.startDrawingQuads();
tess.setColorRGBA_F(rgb[0], rgb[1], rgb[2], alpha);
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;
float r1 = (float) Math.cos(theta1) * radius;
float r2 = (float) Math.cos(theta2) * radius;
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) * r1;
float z1 = (float) Math.sin(phi1) * r1;
float x2 = (float) Math.cos(phi2) * r1;
float z2 = (float) Math.sin(phi2) * r1;
float x3 = (float) Math.cos(phi2) * r2;
float z3 = (float) Math.sin(phi2) * r2;
float x4 = (float) Math.cos(phi1) * r2;
float z4 = (float) Math.sin(phi1) * r2;
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.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();
}
// TODO: Skybox Feature — renderSkyboxSphere method removed
}
@@ -0,0 +1,132 @@
package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.entity.EntityEnergyPanel;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
import org.lwjgl.opengl.GL11;
/**
* Renders the Energy Panel entity as a flat translucent rectangle.
* Used by both Energy Wall and Energy Shield abilities.
* Voxel-blocky style with inner/outer layers.
*/
@SideOnly(Side.CLIENT)
public class RenderEnergyPanel extends RenderEnergyBarrier {
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
EntityEnergyPanel panel = (EntityEnergyPanel) entity;
float width = panel.getPanelData().panelWidth;
float height = panel.getPanelData().panelHeight;
float panelYaw = panel.getPanelYaw();
float healthPercent = panel.getHealthPercent();
setupRenderState();
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);
// Hit flash
float flashAlpha = computeFlashAlpha(panel);
// Subtle pulsing
float pulseTime = entity.ticksExisted + partialTicks;
float pulse = (float) Math.sin(pulseTime * 0.08f) * 0.02f;
float scale = 1.0f + pulse;
GL11.glPushMatrix();
GL11.glScalef(scale, scale, scale);
float innerScale = 0.9f;
float panelThickness = 0.1f;
// Render outer panel (translucent glow)
if (panel.isOuterColorEnabled()) {
float outerScale = 1.0f + panel.getOuterColorWidth() * 0.15f;
float outerAlpha = panel.getOuterColorAlpha() * healthPercent;
GL11.glDepthMask(false);
renderPanel(panel.getOuterColor(), outerAlpha + flashAlpha,
width * 0.5f * outerScale, height * 0.5f * outerScale, panelThickness * 1.5f);
GL11.glDepthMask(true);
}
// 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();
GL11.glPopMatrix();
restoreRenderState();
}
/**
* 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;
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);
// Back 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);
// Top edge (y+)
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);
// Bottom edge (y-)
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);
// Right edge (x+)
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);
// Left edge (x-)
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();
}
}
@@ -0,0 +1,125 @@
package kamkeel.npcs.client.renderer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.entity.EntityEnergySlicer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.Entity;
import org.lwjgl.opengl.GL11;
/**
* Renders the Energy Slicer entity as a thin wide blade.
* Oriented perpendicular to travel direction.
* Inner solid core with outer translucent glow.
*/
@SideOnly(Side.CLIENT)
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 width = slicer.getSliceWidth();
float thickness = slicer.getSliceThickness();
// 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 for uniform spread (like Orb pattern)
if (slicer.hasLightningEffect()) {
renderAttachedLightning(slicer, 0.7f, width * 0.5f);
}
// Orient perpendicular to travel direction
float travelYaw = slicer.getTravelYaw();
float travelPitch = slicer.getTravelPitch();
GL11.glRotatef(travelYaw, 0.0f, 1.0f, 0.0f);
GL11.glRotatef(travelPitch, 1.0f, 0.0f, 0.0f);
float innerScale = 0.7f;
// Render outer blade (translucent glow)
if (slicer.isOuterColorEnabled()) {
float outerScale = 1.0f + slicer.getOuterColorWidth() * 0.2f;
GL11.glDepthMask(false);
renderBlade(slicer.getOuterColor(), slicer.getOuterColorAlpha(),
width * 0.5f * outerScale, thickness * 2.0f, 0.05f);
GL11.glDepthMask(true);
}
// 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();
restoreRenderState();
}
/**
* 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.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);
// Back face (z-)
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);
// Top edge (y+)
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);
// Bottom edge (y-)
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);
// Right edge (x+)
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);
// Left edge (x-)
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();
}
}
@@ -103,7 +103,7 @@ public abstract class Ability implements IAbility {
protected boolean ignoreCooldown = false;
// Configurable potion effects
protected List<AbilityEffect> effects = new ArrayList<>();
protected List<AbilityPotionEffect> effects = new ArrayList<>();
// Burst
protected boolean burstEnabled = false;
@@ -378,7 +378,7 @@ public abstract class Ability implements IAbility {
if (entity == null || effects.isEmpty()) {
return;
}
for (AbilityEffect effect : effects) {
for (AbilityPotionEffect effect : effects) {
effect.apply(entity);
}
}
@@ -1164,7 +1164,7 @@ public abstract class Ability implements IAbility {
// Effects
NBTTagList effectList = new NBTTagList();
for (AbilityEffect effect : effects) {
for (AbilityPotionEffect effect : effects) {
effectList.appendTag(effect.writeNBT());
}
nbt.setTag("effects", effectList);
@@ -1235,7 +1235,7 @@ public abstract class Ability implements IAbility {
if (nbt.hasKey("effects")) {
NBTTagList effectList = nbt.getTagList("effects", 10);
for (int i = 0; i < effectList.tagCount(); i++) {
AbilityEffect effect = AbilityEffect.fromNBT(effectList.getCompoundTagAt(i));
AbilityPotionEffect effect = AbilityPotionEffect.fromNBT(effectList.getCompoundTagAt(i));
if (effect != null && effect.isValid()) {
effects.add(effect);
}
@@ -1727,15 +1727,15 @@ public abstract class Ability implements IAbility {
this.ignoreCooldown = ignoreCooldown;
}
public List<AbilityEffect> getEffects() {
public List<AbilityPotionEffect> getEffects() {
return effects;
}
public void setEffects(List<AbilityEffect> effects) {
public void setEffects(List<AbilityPotionEffect> effects) {
this.effects = effects != null ? effects : new ArrayList<>();
}
public void addEffect(AbilityEffect effect) {
public void addEffect(AbilityPotionEffect effect) {
if (effect != null && effect.isValid()) {
effects.add(effect);
}
@@ -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;
@@ -99,6 +108,11 @@ public class AbilityController implements IAbilityHandler {
registerType("cnpc:hazard", AbilityHazard::new);
registerType("cnpc:trap", AbilityTrap::new);
registerType("cnpc:dome", AbilityDome::new);
registerType("cnpc:wall", AbilityWall::new);
registerType("cnpc:shield", AbilityShield::new);
registerType("cnpc:slicer", AbilitySlicer::new);
}
// ═══════════════════════════════════════════════════════════════════════════
@@ -9,17 +9,17 @@ import noppes.npcs.constants.EnumPotionType;
* Represents a configurable potion effect that can be applied by abilities.
* Uses the unified EnumPotionType enum shared across melee, ranged, and ability systems.
*/
public class AbilityEffect {
public class AbilityPotionEffect {
private EnumPotionType type = EnumPotionType.None;
private int manualPotionId = 0;
private int durationTicks = 60;
private int amplifier = 0; // 0-10
public AbilityEffect() {
public AbilityPotionEffect() {
}
public AbilityEffect(EnumPotionType type, int durationTicks, int amplifier) {
public AbilityPotionEffect(EnumPotionType type, int durationTicks, int amplifier) {
this.type = type;
this.durationTicks = Math.max(1, durationTicks);
this.amplifier = Math.max(0, Math.min(10, amplifier));
@@ -28,8 +28,8 @@ public class AbilityEffect {
/**
* Creates a copy of this effect.
*/
public AbilityEffect copy() {
AbilityEffect copy = new AbilityEffect(type, durationTicks, amplifier);
public AbilityPotionEffect copy() {
AbilityPotionEffect copy = new AbilityPotionEffect(type, durationTicks, amplifier);
copy.manualPotionId = this.manualPotionId;
return copy;
}
@@ -81,10 +81,10 @@ public class AbilityEffect {
}
/**
* Creates an AbilityEffect from NBT.
* Creates an AbilityPotionEffect from NBT.
*/
public static AbilityEffect fromNBT(NBTTagCompound nbt) {
AbilityEffect effect = new AbilityEffect();
public static AbilityPotionEffect fromNBT(NBTTagCompound nbt) {
AbilityPotionEffect effect = new AbilityPotionEffect();
effect.readNBT(nbt);
return effect;
}
@@ -0,0 +1,159 @@
package kamkeel.npcs.controllers.data.ability.data;
import net.minecraft.nbt.NBTTagCompound;
import java.util.HashMap;
import java.util.Map;
/**
* Groups barrier properties shared by energy barrier abilities (Dome, Wall, Shield).
* 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").
* Value is the multiplier applied to damage dealt to this barrier.
*/
private Map<String, Float> damageMultipliers = new HashMap<>();
public EnergyBarrierData() {}
public EnergyBarrierData(float maxHealth, boolean useHealth, int durationTicks, boolean useDuration) {
this.maxHealth = maxHealth;
this.useHealth = useHealth;
this.durationTicks = durationTicks;
this.useDuration = useDuration;
}
// ==================== MULTIPLIER METHODS ====================
public float getMultiplier(String abilityTypeId) {
Float mult = damageMultipliers.get(abilityTypeId);
return mult != null ? mult : defaultMultiplier;
}
public void setMultiplier(String abilityTypeId, float multiplier) {
damageMultipliers.put(abilityTypeId, multiplier);
}
public void removeMultiplier(String abilityTypeId) {
damageMultipliers.remove(abilityTypeId);
}
public Map<String, Float> getDamageMultipliers() {
return damageMultipliers;
}
public void setDamageMultipliers(Map<String, Float> multipliers) {
this.damageMultipliers = multipliers != null ? multipliers : new HashMap<>();
}
// ==================== GETTERS & SETTERS ====================
public float getMaxHealth() { return maxHealth; }
public void setMaxHealth(float maxHealth) { this.maxHealth = Math.max(0, maxHealth); }
public boolean isUseHealth() { return useHealth; }
public void setUseHealth(boolean useHealth) { this.useHealth = useHealth; }
public int getDurationTicks() { return durationTicks; }
public void setDurationTicks(int durationTicks) { this.durationTicks = Math.max(1, durationTicks); }
public boolean isUseDuration() { return useDuration; }
public void setUseDuration(boolean useDuration) { this.useDuration = useDuration; }
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) {
nbt.setFloat("barrierMaxHealth", maxHealth);
nbt.setBoolean("barrierUseHealth", useHealth);
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()) {
multNbt.setFloat(entry.getKey(), entry.getValue());
}
nbt.setTag("barrierMultipliers", multNbt);
}
public void readNBT(NBTTagCompound nbt) {
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")) {
NBTTagCompound multNbt = nbt.getCompoundTag("barrierMultipliers");
@SuppressWarnings("unchecked")
java.util.Set<String> keys = multNbt.func_150296_c();
for (String key : keys) {
damageMultipliers.put(key, multNbt.getFloat(key));
}
}
}
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;
}
}
@@ -0,0 +1,81 @@
package kamkeel.npcs.controllers.data.ability.data;
import net.minecraft.nbt.NBTTagCompound;
/**
* Groups panel-specific properties for energy wall/shield abilities.
* Handles dimensions, positioning, and launch behavior.
*/
public class EnergyPanelData {
public float panelWidth = 3.0f;
public float panelHeight = 3.0f;
public float heightOffset = 0.0f;
// Launch properties (Wall only)
public boolean launching = false;
public float launchSpeed = 0.5f;
public float launchDamage = 8.0f;
public float launchKnockback = 2.0f;
public EnergyPanelData() {}
public EnergyPanelData(float panelWidth, float panelHeight, float heightOffset) {
this.panelWidth = panelWidth;
this.panelHeight = panelHeight;
this.heightOffset = heightOffset;
}
// ==================== GETTERS & SETTERS ====================
public float getPanelWidth() { return panelWidth; }
public void setPanelWidth(float panelWidth) { this.panelWidth = Math.max(0.5f, panelWidth); }
public float getPanelHeight() { return panelHeight; }
public void setPanelHeight(float panelHeight) { this.panelHeight = Math.max(0.5f, panelHeight); }
public float getHeightOffset() { return heightOffset; }
public void setHeightOffset(float heightOffset) { this.heightOffset = heightOffset; }
public boolean isLaunching() { return launching; }
public void setLaunching(boolean launching) { this.launching = launching; }
public float getLaunchSpeed() { return launchSpeed; }
public void setLaunchSpeed(float launchSpeed) { this.launchSpeed = Math.max(0.1f, launchSpeed); }
public float getLaunchDamage() { return launchDamage; }
public void setLaunchDamage(float launchDamage) { this.launchDamage = Math.max(0, launchDamage); }
public float getLaunchKnockback() { return launchKnockback; }
public void setLaunchKnockback(float launchKnockback) { this.launchKnockback = Math.max(0, launchKnockback); }
// ==================== NBT ====================
public void writeNBT(NBTTagCompound nbt) {
nbt.setFloat("panelWidth", panelWidth);
nbt.setFloat("panelHeight", panelHeight);
nbt.setFloat("panelHeightOffset", heightOffset);
nbt.setBoolean("panelLaunching", launching);
nbt.setFloat("panelLaunchSpeed", launchSpeed);
nbt.setFloat("panelLaunchDamage", launchDamage);
nbt.setFloat("panelLaunchKnockback", launchKnockback);
}
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.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;
}
public EnergyPanelData copy() {
EnergyPanelData copy = new EnergyPanelData(panelWidth, panelHeight, heightOffset);
copy.launching = launching;
copy.launchSpeed = launchSpeed;
copy.launchDamage = launchDamage;
copy.launchKnockback = launchKnockback;
return copy;
}
}
@@ -2,7 +2,7 @@ package kamkeel.npcs.controllers.data.ability.gui;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.controllers.data.ability.AbilityEffect;
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import noppes.npcs.client.gui.builder.FieldDef;
@@ -55,14 +55,14 @@ public class AbilityFieldBuilder extends GuiFieldBuilder {
sw.addLabel(new GuiNpcLabel(labelId++, def.getLabel(), colLLabel, y + 2, 0xFFFF55));
y += 15;
List<AbilityEffect> effects = (List<AbilityEffect>) def.getValue();
List<AbilityPotionEffect> effects = (List<AbilityPotionEffect>) def.getValue();
if (effects == null) effects = new ArrayList<>();
String[] typeNames = EnumPotionType.getLangKeysNoNone();
String[] ampValues = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"};
for (int e = 0; e < effects.size() && e < 5; e++) {
AbilityEffect effect = effects.get(e);
AbilityPotionEffect effect = effects.get(e);
// Type selector button (index offset by -1 since None is excluded)
int typeIdx = effect.getType().ordinal() - 1;
@@ -144,7 +144,7 @@ public class AbilityFieldBuilder extends GuiFieldBuilder {
FieldDef def = buttonFieldMap.get(buttonId);
if (def == null) def = clearFieldMap.get(buttonId);
if (def != null && def.getType() == FieldType.EFFECTS_LIST) {
List<AbilityEffect> effects = (List<AbilityEffect>) def.getValue();
List<AbilityPotionEffect> effects = (List<AbilityPotionEffect>) def.getValue();
if (effects == null) return false;
int effectIdx = meta[0];
int action = meta[1];
@@ -167,7 +167,7 @@ public class AbilityFieldBuilder extends GuiFieldBuilder {
return true;
case 4: // Add
if (effects.size() < 5) {
effects.add(new AbilityEffect(EnumPotionType.Slowness, 60, 0));
effects.add(new AbilityPotionEffect(EnumPotionType.Slowness, 60, 0));
}
return true;
}
@@ -184,7 +184,7 @@ public class AbilityFieldBuilder extends GuiFieldBuilder {
if (meta != null) {
FieldDef def = textFieldMap.get(textFieldId);
if (def != null && def.getType() == FieldType.EFFECTS_LIST) {
List<AbilityEffect> effects = (List<AbilityEffect>) def.getValue();
List<AbilityPotionEffect> effects = (List<AbilityPotionEffect>) def.getValue();
if (effects != null && meta[0] < effects.size()) {
if (meta[1] == 1) { // duration field
effects.get(meta[0]).setDurationTicks(field.getInteger());
@@ -2,7 +2,7 @@ package kamkeel.npcs.controllers.data.ability.gui;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.controllers.data.ability.AbilityEffect;
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
import noppes.npcs.client.gui.builder.FieldDef;
import noppes.npcs.client.gui.builder.FieldType;
@@ -18,10 +18,10 @@ import java.util.function.Supplier;
public class AbilityFieldDefs {
@SuppressWarnings("unchecked")
public static FieldDef effectsListField(String label, Supplier<List<AbilityEffect>> getter, Consumer<List<AbilityEffect>> setter) {
public static FieldDef effectsListField(String label, Supplier<List<AbilityPotionEffect>> getter, Consumer<List<AbilityPotionEffect>> setter) {
return FieldDef.custom(label, FieldType.EFFECTS_LIST,
() -> getter.get(),
v -> setter.accept((List<AbilityEffect>) v));
v -> setter.accept((List<AbilityPotionEffect>) v));
}
}
@@ -1,6 +1,6 @@
package kamkeel.npcs.controllers.data.ability.type;
import kamkeel.npcs.controllers.data.ability.AbilityEffect;
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
import kamkeel.npcs.entity.EntityAbilityZone;
@@ -119,7 +119,7 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap {
particleMotion = 1; particleDir = "mc:mobSpell";
colorData.innerColor = 0x44DD44; colorData.outerColor = 0x116611;
windUpColor = 0x6044DD44; activeColor = 0xC044FF44;
effects.add(new AbilityEffect(EnumPotionType.Poison, 100, 0));
effects.add(new AbilityPotionEffect(EnumPotionType.Poison, 100, 0));
break;
case "EXPLOSIVE":
groundAlpha = 0.04f;
@@ -127,7 +127,7 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap {
particleMotion = 0; particleDir = "mc:smoke";
colorData.innerColor = 0xFF6611; colorData.outerColor = 0xCC2200;
windUpColor = 0x60FF6611; activeColor = 0xC0FF4400;
effects.add(new AbilityEffect(EnumPotionType.Fire, 60, 0));
effects.add(new AbilityPotionEffect(EnumPotionType.Fire, 60, 0));
break;
case "CURSED":
groundAlpha = 0.04f;
@@ -135,7 +135,7 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap {
particleMotion = 1; particleDir = "mc:portal";
colorData.innerColor = 0xAA44FF; colorData.outerColor = 0x6622BB;
windUpColor = 0x60AA44FF; activeColor = 0xC0CC66FF;
effects.add(new AbilityEffect(EnumPotionType.Weakness, 100, 0));
effects.add(new AbilityPotionEffect(EnumPotionType.Weakness, 100, 0));
break;
case "SHOCK":
groundAlpha = 0.04f;
@@ -143,7 +143,7 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap {
particleMotion = 2; particleDir = "mc:enchantmenttable";
colorData.innerColor = 0x4488FF; colorData.outerColor = 0x2244BB;
windUpColor = 0x604488FF; activeColor = 0xC066AAFF;
effects.add(new AbilityEffect(EnumPotionType.MiningFatigue, 80, 1));
effects.add(new AbilityPotionEffect(EnumPotionType.MiningFatigue, 80, 1));
break;
case "SNARE":
groundAlpha = 0.04f;
@@ -151,7 +151,7 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap {
particleMotion = 1; particleDir = "mc:snowshovel";
colorData.innerColor = 0x88CCFF; colorData.outerColor = 0x4488CC;
windUpColor = 0x6088CCFF; activeColor = 0xC0AADDFF;
effects.add(new AbilityEffect(EnumPotionType.Slowness, 100, 1));
effects.add(new AbilityPotionEffect(EnumPotionType.Slowness, 100, 1));
break;
case "HIDDEN":
default:
@@ -1,14 +1,13 @@
package kamkeel.npcs.controllers.data.ability.type;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.AbilityEffect;
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
import kamkeel.npcs.controllers.data.ability.LockMovementType;
import kamkeel.npcs.controllers.data.ability.TargetingMode;
import kamkeel.npcs.controllers.data.ability.UserType;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
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.EntityAbilityZone;
import kamkeel.npcs.entity.EntityAbilityZone.AccentStyle;
import kamkeel.npcs.entity.EntityAbilityZone.ParticleMotion;
@@ -220,7 +219,7 @@ public abstract class AbilityZone extends Ability {
particleMotion = 1; particleGlow = true; particleDir = "mc:mobSpell";
colorData.innerColor = 0x44DD44; colorData.outerColor = 0x116611;
windUpColor = 0x6044DD44; activeColor = 0xC044FF44;
effects.add(new AbilityEffect(EnumPotionType.Poison, 100, 0));
effects.add(new AbilityPotionEffect(EnumPotionType.Poison, 100, 0));
break;
case "INFERNO":
groundFill = true; groundAlpha = 0.35f; rings = true; ringCount = 3;
@@ -229,7 +228,7 @@ public abstract class AbilityZone extends Ability {
particleMotion = 0; particleGlow = true; particleDir = "mc:flame";
colorData.innerColor = 0xFF6611; colorData.outerColor = 0xCC2200;
windUpColor = 0x60FF6611; activeColor = 0xC0FF4400;
effects.add(new AbilityEffect(EnumPotionType.Fire, 60, 0));
effects.add(new AbilityPotionEffect(EnumPotionType.Fire, 60, 0));
break;
case "ARCANE":
groundFill = true; groundAlpha = 0.20f; rings = true; ringCount = 1;
@@ -238,7 +237,7 @@ public abstract class AbilityZone extends Ability {
particleMotion = 1; particleGlow = true; particleDir = "mc:portal";
colorData.innerColor = 0xAA44FF; colorData.outerColor = 0x6622BB;
windUpColor = 0x60AA44FF; activeColor = 0xC0CC66FF;
effects.add(new AbilityEffect(EnumPotionType.Weakness, 100, 0));
effects.add(new AbilityPotionEffect(EnumPotionType.Weakness, 100, 0));
break;
case "ELECTRIC":
groundFill = true; groundAlpha = 0.15f; rings = false; ringCount = 1;
@@ -247,7 +246,7 @@ public abstract class AbilityZone extends Ability {
particleMotion = 2; particleGlow = true; particleDir = "mc:enchantmenttable";
colorData.innerColor = 0x4488FF; colorData.outerColor = 0x2244BB;
windUpColor = 0x604488FF; activeColor = 0xC066AAFF;
effects.add(new AbilityEffect(EnumPotionType.MiningFatigue, 80, 1));
effects.add(new AbilityPotionEffect(EnumPotionType.MiningFatigue, 80, 1));
break;
case "FROST":
groundFill = true; groundAlpha = 0.25f; rings = true; ringCount = 3;
@@ -256,7 +255,7 @@ public abstract class AbilityZone extends Ability {
particleMotion = 1; particleGlow = true; particleDir = "mc:snowshovel";
colorData.innerColor = 0x88CCFF; colorData.outerColor = 0x4488CC;
windUpColor = 0x6088CCFF; activeColor = 0xC0AADDFF;
effects.add(new AbilityEffect(EnumPotionType.Slowness, 100, 1));
effects.add(new AbilityPotionEffect(EnumPotionType.Slowness, 100, 1));
break;
case "DEFAULT":
groundFill = true; groundAlpha = 0.25f; rings = true; ringCount = 3;
@@ -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;
@@ -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;
@@ -0,0 +1,130 @@
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.LockMovementType;
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;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.client.gui.builder.FieldDef;
import java.util.Arrays;
import java.util.List;
/**
* Energy Dome ability: Spawns a spherical barrier centered on the caster.
* Blocks incoming energy projectiles with configurable damage multipliers.
* Duration and/or HP based.
*/
public class AbilityDome extends AbilityEnergyBarrier {
private float domeRadius = 5.0f;
private boolean followCaster = false;
public AbilityDome() {
super(
new EnergyDisplayData(0x44CCFF, 0x2288FF, true, 0.3f, 0.35f, 0.5f, 0.0f),
new EnergyBarrierData(100.0f, true, 200, true)
);
this.typeId = "ability.cnpc.dome";
this.name = "Dome";
this.targetingMode = TargetingMode.SELF;
this.cooldownTicks = 100;
this.windUpTicks = 30;
this.lockMovement = LockMovementType.WINDUP;
this.telegraphType = TelegraphType.RING;
this.showTelegraph = true;
this.windUpAnimationName = "";
this.activeAnimationName = "";
}
// ==================== ABSTRACT IMPLEMENTATIONS ====================
@Override
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;
}
@Override
public boolean isTargetingModeLocked() {
return true;
}
@Override
public TargetingMode[] getAllowedTargetingModes() {
return new TargetingMode[]{TargetingMode.SELF};
}
@Override
public float getTelegraphRadius() {
return domeRadius;
}
// ==================== VARIANTS ====================
@Override
public List<AbilityVariant> getVariants() {
return Arrays.asList(
new AbilityVariant("ability.variant.small", a -> {
AbilityDome dome = (AbilityDome) a;
a.setName("Small Dome");
dome.setDomeRadius(3.0f);
dome.setBarrierMaxHealth(60.0f);
}),
new AbilityVariant("ability.variant.large", a -> {
AbilityDome dome = (AbilityDome) a;
a.setName("Large Dome");
dome.setDomeRadius(8.0f);
dome.setBarrierMaxHealth(200.0f);
dome.setBarrierDuration(300);
})
);
}
// ==================== TYPE-SPECIFIC NBT ====================
@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 ====================
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)
@Override
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"));
}
}
@@ -0,0 +1,215 @@
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.data.EnergyBarrierData;
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
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;
/**
* Abstract base for energy barrier abilities (Dome, Wall, Shield).
* Handles shared visual data, barrier configuration, and lifecycle management.
*/
public abstract class AbilityEnergyBarrier extends AbilityEnergy {
protected final EnergyBarrierData barrierData;
// Runtime entity tracking
protected transient EntityEnergyBarrier barrierEntity;
protected AbilityEnergyBarrier(EnergyDisplayData displayData, EnergyBarrierData barrierData) {
super(displayData);
this.barrierData = barrierData;
}
// ==================== ABSTRACT METHODS ====================
/**
* Create and spawn the barrier entity during execution.
*/
protected abstract EntityEnergyBarrier createBarrierEntity(EntityLivingBase caster, EntityLivingBase target);
/**
* Add type-specific GUI field definitions.
*/
@SideOnly(Side.CLIENT)
protected abstract void addBarrierTypeDefinitions(List<FieldDef> defs);
/**
* Write type-specific fields to NBT.
*/
protected abstract void writeBarrierTypeNBT(NBTTagCompound nbt);
/**
* Read type-specific fields from NBT.
*/
protected abstract void readBarrierTypeNBT(NBTTagCompound nbt);
// ==================== LIFECYCLE ====================
@Override
public boolean allowBurst() {
return false;
}
@Override
public boolean hasDamage() {
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) {
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);
}
}
}
@Override
public void onActiveTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
if (barrierEntity == null || barrierEntity.isDead) {
signalCompletion();
return;
}
// Duration tracked by entity itself, but check here too as a failsafe
if (barrierData.useDuration && tick >= barrierData.durationTicks) {
cleanup();
signalCompletion();
}
}
@Override
public void onComplete(EntityLivingBase caster, EntityLivingBase target) {
}
@Override
public void onInterrupt(EntityLivingBase caster, DamageSource source, float damage) {
cleanup();
}
@Override
public void cleanup() {
if (barrierEntity != null && !barrierEntity.isDead) {
barrierEntity.setDead();
}
barrierEntity = null;
}
// ==================== NBT ====================
@Override
public final void writeTypeNBT(NBTTagCompound nbt) {
writeBarrierTypeNBT(nbt);
writeEnergyNBT(nbt);
barrierData.writeNBT(nbt);
}
@Override
public final void readTypeNBT(NBTTagCompound nbt) {
readBarrierTypeNBT(nbt);
readEnergyNBT(nbt);
barrierData.readNBT(nbt);
}
// ==================== API GETTERS & SETTERS ====================
// Display and lightning data inherited from AbilityEnergy
// Barrier data
public float getBarrierMaxHealth() { return barrierData.maxHealth; }
public void setBarrierMaxHealth(float maxHealth) { barrierData.setMaxHealth(maxHealth); }
public boolean isUseHealth() { return barrierData.useHealth; }
public void setUseHealth(boolean useHealth) { barrierData.useHealth = useHealth; }
public int getBarrierDuration() { return barrierData.durationTicks; }
public void setBarrierDuration(int ticks) { barrierData.setDurationTicks(ticks); }
public boolean isUseDuration() { return barrierData.useDuration; }
public void setUseDuration(boolean useDuration) { barrierData.useDuration = useDuration; }
public float getDefaultMultiplier() { return barrierData.defaultMultiplier; }
public void setDefaultMultiplier(float mult) { barrierData.defaultMultiplier = mult; }
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)
@Override
public final void getAbilityDefinitions(List<FieldDef> defs) {
// Type-specific fields first
addBarrierTypeDefinitions(defs);
// Barrier section
defs.add(FieldDef.section("ability.section.barrier"));
defs.add(FieldDef.boolField("ability.useHealth", this::isUseHealth, this::setUseHealth));
defs.add(FieldDef.floatField("ability.maxHealth", this::getBarrierMaxHealth, this::setBarrierMaxHealth)
.visibleWhen(this::isUseHealth));
defs.add(FieldDef.boolField("ability.useDuration", this::isUseDuration, this::setUseDuration));
defs.add(FieldDef.intField("ability.duration", this::getBarrierDuration, this::setBarrierDuration)
.range(1, 6000).visibleWhen(this::isUseDuration));
defs.add(FieldDef.floatField("ability.defaultMultiplier", this::getDefaultMultiplier, this::setDefaultMultiplier));
// 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.knockbackStrength", this::getKnockbackStrength, this::setKnockbackStrength)
.range(0, 10).visibleWhen(this::isKnockbackEnabled),
FieldDef.stringEnumField("ability.knockbackTarget", EnergyBarrierData.getKnockbackTargetKeys(),
this::getKnockbackTargetKey, this::setKnockbackTargetKey)
.visibleWhen(this::isKnockbackEnabled)
));
// 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,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,7 +9,7 @@ import kamkeel.npcs.controllers.data.ability.data.*;
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;
@@ -29,8 +29,7 @@ import java.util.List;
*
* @param <E> The entity type this ability spawns
*/
public abstract class AbilityEnergyProjectile<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;
@@ -38,9 +37,7 @@ public abstract class AbilityEnergyProjectile<E extends EntityAbilityProjectile>
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
@@ -62,11 +59,10 @@ public abstract class AbilityEnergyProjectile<E extends EntityAbilityProjectile>
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);
}
@@ -389,8 +385,7 @@ public abstract class AbilityEnergyProjectile<E extends EntityAbilityProjectile>
nbt.setInteger("fireDelay", fireDelay);
// Shared data classes
displayData.writeNBT(nbt);
lightningData.writeNBT(nbt);
writeEnergyNBT(nbt);
combatData.writeNBT(nbt);
homingData.writeNBT(nbt);
lifespanData.writeNBT(nbt);
@@ -414,8 +409,7 @@ public abstract class AbilityEnergyProjectile<E extends EntityAbilityProjectile>
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);
@@ -473,37 +467,7 @@ public abstract class AbilityEnergyProjectile<E extends EntityAbilityProjectile>
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; }
@@ -556,33 +520,14 @@ public abstract class AbilityEnergyProjectile<E extends EntityAbilityProjectile>
// 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;
@@ -73,7 +73,7 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
/**
* 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;
@@ -0,0 +1,143 @@
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.LockMovementType;
import kamkeel.npcs.controllers.data.ability.TargetingMode;
import kamkeel.npcs.controllers.data.ability.UserType;
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;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.client.gui.builder.FieldDef;
import java.util.Arrays;
import java.util.List;
/**
* Energy Shield ability: Spawns a flat energy panel held in front of the caster.
* The shield follows the caster's position and rotates with their look direction.
* Cannot be placed — it's always held.
*/
public class AbilityShield extends AbilityEnergyBarrier {
private float shieldWidth = 2.5f;
private float shieldHeight = 2.5f;
public AbilityShield() {
super(
new EnergyDisplayData(0xFFDD44, 0xFFAA00, true, 0.3f, 0.4f, 0.5f, 0.0f),
new EnergyBarrierData(60.0f, true, 120, true)
);
this.typeId = "ability.cnpc.shield";
this.name = "Shield";
this.targetingMode = TargetingMode.SELF;
this.cooldownTicks = 60;
this.windUpTicks = 10;
this.lockMovement = LockMovementType.WINDUP_AND_ACTIVE;
this.telegraphType = TelegraphType.NONE;
this.showTelegraph = false;
this.windUpAnimationName = "";
this.activeAnimationName = "";
this.allowedBy = UserType.BOTH;
}
// ==================== ABSTRACT IMPLEMENTATIONS ====================
@Override
protected EntityEnergyBarrier createBarrierEntity(EntityLivingBase caster, EntityLivingBase target) {
EnergyPanelData panelData = new EnergyPanelData(shieldWidth, shieldHeight, 0.0f);
float frontDist = 1.5f;
float yawRad = (float) Math.toRadians(caster.rotationYaw);
double spawnX = caster.posX + (-Math.sin(yawRad) * frontDist);
double spawnY = caster.posY + (caster.height * 0.5f);
double spawnZ = caster.posZ + (Math.cos(yawRad) * frontDist);
EntityEnergyPanel panel = new EntityEnergyPanel(
caster.worldObj, caster,
spawnX, spawnY, spawnZ, caster.rotationYaw,
EntityEnergyPanel.PanelMode.HELD,
displayData.copy(), lightningData.copy(), barrierData.copy(), panelData
);
panel.setSourceAbility(this);
return panel;
}
@Override
public boolean isTargetingModeLocked() {
return true;
}
@Override
public TargetingMode[] getAllowedTargetingModes() {
return new TargetingMode[]{TargetingMode.SELF};
}
@Override
public float getTelegraphRadius() {
return 0; // No telegraph for shield
}
// ==================== VARIANTS ====================
@Override
public List<AbilityVariant> getVariants() {
return Arrays.asList(
new AbilityVariant("ability.variant.small", a -> {
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 -> {
AbilityShield shield = (AbilityShield) a;
a.setName("Large Shield");
shield.setShieldWidth(4.0f);
shield.setShieldHeight(4.0f);
shield.setBarrierMaxHealth(120.0f);
shield.setBarrierDuration(200);
})
);
}
// ==================== TYPE-SPECIFIC NBT ====================
@Override
protected void writeBarrierTypeNBT(NBTTagCompound nbt) {
nbt.setFloat("shieldWidth", shieldWidth);
nbt.setFloat("shieldHeight", shieldHeight);
}
@Override
protected void readBarrierTypeNBT(NBTTagCompound nbt) {
this.shieldWidth = nbt.hasKey("shieldWidth") ? nbt.getFloat("shieldWidth") : 2.5f;
this.shieldHeight = nbt.hasKey("shieldHeight") ? nbt.getFloat("shieldHeight") : 2.5f;
}
// ==================== GETTERS & SETTERS ====================
public float getShieldWidth() { return shieldWidth; }
public void setShieldWidth(float width) { this.shieldWidth = Math.max(0.5f, width); }
public float getShieldHeight() { return shieldHeight; }
public void setShieldHeight(float height) { this.shieldHeight = Math.max(0.5f, height); }
// ==================== TYPE-SPECIFIC GUI ====================
@SideOnly(Side.CLIENT)
@Override
protected void addBarrierTypeDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.row(
FieldDef.floatField("gui.width", this::getShieldWidth, this::setShieldWidth).range(0.5f, 10.0f),
FieldDef.floatField("gui.height", this::getShieldHeight, this::setShieldHeight).range(0.5f, 10.0f)
));
}
}
@@ -0,0 +1,176 @@
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.LockMovementType;
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.TelegraphType;
import kamkeel.npcs.entity.EntityEnergySlicer;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.Vec3;
import noppes.npcs.client.gui.builder.FieldDef;
import java.util.Arrays;
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 AbilityEnergyProjectile to leverage the existing projectile infrastructure.
*/
public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
private float sliceWidth = 3.0f;
private float sliceThickness = 0.15f;
private boolean piercing = true;
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.slicer";
this.name = "Slicer";
this.targetingMode = TargetingMode.AGGRO_TARGET;
this.maxRange = 30.0f;
this.minRange = 3.0f;
this.cooldownTicks = 40;
this.windUpTicks = 15;
this.lockMovement = LockMovementType.WINDUP;
this.telegraphType = TelegraphType.LINE;
this.showTelegraph = true;
this.windUpAnimationName = "";
this.activeAnimationName = "";
}
// ==================== ABSTRACT IMPLEMENTATIONS ====================
@Override
protected EntityEnergySlicer createEntity(EntityLivingBase caster, EntityLivingBase target,
Vec3 spawnPos, EnergyDisplayData resolved, int index) {
return new EntityEnergySlicer(
caster.worldObj, caster, target,
spawnPos.xCoord, spawnPos.yCoord, spawnPos.zCoord,
sliceWidth, sliceThickness,
resolved, combatData, homingData, lightningData, lifespanData, trajectoryData,
piercing
);
}
@Override
protected void fireEntity(EntityEnergySlicer slicer, EntityLivingBase target) {
if (isPreview()) {
slicer.startPreviewFiring();
} else {
slicer.startMoving(target);
}
}
@Override
protected void setupEntityCharging(EntityEnergySlicer slicer, ProjectileData projData, int index) {
slicer.setupSlicerCharging(projData.anchor, windUpTicks, sliceWidth, sliceThickness);
}
@Override
protected void setupEntityPreview(EntityEnergySlicer slicer, EntityLivingBase caster,
EnergyDisplayData resolved, ProjectileData projData, int index) {
slicer.setupPreview(caster, sliceWidth, sliceThickness, resolved, lightningData, projData.anchor, windUpTicks);
}
@Override
protected EntityEnergySlicer[] createEntityArray(int size) {
return new EntityEnergySlicer[size];
}
@Override
protected float getProjectileTelegraphRadius() {
return sliceWidth * 0.75f;
}
// ==================== VARIANTS ====================
@Override
public List<AbilityVariant> getVariants() {
return Arrays.asList(
new AbilityVariant("ability.variant.single", a -> {
a.setName("Slicer");
}),
new AbilityVariant("ability.variant.dual", a -> {
AbilitySlicer slicer = (AbilitySlicer) a;
a.setName("Double Slicer");
slicer.setProjectileCount(2);
slicer.setFireDelay(5);
a.setWindUpAnimationName("");
a.setActiveAnimationName("");
})
);
}
// ==================== TYPE-SPECIFIC NBT ====================
@Override
protected void writeTypeSpecificNBT(NBTTagCompound nbt) {
nbt.setFloat("sliceWidth", sliceWidth);
nbt.setFloat("sliceThickness", sliceThickness);
nbt.setBoolean("piercing", piercing);
}
@Override
protected void readTypeSpecificNBT(NBTTagCompound nbt) {
this.sliceWidth = nbt.hasKey("sliceWidth") ? nbt.getFloat("sliceWidth") : 3.0f;
this.sliceThickness = nbt.hasKey("sliceThickness") ? nbt.getFloat("sliceThickness") : 0.15f;
this.piercing = !nbt.hasKey("piercing") || nbt.getBoolean("piercing");
}
// ==================== TYPE-SPECIFIC GETTERS ====================
public float getSlicerSpeed() { return homingData.speed; }
public void setSlicerSpeed(float speed) { homingData.speed = speed; }
public float getSliceWidth() { return sliceWidth; }
public void setSliceWidth(float width) { this.sliceWidth = Math.max(0.5f, width); }
public float getSliceThickness() { return sliceThickness; }
public void setSliceThickness(float thickness) { this.sliceThickness = Math.max(0.05f, thickness); }
public boolean isPiercing() { return piercing; }
public void setPiercing(boolean piercing) { this.piercing = piercing; }
// ==================== TYPE-SPECIFIC GUI ====================
@SideOnly(Side.CLIENT)
@Override
protected void addTypeDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.row(
FieldDef.intField("ability.projectileCount", this::getProjectileCount, this::setProjectileCount).range(1, MAX_PROJECTILES),
FieldDef.intField("ability.fireDelay", this::getFireDelay, this::setFireDelay)
.range(0, 200).visibleWhen(() -> projectileCount > 1)
));
defs.add(FieldDef.row(
FieldDef.floatField("enchantment.damage", this::getDamage, this::setDamage),
FieldDef.floatField("stats.speed", this::getSlicerSpeed, this::setSlicerSpeed)
));
defs.add(FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback));
defs.add(FieldDef.section("ability.section.slicer"));
defs.add(FieldDef.row(
FieldDef.floatField("gui.width", this::getSliceWidth, this::setSliceWidth).range(0.5f, 20.0f),
FieldDef.floatField("gui.thickness", this::getSliceThickness, this::setSliceThickness).range(0.05f, 2.0f)
));
defs.add(FieldDef.boolField("ability.piercing", this::isPiercing, this::setPiercing)
.hover("ability.hover.piercing"));
defs.add(FieldDef.row(
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime)
));
defs.add(FieldDef.section("ability.section.explosive"));
defs.add(FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"));
defs.add(FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius).visibleWhen(this::isExplosive));
defs.add(AbilityFieldDefs.effectsListField("ability.effects", this::getEffects, this::setEffects));
}
}
@@ -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);
}
}
@@ -0,0 +1,189 @@
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.LockMovementType;
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.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;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.client.gui.builder.FieldDef;
import java.util.Arrays;
import java.util.List;
/**
* Energy Wall ability: Spawns a placeable flat energy barrier.
* Can float above ground (configurable height offset).
* Optional launching mode fires the wall forward with damage/knockback.
*/
public class AbilityWall extends AbilityEnergyBarrier {
private final EnergyPanelData panelData;
public AbilityWall() {
super(
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.wall";
this.name = "Wall";
this.targetingMode = TargetingMode.AGGRO_TARGET;
this.maxRange = 15.0f;
this.minRange = 2.0f;
this.cooldownTicks = 80;
this.windUpTicks = 20;
this.lockMovement = LockMovementType.WINDUP;
this.telegraphType = TelegraphType.LINE;
this.showTelegraph = true;
this.windUpAnimationName = "";
this.activeAnimationName = "";
}
@Override
public boolean hasDamage() {
return panelData.launching; // Only has damage when launching
}
// ==================== ABSTRACT IMPLEMENTATIONS ====================
@Override
protected EntityEnergyBarrier createBarrierEntity(EntityLivingBase caster, EntityLivingBase target) {
// Place wall between caster and target
double placeX, placeY, placeZ;
float yaw;
if (target != null) {
double dx = target.posX - caster.posX;
double dz = target.posZ - caster.posZ;
double dist = Math.sqrt(dx * dx + dz * dz);
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);
placeY = caster.posY;
placeZ = caster.posZ + (Math.cos(yawRad) * 3.0);
yaw = caster.rotationYaw;
}
EntityEnergyPanel.PanelMode mode = panelData.launching
? EntityEnergyPanel.PanelMode.LAUNCHED
: EntityEnergyPanel.PanelMode.PLACED;
EntityEnergyPanel panel = new EntityEnergyPanel(
caster.worldObj, caster,
placeX, placeY, placeZ, yaw, mode,
displayData.copy(), lightningData.copy(), barrierData.copy(), panelData.copy()
);
panel.setSourceAbility(this);
return panel;
}
@Override
public float getTelegraphRadius() {
return Math.max(panelData.panelWidth, panelData.panelHeight) * 0.5f;
}
// ==================== VARIANTS ====================
@Override
public List<AbilityVariant> getVariants() {
return Arrays.asList(
new AbilityVariant("ability.variant.static", a -> {
a.setName("Static Wall");
}),
new AbilityVariant("ability.variant.launched", a -> {
AbilityWall wall = (AbilityWall) a;
a.setName("Launched Wall");
wall.panelData.launching = true;
wall.panelData.launchSpeed = 0.6f;
wall.panelData.launchDamage = 10.0f;
wall.panelData.launchKnockback = 3.0f;
})
);
}
// ==================== TYPE-SPECIFIC NBT ====================
@Override
protected void writeBarrierTypeNBT(NBTTagCompound nbt) {
panelData.writeNBT(nbt);
}
@Override
protected void readBarrierTypeNBT(NBTTagCompound nbt) {
panelData.readNBT(nbt);
}
// ==================== GETTERS & SETTERS ====================
public EnergyPanelData getPanelData() { return panelData; }
public float getPanelWidth() { return panelData.panelWidth; }
public void setPanelWidth(float width) { panelData.setPanelWidth(width); }
public float getPanelHeight() { return panelData.panelHeight; }
public void setPanelHeight(float height) { panelData.setPanelHeight(height); }
public float getHeightOffset() { return panelData.heightOffset; }
public void setHeightOffset(float offset) { panelData.heightOffset = offset; }
public boolean isLaunching() { return panelData.launching; }
public void setLaunching(boolean launching) { panelData.launching = launching; }
public float getLaunchSpeed() { return panelData.launchSpeed; }
public void setLaunchSpeed(float speed) { panelData.setLaunchSpeed(speed); }
public float getLaunchDamage() { return panelData.launchDamage; }
public void setLaunchDamage(float damage) { panelData.setLaunchDamage(damage); }
public float getLaunchKnockback() { return panelData.launchKnockback; }
public void setLaunchKnockback(float kb) { panelData.setLaunchKnockback(kb); }
// ==================== TYPE-SPECIFIC GUI ====================
@SideOnly(Side.CLIENT)
@Override
protected void addBarrierTypeDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.section("ability.section.panel"));
defs.add(FieldDef.row(
FieldDef.floatField("gui.width", this::getPanelWidth, this::setPanelWidth).range(0.5f, 20.0f),
FieldDef.floatField("gui.height", this::getPanelHeight, this::setPanelHeight).range(0.5f, 20.0f)
));
defs.add(FieldDef.floatField("ability.heightOffset", this::getHeightOffset, this::setHeightOffset)
.min(Float.NEGATIVE_INFINITY));
defs.add(FieldDef.section("ability.section.launch"));
defs.add(FieldDef.boolField("gui.enabled", this::isLaunching, this::setLaunching)
.hover("ability.hover.launching"));
defs.add(FieldDef.row(
FieldDef.floatField("stats.speed", this::getLaunchSpeed, this::setLaunchSpeed)
.visibleWhen(this::isLaunching),
FieldDef.floatField("enchantment.damage", this::getLaunchDamage, this::setLaunchDamage)
.visibleWhen(this::isLaunching)
));
defs.add(FieldDef.floatField("ability.knockback", this::getLaunchKnockback, this::setLaunchKnockback)
.visibleWhen(this::isLaunching));
}
}
@@ -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;
}
}
@@ -6,7 +6,7 @@ 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.AbilityEffect;
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
@@ -121,7 +121,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
private boolean particleGlow = true;
private long deathWorldTime = -1;
private List<AbilityEffect> effects = new ArrayList<>();
private List<AbilityPotionEffect> effects = new ArrayList<>();
// ═══════════════════════════════════════════════════════════════════
// TRAP-SPECIFIC PROPERTIES
@@ -194,7 +194,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
float particleDensity, float particleScale,
float animSpeed, float lightningDensity,
boolean visible,
List<AbilityEffect> effects) {
List<AbilityPotionEffect> effects) {
EntityAbilityZone zone = new EntityAbilityZone(world, ZoneType.TRAP, owner, x, y, z);
zone.shape = shape;
zone.radius = triggerRadius;
@@ -217,7 +217,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
zone.lightningDensity = lightningDensity;
zone.visible = visible;
if (effects != null) {
for (AbilityEffect e : effects) {
for (AbilityPotionEffect e : effects) {
zone.effects.add(e.copy());
}
}
@@ -236,7 +236,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
float zoneHeight,
float particleDensity, float particleScale,
float animSpeed, float lightningDensity,
List<AbilityEffect> effects) {
List<AbilityPotionEffect> effects) {
EntityAbilityZone zone = new EntityAbilityZone(world, ZoneType.HAZARD, owner, x, y, z);
zone.shape = shape;
zone.radius = radius;
@@ -255,7 +255,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
zone.animSpeed = animSpeed;
zone.lightningDensity = lightningDensity;
if (effects != null) {
for (AbilityEffect e : effects) {
for (AbilityPotionEffect e : effects) {
zone.effects.add(e.copy());
}
}
@@ -499,7 +499,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
private void applyEffects(EntityLivingBase entity) {
if (entity == null || effects.isEmpty()) return;
for (AbilityEffect effect : effects) {
for (AbilityPotionEffect effect : effects) {
effect.apply(entity);
}
}
@@ -668,7 +668,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
if (nbt.hasKey("Effects")) {
NBTTagList effectList = nbt.getTagList("Effects", 10);
for (int i = 0; i < effectList.tagCount(); i++) {
AbilityEffect effect = AbilityEffect.fromNBT(effectList.getCompoundTagAt(i));
AbilityPotionEffect effect = AbilityPotionEffect.fromNBT(effectList.getCompoundTagAt(i));
if (effect != null && effect.isValid()) {
effects.add(effect);
}
@@ -728,7 +728,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
// Effects
NBTTagList effectList = new NBTTagList();
for (AbilityEffect effect : effects) {
for (AbilityPotionEffect effect : effects) {
effectList.appendTag(effect.writeNBT());
}
nbt.setTag("Effects", effectList);
@@ -778,7 +778,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
// Effects
NBTTagCompound effectsNbt = new NBTTagCompound();
NBTTagList effectList = new NBTTagList();
for (AbilityEffect effect : effects) {
for (AbilityPotionEffect effect : effects) {
effectList.appendTag(effect.writeNBT());
}
effectsNbt.setTag("Effects", effectList);
@@ -832,7 +832,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
if (effectsNbt != null && effectsNbt.hasKey("Effects")) {
NBTTagList effectList = effectsNbt.getTagList("Effects", 10);
for (int i = 0; i < effectList.tagCount(); i++) {
AbilityEffect effect = AbilityEffect.fromNBT(effectList.getCompoundTagAt(i));
AbilityPotionEffect effect = AbilityPotionEffect.fromNBT(effectList.getCompoundTagAt(i));
if (effect != null && effect.isValid()) {
effects.add(effect);
}
@@ -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);
}
}
@@ -0,0 +1,220 @@
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.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.World;
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 EntityEnergyBarrier {
// ==================== DOME-SPECIFIC PROPERTIES ====================
protected float domeRadius = 5.0f;
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
}
public EntityEnergyDome(World world, EntityLivingBase owner, double x, double y, double z,
float domeRadius, EnergyDisplayData display, EnergyLightningData lightning,
EnergyBarrierData barrier) {
this(world);
this.setPosition(x, y, z);
this.ownerEntityId = owner.getEntityId();
this.domeRadius = domeRadius;
this.displayData = display;
this.lightningData = lightning;
this.barrierData = barrier;
this.currentHealth = barrier.maxHealth;
}
@Override
public void onUpdate() {
this.prevPosX = this.posX;
this.prevPosY = this.posY;
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 (updateBarrierTick()) return;
// Knockback entities inside the dome
if (!worldObj.isRemote && barrierData.knockbackEnabled) {
knockbackEntities();
}
}
// ==================== INCOMING CHECK ====================
/**
* Check if a projectile at the given position is entering this dome from outside.
* Only blocks incoming projectiles (dot product check).
*/
@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 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;
}
}
double dx = projectile.posX - this.posX;
double dy = projectile.posY - this.posY;
double dz = projectile.posZ - this.posZ;
double distSq = dx * dx + dy * dy + dz * dz;
// Check if projectile is within dome radius
if (distSq > domeRadius * domeRadius) return false;
// Check if projectile is moving inward (dot product of velocity and position relative to center)
double dot = dx * projectile.motionX + dy * projectile.motionY + dz * projectile.motionZ;
// Negative dot = moving toward center = incoming
return dot < 0;
}
// ==================== CHARGING ====================
@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;
}
}
}
// ==================== 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;
}
@Override
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double distance) {
double d = domeRadius * 8.0D;
d *= 64.0D;
return distance < d * d;
}
// ==================== GETTERS ====================
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.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.setFloat("TargetDomeRadius", targetDomeRadius);
nbt.setBoolean("FollowCaster", followCaster);
}
}
@@ -0,0 +1,373 @@
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 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 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 EntityEnergyBarrier {
public enum PanelMode {
PLACED, // Stationary wall
HELD, // Follows caster, rotates with caster look
LAUNCHED // Moves forward, deals damage/knockback on contact
}
// ==================== PANEL-SPECIFIC PROPERTIES ====================
protected EnergyPanelData panelData = new EnergyPanelData();
protected PanelMode mode = PanelMode.PLACED;
protected float panelYaw = 0.0f; // Rotation of the panel face (degrees)
// ==================== CHARGING (panel-specific targets) ====================
protected float targetPanelWidth;
protected float targetPanelHeight;
public EntityEnergyPanel(World world) {
super(world);
this.setSize(0.5f, 0.5f);
}
public EntityEnergyPanel(World world, EntityLivingBase owner, double x, double y, double z,
float yaw, PanelMode mode,
EnergyDisplayData display, EnergyLightningData lightning,
EnergyBarrierData barrier, EnergyPanelData panel) {
this(world);
this.setPosition(x, y + panel.heightOffset, z);
this.ownerEntityId = owner.getEntityId();
this.panelYaw = yaw;
this.mode = mode;
this.displayData = display;
this.lightningData = lightning;
this.barrierData = barrier;
this.panelData = panel;
this.currentHealth = barrier.maxHealth;
// For launched panels, set initial velocity
if (mode == PanelMode.LAUNCHED) {
float yawRad = (float) Math.toRadians(yaw);
this.motionX = -Math.sin(yawRad) * panel.launchSpeed;
this.motionY = 0;
this.motionZ = Math.cos(yawRad) * panel.launchSpeed;
}
}
@Override
public void onUpdate() {
this.prevPosX = this.posX;
this.prevPosY = this.posY;
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 (updateBarrierTick()) return;
// Knockback (not during launched mode)
if (!worldObj.isRemote && barrierData.knockbackEnabled && mode != PanelMode.LAUNCHED) {
knockbackEntities();
}
// Mode-specific updates
switch (mode) {
case HELD:
updateHeld();
break;
case LAUNCHED:
updateLaunched();
break;
case PLACED:
default:
// Stationary, no update needed
break;
}
}
private void updateHeld() {
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(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);
}
private void updateLaunched() {
// Move forward on both sides for smooth client rendering
this.posX += motionX;
this.posY += motionY;
this.posZ += motionZ;
this.setPosition(posX, posY, posZ);
// 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();
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 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();
}
}
}
// ==================== INCOMING CHECK ====================
/**
* Check if a projectile hits this panel surface.
* Only blocks incoming projectiles (not from the panel's owner).
*/
@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
Entity owner = getOwnerEntity();
Entity projOwner = projectile.getOwnerEntity();
if (owner instanceof EntityNPCInterface && projOwner instanceof EntityNPCInterface) {
if (((EntityNPCInterface) owner).faction.id == ((EntityNPCInterface) projOwner).faction.id) {
return false;
}
}
// Check if projectile is within panel bounds
float halfW = panelData.panelWidth * 0.5f;
float halfH = panelData.panelHeight * 0.5f;
float panelThickness = 0.5f; // Collision thickness
// Transform projectile position to panel-local space
float yawRad = (float) Math.toRadians(panelYaw);
double relX = projectile.posX - this.posX;
double relY = projectile.posY - this.posY;
double relZ = projectile.posZ - this.posZ;
// Rotate into panel space (panel faces along -sin(yaw), cos(yaw))
double cos = Math.cos(yawRad);
double sin = Math.sin(yawRad);
double localForward = relX * (-sin) + relZ * cos; // Distance along panel normal
double localRight = relX * cos + relZ * sin; // Distance along panel width
// Check bounds
if (Math.abs(localForward) > panelThickness) return false;
if (Math.abs(localRight) > halfW) return false;
if (Math.abs(relY) > halfH) return false;
// Check incoming direction via dot product with panel normal
double normalX = -Math.sin(yawRad);
double normalZ = Math.cos(yawRad);
double dot = projectile.motionX * normalX + projectile.motionZ * normalZ;
// Determine which side of the panel the projectile is on
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;
}
// ==================== CHARGING ====================
@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;
}
}
}
// ==================== 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;
}
@Override
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double distance) {
double d = Math.max(panelData.panelWidth, panelData.panelHeight) * 8.0D;
d *= 64.0D;
return distance < d * d;
}
// ==================== GETTERS ====================
public PanelMode getMode() { return mode; }
public float getPanelYaw() { return panelYaw; }
public EnergyPanelData getPanelData() { return panelData; }
// ==================== NBT ====================
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
readBarrierBaseNBT(nbt);
this.panelYaw = nbt.getFloat("PanelYaw");
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) {
writeBarrierBaseNBT(nbt);
nbt.setFloat("PanelYaw", panelYaw);
nbt.setInteger("PanelMode", mode.ordinal());
nbt.setFloat("TargetPanelWidth", targetPanelWidth);
nbt.setFloat("TargetPanelHeight", targetPanelHeight);
panelData.writeNBT(nbt);
}
}
@@ -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.AbilityPotionEffect;
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 {
@@ -80,17 +77,7 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
protected EnergyCombatData combatData = new EnergyCombatData();
// ==================== 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();
protected List<AbilityPotionEffect> effects = new ArrayList<>();
// ==================== 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
@@ -270,6 +235,13 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
}
}
// Check barrier collisions (server-side, non-preview, non-charging)
if (!previewMode && !worldObj.isRemote && !isCharging()) {
if (checkBarrierCollision()) {
return; // Projectile was absorbed by a barrier
}
}
// Subclass-specific update
updateProjectile();
@@ -310,6 +282,37 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
*/
protected abstract void updateProjectile();
// ==================== BARRIER COLLISION ====================
/**
* 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 - 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
);
@SuppressWarnings("unchecked")
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;
}
}
}
return false;
}
// ==================== POSITION INTERPOLATION ====================
@Override
@@ -353,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) {
@@ -430,7 +416,7 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
}
protected void applyEffects(EntityLivingBase target) {
for (AbilityEffect effect : effects) {
for (AbilityPotionEffect effect : effects) {
effect.apply(target);
}
}
@@ -438,25 +424,10 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
/**
* Set the effects list from the ability's configured effects.
*/
public void setEffects(List<AbilityEffect> effects) {
public void setEffects(List<AbilityPotionEffect> effects) {
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);
@@ -513,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).
@@ -677,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;
}
@@ -741,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;
}
@@ -768,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) {
@@ -804,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(); }
@@ -879,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
@@ -910,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
@@ -917,7 +789,7 @@ public abstract class EntityAbilityProjectile extends Entity implements IEntityA
if (nbt.hasKey("Effects")) {
NBTTagList effectsList = nbt.getTagList("Effects", 10); // 10 = compound tag
for (int i = 0; i < effectsList.tagCount(); i++) {
effects.add(AbilityEffect.fromNBT(effectsList.getCompoundTagAt(i)));
effects.add(AbilityPotionEffect.fromNBT(effectsList.getCompoundTagAt(i)));
}
}
@@ -927,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");
@@ -941,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);
}
@@ -952,12 +821,14 @@ 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
if (!effects.isEmpty()) {
NBTTagList effectsList = new NBTTagList();
for (AbilityEffect effect : effects) {
for (AbilityPotionEffect effect : effects) {
effectsList.appendTag(effect.writeNBT());
}
nbt.setTag("Effects", effectsList);
@@ -969,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);
@@ -978,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);
}
@@ -995,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;
}
}
@@ -0,0 +1,326 @@
package kamkeel.npcs.entity;
import kamkeel.npcs.controllers.data.ability.data.*;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import noppes.npcs.EventHooks;
import java.util.ArrayList;
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 EntityEnergyAbility to leverage the existing projectile infrastructure.
*/
public class EntityEnergySlicer extends EntityEnergyProjectile {
private float sliceWidth = 3.0f;
private float sliceThickness = 0.15f;
private boolean piercing = true;
private List<Integer> hitEntities = new ArrayList<>();
public EntityEnergySlicer(World world) {
super(world);
}
/**
* Full constructor with all parameters.
*/
public EntityEnergySlicer(World world, EntityLivingBase owner, EntityLivingBase target,
double x, double y, double z,
float sliceWidth, float sliceThickness,
EnergyDisplayData display, EnergyCombatData combat,
EnergyHomingData homing, EnergyLightningData lightning,
EnergyLifespanData lifespan, EnergyTrajectoryData trajectory,
boolean piercing) {
super(world);
initProjectile(owner, target, x, y, z, sliceWidth, display, combat, lightning, lifespan, trajectory);
this.homingData = homing;
this.sliceWidth = sliceWidth;
this.sliceThickness = sliceThickness;
this.piercing = piercing;
calculateInitialVelocity(owner, target, x, y, z);
}
// Targets stored during charging for grow animation
private float targetSliceWidth = 3.0f;
private float targetSliceThickness = 0.15f;
/**
* Setup charging with full target dimensions.
*/
public void setupSlicerCharging(EnergyAnchorData anchor, int chargeDuration, float width, float thickness) {
this.targetSliceWidth = width;
this.targetSliceThickness = thickness;
this.sliceWidth = 0.01f;
this.sliceThickness = 0.01f;
super.setupCharging(anchor, chargeDuration);
}
/**
* Setup preview mode.
*/
public void setupPreview(EntityLivingBase owner, float width, float thickness,
EnergyDisplayData display, EnergyLightningData lightning,
EnergyAnchorData anchor, int chargeDuration) {
this.setPreviewMode(true);
this.setPreviewOwner(owner);
this.displayData = display;
this.lightningData = lightning;
this.setCharging(true);
this.chargeDuration = chargeDuration;
this.chargeTick = 0;
this.anchorData = anchor;
this.targetSliceWidth = width;
this.targetSliceThickness = thickness;
this.sliceWidth = 0.01f;
this.sliceThickness = 0.01f;
this.size = 0.01f;
this.renderCurrentSize = 0.01f;
this.prevRenderSize = 0.01f;
Vec3 pos = kamkeel.npcs.util.AnchorPointHelper.calculateAnchorPosition(owner, anchorData);
this.setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
this.prevPosX = pos.xCoord;
this.prevPosY = pos.yCoord;
this.prevPosZ = pos.zCoord;
this.startX = pos.xCoord;
this.startY = pos.yCoord;
this.startZ = pos.zCoord;
this.motionX = 0;
this.motionY = 0;
this.motionZ = 0;
}
/**
* Start preview firing.
*/
public void startPreviewFiring() {
setCharging(false);
startX = posX;
startY = posY;
startZ = posZ;
prevPosX = posX;
prevPosY = posY;
prevPosZ = posZ;
Entity owner = getOwnerEntity();
if (owner != null) {
float yaw = (float) Math.toRadians(owner.rotationYaw);
motionX = -Math.sin(yaw) * getSpeed();
motionY = 0;
motionZ = Math.cos(yaw) * getSpeed();
} else {
motionX = getSpeed();
motionY = 0;
motionZ = 0;
}
}
/**
* Start the slicer moving (exit charging mode).
*/
public void startMoving(EntityLivingBase target) {
setCharging(false);
snapToPlayerLookVector();
startX = posX;
startY = posY;
startZ = posZ;
Entity owner = getOwnerEntity();
if (target != null) {
double dx = target.posX - posX;
double dy = (target.posY + target.getEyeHeight()) - posY;
double dz = target.posZ - posZ;
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (len > 0) {
motionX = (dx / len) * getSpeed();
motionY = (dy / len) * getSpeed();
motionZ = (dz / len) * getSpeed();
}
} else if (owner != null) {
float yaw = (float) Math.toRadians(owner.rotationYaw);
float pitch = (float) Math.toRadians(owner.rotationPitch);
motionX = -Math.sin(yaw) * Math.cos(pitch) * getSpeed();
motionY = -Math.sin(pitch) * getSpeed();
motionZ = Math.cos(yaw) * Math.cos(pitch) * getSpeed();
}
}
@Override
protected void updateRotation() {
// Slicer doesn't spin - it stays oriented perpendicular to travel direction
// No rotation update needed
}
@Override
protected void updateProjectile() {
if (isCharging()) {
updateCharging();
return;
}
if (previewMode) {
posX += motionX;
posY += motionY;
posZ += motionZ;
return;
}
if (worldObj.isRemote) {
handleClientInterpolation();
} else {
// No homing by default for slicer (straight line flight)
if (homingData.isHoming()) {
updateHoming();
}
checkBlockCollision();
checkEntityCollision();
this.moveEntity(motionX, motionY, motionZ);
}
// Wall collision
if (this.isCollidedHorizontally || this.isCollidedVertically) {
if (!worldObj.isRemote) {
hasHit = true;
if (isExplosive()) {
doExplosion();
}
}
this.setDead();
}
}
@Override
protected void updateCharging() {
chargeTick++;
float progress = getChargeProgress();
this.sliceWidth = targetSliceWidth * progress;
this.sliceThickness = targetSliceThickness * progress;
this.size = this.sliceWidth;
Entity owner = getOwnerEntity();
if (owner instanceof EntityLivingBase) {
Vec3 pos = kamkeel.npcs.util.AnchorPointHelper.calculateAnchorPosition((EntityLivingBase) owner, anchorData);
setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
}
}
private void checkBlockCollision() {
if (piercing) return; // Piercing slicers pass through blocks
Vec3 currentPos = Vec3.createVectorHelper(posX, posY, posZ);
Vec3 nextPos = Vec3.createVectorHelper(posX + motionX, posY + motionY, posZ + motionZ);
MovingObjectPosition blockHit = worldObj.func_147447_a(currentPos, nextPos, false, true, false);
if (blockHit != null && blockHit.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
if (!worldObj.isRemote) {
EventHooks.onEnergyProjectileBlockImpact(this, blockHit.blockX, blockHit.blockY, blockHit.blockZ);
}
hasHit = true;
this.setDead();
}
}
private void checkEntityCollision() {
float halfW = sliceWidth * 0.5f;
float halfH = sliceThickness * 0.5f;
AxisAlignedBB hitBox = AxisAlignedBB.getBoundingBox(
posX - halfW, posY - halfH, posZ - halfW,
posX + halfW, posY + halfH, posZ + halfW
);
@SuppressWarnings("unchecked")
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, hitBox);
for (EntityLivingBase entity : entities) {
if (shouldIgnoreEntity(entity)) continue;
if (hitEntities.contains(entity.getEntityId())) continue; // Already hit this entity
if (piercing) {
// Pierce: damage but keep flying
applyDamage(entity);
hitEntities.add(entity.getEntityId());
} else {
// Non-piercing: damage and die
hasHit = true;
if (isExplosive()) {
doExplosion();
} else {
applyDamage(entity);
}
this.setDead();
return;
}
}
}
/**
* Get the yaw angle from motion vector for orientation.
*/
public float getTravelYaw() {
if (isCharging()) {
Entity owner = previewMode ? previewOwner : getOwnerEntity();
if (owner != null) return owner.rotationYaw;
}
double speed = Math.sqrt(motionX * motionX + motionZ * motionZ);
if (speed > 0.001) {
return (float) (Math.atan2(-motionX, motionZ) * 180.0 / Math.PI);
}
return 0.0f;
}
/**
* Get the pitch angle from motion vector.
*/
public float getTravelPitch() {
double horizSpeed = Math.sqrt(motionX * motionX + motionZ * motionZ);
if (horizSpeed > 0.001 || Math.abs(motionY) > 0.001) {
return (float) (-Math.atan2(motionY, horizSpeed) * 180.0 / Math.PI);
}
return 0.0f;
}
// ==================== GETTERS ====================
public float getSliceWidth() { return sliceWidth; }
public void setSliceWidth(float width) { this.sliceWidth = width; }
public float getSliceThickness() { return sliceThickness; }
public void setSliceThickness(float thickness) { this.sliceThickness = thickness; }
public boolean isPiercing() { return piercing; }
public void setPiercing(boolean piercing) { this.piercing = piercing; }
// ==================== NBT ====================
@Override
protected void readProjectileNBT(NBTTagCompound nbt) {
readChargingNBT(nbt);
this.sliceWidth = nbt.hasKey("SliceWidth") ? nbt.getFloat("SliceWidth") : 3.0f;
this.sliceThickness = nbt.hasKey("SliceThickness") ? nbt.getFloat("SliceThickness") : 0.15f;
this.piercing = !nbt.hasKey("Piercing") || nbt.getBoolean("Piercing");
this.targetSliceWidth = nbt.hasKey("TargetSliceWidth") ? nbt.getFloat("TargetSliceWidth") : sliceWidth;
this.targetSliceThickness = nbt.hasKey("TargetSliceThickness") ? nbt.getFloat("TargetSliceThickness") : sliceThickness;
}
@Override
protected void writeProjectileNBT(NBTTagCompound nbt) {
writeChargingNBT(nbt);
nbt.setFloat("SliceWidth", sliceWidth);
nbt.setFloat("SliceThickness", sliceThickness);
nbt.setBoolean("Piercing", piercing);
nbt.setFloat("TargetSliceWidth", targetSliceWidth);
nbt.setFloat("TargetSliceThickness", targetSliceThickness);
}
}
@@ -92,6 +92,9 @@ import kamkeel.npcs.entity.EntityAbilityLaser;
import kamkeel.npcs.entity.EntityAbilityBeam;
import kamkeel.npcs.entity.EntityAbilitySweeper;
import kamkeel.npcs.entity.EntityAbilityZone;
import kamkeel.npcs.entity.EntityEnergyDome;
import kamkeel.npcs.entity.EntityEnergyPanel;
import kamkeel.npcs.entity.EntityEnergySlicer;
import noppes.npcs.entity.old.EntityNPCDwarfFemale;
import noppes.npcs.entity.old.EntityNPCDwarfMale;
import noppes.npcs.entity.old.EntityNPCElfFemale;
@@ -294,6 +297,9 @@ public class CustomNpcs {
registerNewEntity(EntityAbilityBeam.class, "abilitybeam", 64, 3, true);
registerNewEntity(EntityAbilitySweeper.class, "abilitysweeper", 64, 3, true);
registerNewEntity(EntityAbilityZone.class, "abilityzone", 64, 3, true);
registerNewEntity(EntityEnergyDome.class, "energydome", 64, 3, true);
registerNewEntity(EntityEnergyPanel.class, "energypanel", 64, 3, true);
registerNewEntity(EntityEnergySlicer.class, "energyslicer", 64, 3, true);
new RecipeController();
+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);
@@ -152,6 +152,9 @@ import kamkeel.npcs.client.renderer.RenderAbilityLaser;
import kamkeel.npcs.client.renderer.RenderAbilityBeam;
import kamkeel.npcs.client.renderer.RenderAbilitySweeper;
import kamkeel.npcs.client.renderer.RenderAbilityZone;
import kamkeel.npcs.client.renderer.RenderEnergyDome;
import kamkeel.npcs.client.renderer.RenderEnergyPanel;
import kamkeel.npcs.client.renderer.RenderEnergySlicer;
import kamkeel.npcs.client.renderer.lightning.LightningHandler;
import kamkeel.npcs.entity.EntityAbilityOrb;
import kamkeel.npcs.entity.EntityAbilityDisc;
@@ -159,6 +162,9 @@ import kamkeel.npcs.entity.EntityAbilityLaser;
import kamkeel.npcs.entity.EntityAbilityBeam;
import kamkeel.npcs.entity.EntityAbilitySweeper;
import kamkeel.npcs.entity.EntityAbilityZone;
import kamkeel.npcs.entity.EntityEnergyDome;
import kamkeel.npcs.entity.EntityEnergyPanel;
import kamkeel.npcs.entity.EntityEnergySlicer;
import noppes.npcs.client.renderer.blocks.BlockBannerRenderer;
import noppes.npcs.client.renderer.blocks.BlockBarrelRenderer;
import noppes.npcs.client.renderer.blocks.BlockBeamRenderer;
@@ -273,6 +279,9 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityAbilityBeam.class, new RenderAbilityBeam());
RenderingRegistry.registerEntityRenderingHandler(EntityAbilitySweeper.class, new RenderAbilitySweeper());
RenderingRegistry.registerEntityRenderingHandler(EntityAbilityZone.class, new RenderAbilityZone());
RenderingRegistry.registerEntityRenderingHandler(EntityEnergyDome.class, new RenderEnergyDome());
RenderingRegistry.registerEntityRenderingHandler(EntityEnergyPanel.class, new RenderEnergyPanel());
RenderingRegistry.registerEntityRenderingHandler(EntityEnergySlicer.class, new RenderEnergySlicer());
RenderingRegistry.registerEntityRenderingHandler(EntityCustomNpc.class, new RenderCustomNpc());
@@ -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;
}
}
}