mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 01:27:27 +00:00
numerous fixes to pillars (now they work!)
This commit is contained in:
1 parent
0222a4984e
commit
a2beef0549
4 files changed
+260
-149
No files matched your search
@@ -2,12 +2,13 @@ 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.EntityAbilityPillar;
|
||||
import kamkeel.npcs.entity.EntityAbilityPillar.PillarOrigin;
|
||||
import kamkeel.npcs.entity.EntityAbilityPillar.PillarShape;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.entity.Entity;
|
||||
import noppes.npcs.LogWriter;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
@@ -17,12 +18,13 @@ import org.lwjgl.opengl.GL11;
|
||||
* - CIRCLE: cylindrical pillar rendered with triangulated segments.
|
||||
* - SQUARE: rectangular prism pillar.
|
||||
*
|
||||
* Both modes render three color layers (outer glow, mid, inner core)
|
||||
* following the same pattern as RenderEnergyLaser.
|
||||
* Both modes render three color layers (outer glow, mid, inner core).
|
||||
*
|
||||
* Origin modes:
|
||||
* - FROM_GROUND: base at posY, tip grows upward.
|
||||
* - FROM_ABOVE: base at posY + height, tip grows downward.
|
||||
* - FROM_GROUND: base at posY, pillar grows upward toward posY + height.
|
||||
* - FROM_ABOVE: pillar falls from above, tip at posY, grows downward.
|
||||
* The entity posY is always the ground anchor point.
|
||||
* Visually: base = posY + targetHeight, tip grows down toward posY.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class RenderEnergyPillar extends RenderEnergy {
|
||||
@@ -34,12 +36,6 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
EntityAbilityPillar pillar = (EntityAbilityPillar) entity;
|
||||
|
||||
noppes.npcs.LogWriter.info("RenderEnergyPillar: tick=" + pillar.ticksExisted
|
||||
+ " charging=" + pillar.isCharging()
|
||||
+ " growing=" + pillar.isGrowing()
|
||||
+ " radius=" + pillar.getPillarRadius()
|
||||
+ " height=" + pillar.getPillarHeight());
|
||||
|
||||
if (shouldSkipInitialActiveRender(pillar)) return;
|
||||
|
||||
setupRenderState();
|
||||
@@ -60,17 +56,22 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve Y extents based on origin
|
||||
// FROM_GROUND: base at y, tip at y+height (grows up).
|
||||
// FROM_ABOVE: pillar descends from above. The entity's posY is the ground anchor.
|
||||
// The full column height is targetHeight. The current height is how far
|
||||
// it has grown. So the visible column spans from:
|
||||
// tip = posY + targetHeight - height (starts high, descends)
|
||||
// base = posY + targetHeight (fixed ceiling)
|
||||
float baseY, tipY;
|
||||
float targetHeight = pillar.getPillarData().targetHeight;
|
||||
if (pillar.getPillarOrigin() == PillarOrigin.FROM_ABOVE) {
|
||||
baseY = (float) y + height;
|
||||
tipY = (float) y;
|
||||
baseY = (float) y + targetHeight;
|
||||
tipY = (float) y + targetHeight - height;
|
||||
} else {
|
||||
baseY = (float) y;
|
||||
tipY = (float) y + height;
|
||||
}
|
||||
|
||||
// Rotation around Y axis
|
||||
float rotationAngle = 0f;
|
||||
float rotationSpeed = pillar.getRotationSpeed();
|
||||
if (rotationSpeed != 0f) {
|
||||
@@ -88,15 +89,12 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
float midRadius = innerRadius + (outerRadius - innerRadius) * 0.5f;
|
||||
|
||||
GL11.glDepthMask(false);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z,
|
||||
outerRadius, outerColor, outerAlpha * 0.4f, rotationAngle);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z,
|
||||
midRadius, outerColor, outerAlpha * 0.7f, rotationAngle);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z, outerRadius, outerColor, outerAlpha * 0.4f, rotationAngle);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z, midRadius, outerColor, outerAlpha * 0.7f, rotationAngle);
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z,
|
||||
innerRadius, innerColor, innerAlpha, rotationAngle);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z, innerRadius, innerColor, innerAlpha, rotationAngle);
|
||||
|
||||
if (pillar.hasLightningEffect()) {
|
||||
float midWorldY = (baseY + tipY) * 0.5f;
|
||||
@@ -119,14 +117,13 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
|
||||
if (renderRadius <= 0.01f) return;
|
||||
|
||||
// Pulse effect
|
||||
float pulseTime = pillar.ticksExisted + partialTicks;
|
||||
float pulse = (float) Math.sin(pulseTime * 0.2f) * 0.08f;
|
||||
renderRadius *= (1.0f + pulse);
|
||||
|
||||
float height = 0.05f; // MIN_HEIGHT — flat disc during charging
|
||||
// During charging, render a flat disc at ground level
|
||||
float baseY = (float) y;
|
||||
float tipY = baseY + height;
|
||||
float tipY = baseY + 0.05f;
|
||||
|
||||
float rotationAngle = pillar.getRotationSpeed() != 0f
|
||||
? (pillar.ticksExisted + partialTicks) * pillar.getRotationSpeed()
|
||||
@@ -139,7 +136,7 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
|
||||
if (pillar.hasLightningEffect()) {
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslated(x, y + height * 0.5f, z);
|
||||
GL11.glTranslated(x, y + 0.025f, z);
|
||||
renderAttachedLightning(pillar, INNER_SCALE, renderRadius);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
@@ -150,15 +147,12 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
float midRadius = innerRadius + (outerRadius - innerRadius) * 0.5f;
|
||||
|
||||
GL11.glDepthMask(false);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z,
|
||||
outerRadius, outerColor, outerAlpha * 0.4f, rotationAngle);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z,
|
||||
midRadius, outerColor, outerAlpha * 0.7f, rotationAngle);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z, outerRadius, outerColor, outerAlpha * 0.4f, rotationAngle);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z, midRadius, outerColor, outerAlpha * 0.7f, rotationAngle);
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z,
|
||||
innerRadius, innerColor, innerAlpha, rotationAngle);
|
||||
renderPillarShape(pillar.getPillarShape(), x, baseY, tipY, z, innerRadius, innerColor, innerAlpha, rotationAngle);
|
||||
}
|
||||
|
||||
// ==================== SHAPE DISPATCH ====================
|
||||
@@ -175,10 +169,6 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
|
||||
// ==================== CYLINDER ====================
|
||||
|
||||
/**
|
||||
* Render a cylindrical pillar with lateral faces and top/bottom caps.
|
||||
* Rotation is applied around Y axis.
|
||||
*/
|
||||
private void renderCylinderPillar(double cx, float baseY, float tipY, double cz,
|
||||
float radius, int color, float alpha, float rotationAngle) {
|
||||
float[] rgb = extractRGB(color);
|
||||
@@ -203,11 +193,9 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
float z1 = (float) cz + sinTable[i] * radius;
|
||||
float x2 = (float) cx + cosTable[i + 1] * radius;
|
||||
float z2 = (float) cz + sinTable[i + 1] * radius;
|
||||
|
||||
float nx = (cosTable[i] + cosTable[i + 1]) * 0.5f;
|
||||
float nz = (sinTable[i] + sinTable[i + 1]) * 0.5f;
|
||||
tess.setNormal(nx, 0, nz);
|
||||
|
||||
tess.addVertex(x1, baseY, z1);
|
||||
tess.addVertex(x2, baseY, z2);
|
||||
tess.addVertex(x2, tipY, z2);
|
||||
@@ -250,24 +238,18 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
|
||||
// ==================== SQUARE ====================
|
||||
|
||||
/**
|
||||
* Render a rectangular prism pillar with four lateral faces and top/bottom caps.
|
||||
* Rotation is applied around Y axis.
|
||||
*/
|
||||
private void renderSquarePillar(double cx, float baseY, float tipY, double cz,
|
||||
float radius, int color, float alpha, float rotationAngle) {
|
||||
float[] rgb = extractRGB(color);
|
||||
float r = rgb[0], g = rgb[1], b = rgb[2];
|
||||
|
||||
// Four corners of the square, rotated around Y
|
||||
double rotRad = Math.toRadians(rotationAngle);
|
||||
float cosA = (float) Math.cos(rotRad);
|
||||
float sinA = (float) Math.sin(rotRad);
|
||||
|
||||
// Local corners before rotation: (±radius, ±radius)
|
||||
float[][] corners = new float[4][2];
|
||||
float[] lx = {-radius, radius, radius, -radius};
|
||||
float[] lz = {-radius, -radius, radius, radius};
|
||||
float[][] corners = new float[4][2];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
corners[i][0] = (float) cx + lx[i] * cosA - lz[i] * sinA;
|
||||
corners[i][1] = (float) cz + lx[i] * sinA + lz[i] * cosA;
|
||||
@@ -277,19 +259,15 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
tess.startDrawingQuads();
|
||||
tess.setColorRGBA_F(r, g, b, alpha);
|
||||
|
||||
// Four lateral faces
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int next = (i + 1) % 4;
|
||||
float x1 = corners[i][0], z1 = corners[i][1];
|
||||
float x2 = corners[next][0], z2 = corners[next][1];
|
||||
|
||||
// Outward normal (midpoint of edge, normalized)
|
||||
float nx = (x1 + x2) * 0.5f - (float) cx;
|
||||
float nz = (z1 + z2) * 0.5f - (float) cz;
|
||||
float nlen = (float) Math.sqrt(nx * nx + nz * nz);
|
||||
if (nlen > 0) { nx /= nlen; nz /= nlen; }
|
||||
tess.setNormal(nx, 0, nz);
|
||||
|
||||
tess.addVertex(x1, baseY, z1);
|
||||
tess.addVertex(x2, baseY, z2);
|
||||
tess.addVertex(x2, tipY, z2);
|
||||
@@ -314,7 +292,7 @@ public class RenderEnergyPillar extends RenderEnergy {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected net.minecraft.util.ResourceLocation getEntityTexture(Entity entity) {
|
||||
protected ResourceLocation getEntityTexture(Entity entity) {
|
||||
return WHITE_TEXTURE;
|
||||
}
|
||||
}
|
||||
+128
-50
@@ -6,9 +6,9 @@ import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyPillarData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.HitType;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
@@ -27,10 +27,16 @@ import java.util.List;
|
||||
* or fall from the sky.
|
||||
*
|
||||
* ANCHORED: pillar spawns at the target's position and stays there.
|
||||
* MOVING: pillar spawns in front of the caster and travels forward,
|
||||
* optionally homing toward a target.
|
||||
* When count > 1, each subsequent pillar recalculates a random position
|
||||
* near the target at spawn time.
|
||||
*
|
||||
* MOVING: pillar spawns in front of the caster and travels forward.
|
||||
* With homing: steers toward target.
|
||||
* Without homing: straight line along caster look vector or toward target.
|
||||
* When count > 1, pillarOffset spaces pillars apart along the travel direction.
|
||||
*
|
||||
* Telegraph shape matches PillarShape (CIRCLE or SQUARE).
|
||||
* In MOVING mode, telegraph appears at the spawn point in front of the caster.
|
||||
*/
|
||||
public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
|
||||
@@ -39,6 +45,12 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
private EnergyPillarData pillarData = new EnergyPillarData();
|
||||
private EnergyHomingData homingData = new EnergyHomingData();
|
||||
|
||||
/**
|
||||
* Spacing between pillars in MOVING mode when count > 1.
|
||||
* 0 = all spawn at the same position.
|
||||
*/
|
||||
private float pillarOffset = 0f;
|
||||
|
||||
// ==================== CONSTRUCTOR ====================
|
||||
|
||||
public AbilityPillar() {
|
||||
@@ -47,16 +59,8 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
new EnergyCombatData(),
|
||||
new EnergyLifespanData()
|
||||
);
|
||||
this.typeId = "ability.cnpc.pillar";
|
||||
this.name = "Pillar";
|
||||
this.targetingMode = TargetingMode.AGGRO_TARGET;
|
||||
this.maxRange = 25.0f;
|
||||
this.minRange = 5.0f;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 30;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.telegraphType = pillarData.shape.getTelegraphType();
|
||||
this.showTelegraph = true;
|
||||
combatData.hitType = HitType.MULTI;
|
||||
combatData.multiHitDelayTicks = 20;
|
||||
}
|
||||
|
||||
// ==================== ABSTRACT IMPLEMENTATIONS ====================
|
||||
@@ -77,6 +81,8 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
|
||||
@Override
|
||||
protected void setupEntityCharging(EntityAbilityPillar entity, int index) {
|
||||
// The charge visual should appear at ground level at the spawn position,
|
||||
// not following the caster anchor. Position is already set by createEntity.
|
||||
entity.setupCharging(windUpTicks);
|
||||
}
|
||||
|
||||
@@ -101,42 +107,81 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
@Override
|
||||
protected double[] getSpawnPosition(EntityLivingBase caster, EntityLivingBase target, int index) {
|
||||
if (pillarData.mode == PillarMode.MOVING) {
|
||||
// Spawn in front of caster along look vector
|
||||
Vec3 look = caster.getLookVec();
|
||||
if (look != null) {
|
||||
float dist = Math.max(1.0f, pillarData.targetRadius);
|
||||
return new double[]{
|
||||
caster.posX + look.xCoord * dist,
|
||||
caster.posY,
|
||||
caster.posZ + look.zCoord * dist
|
||||
};
|
||||
if (look == null) {
|
||||
return new double[]{caster.posX, caster.posY, caster.posZ};
|
||||
}
|
||||
return new double[]{caster.posX, caster.posY, caster.posZ};
|
||||
float baseDist = Math.max(1.0f, pillarData.targetRadius);
|
||||
float offsetDist = baseDist + pillarOffset * index;
|
||||
return new double[]{
|
||||
caster.posX + look.xCoord * offsetDist,
|
||||
caster.posY,
|
||||
caster.posZ + look.zCoord * offsetDist
|
||||
};
|
||||
}
|
||||
|
||||
// ANCHORED: use pre-calculated position or target position
|
||||
return super.getSpawnPosition(caster, target, index);
|
||||
// ANCHORED: index 0 uses pre-calculated telegraph position.
|
||||
// Subsequent indices recalculate a random nearby position at spawn time.
|
||||
if (index == 0) {
|
||||
return super.getSpawnPosition(caster, target, index);
|
||||
}
|
||||
|
||||
// For index > 0, find a new random position near the target
|
||||
double baseX = target != null ? target.posX : caster.posX;
|
||||
double baseZ = target != null ? target.posZ : caster.posZ;
|
||||
double baseY = target != null ? target.posY : caster.posY;
|
||||
|
||||
float spawnRadius = Math.max(pillarData.targetRadius * 2.0f, 3.0f);
|
||||
double angle = Math.random() * Math.PI * 2;
|
||||
double dist = Math.sqrt(Math.random()) * spawnRadius;
|
||||
return new double[]{
|
||||
baseX + Math.cos(angle) * dist,
|
||||
baseY,
|
||||
baseZ + Math.sin(angle) * dist
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== EXECUTE — startGrowing ====================
|
||||
// ==================== EXECUTE ====================
|
||||
|
||||
@Override
|
||||
protected void spawnZoneAt(EntityLivingBase caster, EntityLivingBase target, int index) {
|
||||
super.spawnZoneAt(caster, target, index);
|
||||
EntityAbilityPillar entity = entities[index];
|
||||
if (entity != null && !entity.isDead) {
|
||||
if (target != null) {
|
||||
entity.setTarget(target);
|
||||
}
|
||||
entity.startGrowing();
|
||||
if (entity == null || entity.isDead) return;
|
||||
|
||||
if (target != null) {
|
||||
entity.setTarget(target);
|
||||
}
|
||||
|
||||
// Set straight-line motion for MOVING mode without homing
|
||||
if (pillarData.mode == PillarMode.MOVING && !homingData.isHoming()) {
|
||||
double mx = 0, mz = 0;
|
||||
if (target != null) {
|
||||
double dx = target.posX - entity.posX;
|
||||
double dz = target.posZ - entity.posZ;
|
||||
double len = Math.sqrt(dx * dx + dz * dz);
|
||||
if (len > 0) {
|
||||
mx = (dx / len) * homingData.getSpeed();
|
||||
mz = (dz / len) * homingData.getSpeed();
|
||||
}
|
||||
} else {
|
||||
Vec3 look = caster.getLookVec();
|
||||
if (look != null) {
|
||||
mx = look.xCoord * homingData.getSpeed();
|
||||
mz = look.zCoord * homingData.getSpeed();
|
||||
}
|
||||
}
|
||||
entity.setInitialMotion(mx, mz);
|
||||
}
|
||||
|
||||
entity.startGrowing();
|
||||
}
|
||||
|
||||
// ==================== TELEGRAPH ====================
|
||||
|
||||
@Override
|
||||
public TelegraphInstance createTelegraph(EntityLivingBase caster, EntityLivingBase target) {
|
||||
if (!showTelegraph || telegraphType == TelegraphType.NONE || isPlayerCaster(caster) || target == null) {
|
||||
if (!showTelegraph || telegraphType == TelegraphType.NONE || isPlayerCaster(caster)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -153,17 +198,44 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
telegraph.setWarningStartTick(Math.max(5, windUpTicks / 4));
|
||||
telegraph.setHeightOffset(telegraphHeightOffset);
|
||||
|
||||
double groundY = findGroundLevel(caster.worldObj, target.posX, target.posY, target.posZ);
|
||||
double telegraphX, telegraphZ, telegraphY;
|
||||
|
||||
// Store position for use in getSpawnPosition (ANCHORED mode)
|
||||
preCalculatedPositions.clear();
|
||||
for (int i = 0; i < zoneCount; i++) {
|
||||
preCalculatedPositions.add(new double[]{target.posX, groundY, target.posZ});
|
||||
if (pillarData.mode == PillarMode.MOVING) {
|
||||
// Telegraph appears at the spawn point in front of the caster
|
||||
Vec3 look = caster.getLookVec();
|
||||
if (look != null) {
|
||||
float dist = Math.max(1.0f, pillarData.targetRadius);
|
||||
telegraphX = caster.posX + look.xCoord * dist;
|
||||
telegraphZ = caster.posZ + look.zCoord * dist;
|
||||
} else {
|
||||
telegraphX = caster.posX;
|
||||
telegraphZ = caster.posZ;
|
||||
}
|
||||
telegraphY = findGroundLevel(caster.worldObj, telegraphX, caster.posY, telegraphZ);
|
||||
|
||||
preCalculatedPositions.clear();
|
||||
preCalculatedPositions.add(new double[]{telegraphX, telegraphY, telegraphZ});
|
||||
} else {
|
||||
// ANCHORED: telegraph at target position
|
||||
if (target == null) return null;
|
||||
telegraphX = target.posX;
|
||||
telegraphZ = target.posZ;
|
||||
telegraphY = findGroundLevel(caster.worldObj, telegraphX, target.posY, telegraphZ);
|
||||
|
||||
preCalculatedPositions.clear();
|
||||
preCalculatedPositions.add(new double[]{telegraphX, telegraphY, telegraphZ});
|
||||
}
|
||||
|
||||
TelegraphInstance instance = new TelegraphInstance(telegraph, target.posX, groundY, target.posZ, caster.rotationYaw);
|
||||
TelegraphInstance instance = new TelegraphInstance(
|
||||
telegraph, telegraphX, telegraphY, telegraphZ, caster.rotationYaw);
|
||||
instance.setCasterEntityId(caster.getEntityId());
|
||||
instance.setEntityIdToFollow(homingData.isHoming() ? target.getEntityId() : -1);
|
||||
|
||||
// In ANCHORED+homing, telegraph follows the target
|
||||
if (pillarData.mode == PillarMode.ANCHORED && homingData.isHoming() && target != null) {
|
||||
instance.setEntityIdToFollow(target.getEntityId());
|
||||
} else {
|
||||
instance.setEntityIdToFollow(-1);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
@@ -179,12 +251,14 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
protected void writeTypeSpecificNBT(NBTTagCompound nbt) {
|
||||
pillarData.writeNBT(nbt);
|
||||
homingData.writeNBT(nbt);
|
||||
nbt.setFloat("PillarOffset", pillarOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readTypeSpecificNBT(NBTTagCompound nbt) {
|
||||
pillarData.readNBT(nbt);
|
||||
homingData.readNBT(nbt);
|
||||
this.pillarOffset = nbt.hasKey("PillarOffset") ? nbt.getFloat("PillarOffset") : 0f;
|
||||
}
|
||||
|
||||
// ==================== GUI ====================
|
||||
@@ -195,16 +269,13 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
defs.add(FieldDef.section("ability.section.pillar"));
|
||||
|
||||
defs.add(FieldDef.enumField("ability.pillarMode", PillarMode.class,
|
||||
() -> pillarData.mode,
|
||||
v -> pillarData.mode = v));
|
||||
() -> pillarData.mode, v -> pillarData.mode = v));
|
||||
|
||||
defs.add(FieldDef.enumField("ability.pillarOrigin", EntityAbilityPillar.PillarOrigin.class,
|
||||
() -> pillarData.origin,
|
||||
v -> pillarData.origin = v));
|
||||
() -> pillarData.origin, v -> pillarData.origin = v));
|
||||
|
||||
defs.add(FieldDef.enumField("ability.pillarShape", PillarShape.class,
|
||||
() -> pillarData.shape,
|
||||
v -> pillarData.shape = v));
|
||||
() -> pillarData.shape, v -> pillarData.shape = v));
|
||||
|
||||
defs.add(FieldDef.row(
|
||||
FieldDef.floatField("ability.pillar.targetRadius", () -> pillarData.targetRadius, v -> pillarData.targetRadius = v).range(0.1f, 64f),
|
||||
@@ -219,9 +290,17 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
defs.add(FieldDef.intField("ability.pillar.spawnDelay", () -> pillarData.spawnDelay, v -> pillarData.spawnDelay = Math.max(0, v))
|
||||
.range(0, 200));
|
||||
|
||||
defs.add(FieldDef.section("ability.section.homing")
|
||||
defs.add(FieldDef.floatField("ability.pillar.offset", () -> pillarOffset, v -> pillarOffset = Math.max(0f, v))
|
||||
.visibleWhen(() -> zoneCount > 1)
|
||||
.range(0f, 20f));
|
||||
|
||||
defs.add(FieldDef.section("ability.section.movement")
|
||||
.visibleWhen(() -> pillarData.mode == PillarMode.MOVING));
|
||||
|
||||
defs.add(FieldDef.floatField("ability.speed", () -> homingData.getSpeed(), v -> homingData.setSpeed(v))
|
||||
.visibleWhen(() -> pillarData.mode == PillarMode.MOVING)
|
||||
.range(0.01f, 10f));
|
||||
|
||||
defs.add(FieldDef.boolField("ability.homing", () -> homingData.isHoming(), v -> homingData.setHoming(v))
|
||||
.visibleWhen(() -> pillarData.mode == PillarMode.MOVING));
|
||||
|
||||
@@ -230,10 +309,6 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
FieldDef.floatField("ability.homingRange", () -> homingData.getHomingRange(), v -> homingData.setHomingRange(v))
|
||||
).visibleWhen(() -> pillarData.mode == PillarMode.MOVING && homingData.isHoming()));
|
||||
|
||||
defs.add(FieldDef.floatField("ability.speed", () -> homingData.getSpeed(), v -> homingData.setSpeed(v))
|
||||
.visibleWhen(() -> pillarData.mode == PillarMode.MOVING)
|
||||
.range(0.01f, 10f));
|
||||
|
||||
defs.add(FieldDef.section("ability.section.combat"));
|
||||
|
||||
defs.add(FieldDef.floatField("ability.damage", this::getDamage, this::setDamage).range(0f, 1000f));
|
||||
@@ -248,4 +323,7 @@ public class AbilityPillar extends AbilityEnergyZone<EntityAbilityPillar> {
|
||||
|
||||
public EnergyHomingData getHomingData() { return homingData; }
|
||||
public void setHomingData(EnergyHomingData data) { this.homingData = data != null ? data : new EnergyHomingData(); }
|
||||
|
||||
public float getPillarOffset() { return pillarOffset; }
|
||||
public void setPillarOffset(float offset) { this.pillarOffset = Math.max(0f, offset); }
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package kamkeel.npcs.entity;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyPillarData;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyPillarData;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
@@ -19,7 +22,8 @@ import java.util.List;
|
||||
* Behavior modes:
|
||||
* - ANCHORED: spawns at a fixed position and stays there.
|
||||
* - MOVING: spawns in front of the caster and travels forward,
|
||||
* optionally homing toward a target.
|
||||
* optionally homing toward a target. Without homing, travels in a straight
|
||||
* line along the initial direction set by setInitialMotion().
|
||||
*
|
||||
* Lifecycle:
|
||||
* 1. Charging — pillarRadius grows from 0 to targetRadius; pillarHeight stays at MIN_HEIGHT.
|
||||
@@ -35,6 +39,10 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
|
||||
private static final float MIN_HEIGHT = 0.05f;
|
||||
|
||||
// DataWatcher slots (base uses 20 for charging)
|
||||
private static final int DW_PILLAR_RADIUS = 21;
|
||||
private static final int DW_PILLAR_HEIGHT = 22;
|
||||
|
||||
// ==================== ENUMS ====================
|
||||
|
||||
public enum PillarMode {
|
||||
@@ -52,11 +60,8 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
SQUARE;
|
||||
|
||||
public TelegraphType getTelegraphType() {
|
||||
if (this == CIRCLE) {
|
||||
return TelegraphType.CIRCLE;
|
||||
} else {
|
||||
return TelegraphType.SQUARE;
|
||||
}
|
||||
if (this == CIRCLE) return TelegraphType.CIRCLE;
|
||||
else return TelegraphType.SQUARE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +69,13 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
|
||||
private EnergyPillarData pillarData = new EnergyPillarData();
|
||||
|
||||
// ==================== PILLAR DIMENSIONS ====================
|
||||
// ==================== PILLAR DIMENSIONS — LOCAL ====================
|
||||
|
||||
private float pillarRadius = 0.01f;
|
||||
private float pillarHeight = MIN_HEIGHT;
|
||||
|
||||
// ==================== PILLAR DIMENSIONS — RENDER INTERPOLATION ====================
|
||||
|
||||
private float renderPillarRadius = 0.01f;
|
||||
private float renderPillarHeight = MIN_HEIGHT;
|
||||
|
||||
@@ -107,26 +114,52 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
|
||||
snapToGround(x, y, z);
|
||||
|
||||
this.pillarRadius = 0.01f;
|
||||
this.pillarHeight = MIN_HEIGHT;
|
||||
setVisualRadius(0.01f);
|
||||
setVisualHeight(MIN_HEIGHT);
|
||||
|
||||
setPillarRadiusInternal(0.01f);
|
||||
setPillarHeightInternal(MIN_HEIGHT);
|
||||
syncHitbox();
|
||||
}
|
||||
|
||||
// ==================== ENTITY INIT ====================
|
||||
|
||||
@Override
|
||||
protected void entityInit() {
|
||||
super.entityInit();
|
||||
this.dataWatcher.addObject(DW_PILLAR_RADIUS, 0.01f);
|
||||
this.dataWatcher.addObject(DW_PILLAR_HEIGHT, MIN_HEIGHT);
|
||||
}
|
||||
|
||||
// ==================== INTERNAL DIMENSION SETTERS ====================
|
||||
|
||||
private void setPillarRadiusInternal(float value) {
|
||||
this.pillarRadius = value;
|
||||
if (!previewMode && worldObj != null && !worldObj.isRemote) {
|
||||
this.dataWatcher.updateObject(DW_PILLAR_RADIUS, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setPillarHeightInternal(float value) {
|
||||
this.pillarHeight = value;
|
||||
if (!previewMode && worldObj != null && !worldObj.isRemote) {
|
||||
this.dataWatcher.updateObject(DW_PILLAR_HEIGHT, value);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== SETUP ====================
|
||||
|
||||
/**
|
||||
* Set the entity this pillar tracks in MOVING+homing mode.
|
||||
*/
|
||||
public void setTarget(EntityLivingBase target) {
|
||||
this.targetEntityId = target != null ? target.getEntityId() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up for GUI preview rendering.
|
||||
* Set straight-line motion for MOVING mode without homing.
|
||||
* Called by the ability before spawning, using the caster's look vector or
|
||||
* direction toward target.
|
||||
*/
|
||||
public void setInitialMotion(double mx, double mz) {
|
||||
this.motionX = mx;
|
||||
this.motionZ = mz;
|
||||
}
|
||||
|
||||
public void setupPreview(EntityLivingBase owner, EnergyPillarData pillarData,
|
||||
EnergyDisplayData display, EnergyLightningData lightning,
|
||||
int chargeDuration) {
|
||||
@@ -135,10 +168,12 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
this.displayData = display != null ? display.copy() : new EnergyDisplayData();
|
||||
this.lightningData = lightning != null ? lightning.copy() : new EnergyLightningData();
|
||||
setupCharging(chargeDuration);
|
||||
this.pillarRadius = 0.01f;
|
||||
this.pillarHeight = MIN_HEIGHT;
|
||||
setVisualRadius(0.01f);
|
||||
setVisualHeight(MIN_HEIGHT);
|
||||
setPillarRadiusInternal(0.01f);
|
||||
setPillarHeightInternal(MIN_HEIGHT);
|
||||
this.renderPillarRadius = 0.01f;
|
||||
this.prevRenderPillarRadius = 0.01f;
|
||||
this.renderPillarHeight = MIN_HEIGHT;
|
||||
this.prevRenderPillarHeight = MIN_HEIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,6 +184,9 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
setCharging(false);
|
||||
this.spawnDelayTick = 0;
|
||||
this.growing = pillarData.spawnDelay <= 0;
|
||||
setPillarRadiusInternal(pillarData.targetRadius);
|
||||
setPillarHeightInternal(MIN_HEIGHT);
|
||||
syncHitbox();
|
||||
}
|
||||
|
||||
// ==================== CHARGING ====================
|
||||
@@ -157,8 +195,8 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
protected void updateCharging() {
|
||||
chargeTick++;
|
||||
float progress = getChargeProgress();
|
||||
this.pillarRadius = pillarData.targetRadius * progress;
|
||||
this.pillarHeight = MIN_HEIGHT;
|
||||
setPillarRadiusInternal(pillarData.targetRadius * progress);
|
||||
setPillarHeightInternal(MIN_HEIGHT);
|
||||
syncHitbox();
|
||||
}
|
||||
|
||||
@@ -169,27 +207,29 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
prevRenderPillarRadius = renderPillarRadius;
|
||||
prevRenderPillarHeight = renderPillarHeight;
|
||||
|
||||
if (worldObj.isRemote) {
|
||||
float dwRadius = this.dataWatcher.getWatchableObjectFloat(DW_PILLAR_RADIUS);
|
||||
float dwHeight = this.dataWatcher.getWatchableObjectFloat(DW_PILLAR_HEIGHT);
|
||||
this.pillarRadius = dwRadius;
|
||||
this.pillarHeight = dwHeight;
|
||||
renderPillarRadius += (pillarRadius - renderPillarRadius) * 0.15f;
|
||||
renderPillarHeight += (pillarHeight - renderPillarHeight) * 0.15f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!growing) {
|
||||
spawnDelayTick++;
|
||||
if (spawnDelayTick >= pillarData.spawnDelay) {
|
||||
growing = true;
|
||||
}
|
||||
renderPillarRadius += (pillarRadius - renderPillarRadius) * 0.15f;
|
||||
renderPillarHeight += (pillarHeight - renderPillarHeight) * 0.15f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (worldObj.isRemote) {
|
||||
renderPillarRadius += (pillarRadius - renderPillarRadius) * 0.15f;
|
||||
renderPillarHeight += (pillarHeight - renderPillarHeight) * 0.15f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pillarRadius < pillarData.targetRadius) {
|
||||
pillarRadius = Math.min(pillarData.targetRadius, pillarRadius + pillarData.radiusGrowSpeed);
|
||||
setPillarRadiusInternal(Math.min(pillarData.targetRadius, pillarRadius + pillarData.radiusGrowSpeed));
|
||||
}
|
||||
if (pillarHeight < pillarData.targetHeight) {
|
||||
pillarHeight = Math.min(pillarData.targetHeight, pillarHeight + pillarData.heightGrowSpeed);
|
||||
setPillarHeightInternal(Math.min(pillarData.targetHeight, pillarHeight + pillarData.heightGrowSpeed));
|
||||
}
|
||||
|
||||
syncHitbox();
|
||||
@@ -207,6 +247,7 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
|
||||
private void updateMovement() {
|
||||
if (!homingData.isHoming()) {
|
||||
// Straight-line: motion was set by setInitialMotion() at spawn time
|
||||
this.posX += motionX;
|
||||
this.posZ += motionZ;
|
||||
snapToGround(posX, posY, posZ);
|
||||
@@ -272,10 +313,6 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Narrow-phase XZ check using PillarShape.
|
||||
* AABB broad phase already filtered by Y; this checks the XZ footprint.
|
||||
*/
|
||||
private boolean isInPillar(EntityLivingBase entity) {
|
||||
double dx = entity.posX - posX;
|
||||
double dz = entity.posZ - posZ;
|
||||
@@ -288,16 +325,20 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync entity bounding box to current pillar dimensions.
|
||||
* Called after any dimension change so Minecraft chunk tracking stays accurate.
|
||||
*/
|
||||
private void syncHitbox() {
|
||||
float w = Math.max(0.1f, pillarRadius * 2.0f);
|
||||
float h = Math.max(0.1f, pillarHeight);
|
||||
this.setSize(w, h);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean isInRangeToRenderDist(double distance) {
|
||||
double extent = Math.max(pillarRadius, pillarHeight) * 2.0D;
|
||||
double range = Math.max(128.0D, extent * 4.0D + 64.0D);
|
||||
return distance < range * range;
|
||||
}
|
||||
|
||||
// ==================== ENTITY HELPERS ====================
|
||||
|
||||
public Entity getTargetEntity() {
|
||||
@@ -348,19 +389,28 @@ public class EntityAbilityPillar extends EntityEnergyZone {
|
||||
pillarData.readNBT(nbt);
|
||||
homingData.readNBT(nbt);
|
||||
|
||||
this.pillarRadius = Math.max(0.01f, nbt.getFloat("PillarRadius"));
|
||||
this.pillarHeight = Math.max(MIN_HEIGHT, nbt.getFloat("PillarHeight"));
|
||||
float radius = Math.max(0.01f, nbt.getFloat("PillarRadius"));
|
||||
float height = Math.max(MIN_HEIGHT, nbt.getFloat("PillarHeight"));
|
||||
|
||||
this.pillarRadius = radius;
|
||||
this.pillarHeight = height;
|
||||
|
||||
if (this.dataWatcher != null) {
|
||||
this.dataWatcher.updateObject(DW_PILLAR_RADIUS, radius);
|
||||
this.dataWatcher.updateObject(DW_PILLAR_HEIGHT, height);
|
||||
}
|
||||
|
||||
this.renderPillarRadius = radius;
|
||||
this.prevRenderPillarRadius = radius;
|
||||
this.renderPillarHeight = height;
|
||||
this.prevRenderPillarHeight = height;
|
||||
|
||||
this.spawnDelayTick = nbt.getInteger("SpawnDelayTick");
|
||||
this.growing = nbt.getBoolean("Growing");
|
||||
this.targetEntityId = nbt.hasKey("TargetEntityId") ? nbt.getInteger("TargetEntityId") : -1;
|
||||
this.motionX = nbt.getDouble("MotionX");
|
||||
this.motionZ = nbt.getDouble("MotionZ");
|
||||
|
||||
this.renderPillarRadius = this.pillarRadius;
|
||||
this.prevRenderPillarRadius = this.pillarRadius;
|
||||
this.renderPillarHeight = this.pillarHeight;
|
||||
this.prevRenderPillarHeight = this.pillarHeight;
|
||||
|
||||
syncHitbox();
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,12 @@ import net.minecraft.world.World;
|
||||
import noppes.npcs.NpcDamageSource;
|
||||
import noppes.npcs.entity.EntityNPCInterface;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Abstract base class for all energy zone entities.
|
||||
@@ -50,7 +55,7 @@ public abstract class EntityEnergyZone extends EntityEnergyAbility {
|
||||
|
||||
protected static final float MAX_ZONE_RADIUS = 64.0f;
|
||||
protected static final float MAX_ZONE_HEIGHT = 64.0f;
|
||||
protected static final double GROUND_OFFSET = 0.005;
|
||||
protected static final double GROUND_OFFSET = 0.05;
|
||||
|
||||
// ==================== LIFESPAN ====================
|
||||
|
||||
|
||||
Reference in new issue
Block a user