From 45d05b9157220d1d639bb40df37db08781da8f4f Mon Sep 17 00:00:00 2001 From: Kam Date: Sun, 15 Feb 2026 19:19:57 -0500 Subject: [PATCH] Add trap presets & trigger-reveal visuals Introduce client-side trap presets and a visual "trigger reveal" for traps, plus related entity/renderer changes. - AbilityTrap: add TrapPreset enum and applyTrapPresetDefaults; wire a SubGuiTrapPresetSelector hook to apply presets from the GUI; pass visibility into created trap entities. - EntityAbilityZone: add visible flag, TRIGGER_FLASH_STATUS state, setEntityState usage to notify clients of triggers, handleHealthUpdate to start client-side flash, and adjust construction/serialization accordingly. - RenderAbilityZone: implement trigger-reveal visuals (revealFade, triggerReveal) that briefly show a burst when traps trigger, skip rendering for invisible traps unless revealing, and tweak flash/timing logic. - Minor cleanup: remove/condense comment blocks and refactor noisy helpers across AbilityZone/AbilityHazard, plus language entry update and new SubGuiTrapPresetSelector GUI class. These changes enable preset application for traps and improve client-side feedback when traps trigger. --- .../client/renderer/RenderAbilityZone.java | 114 ++++------ .../data/ability/type/AbilityHazard.java | 19 -- .../data/ability/type/AbilityTrap.java | 113 ++++++++-- .../data/ability/type/AbilityZone.java | 64 ------ .../npcs/entity/EntityAbilityZone.java | 206 +++++------------- .../client/gui/SubGuiTrapPresetSelector.java | 72 ++++++ .../assets/customnpcs/lang/en_US.lang | 6 + 7 files changed, 271 insertions(+), 323 deletions(-) create mode 100644 src/main/java/noppes/npcs/client/gui/SubGuiTrapPresetSelector.java diff --git a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityZone.java b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityZone.java index 9fae0e982..e9775c542 100644 --- a/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityZone.java +++ b/src/main/java/kamkeel/npcs/client/renderer/RenderAbilityZone.java @@ -22,10 +22,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Random; -/** - * Renders zone effects for Trap and Hazard abilities. - * Supports CIRCLE and SQUARE shapes with composable visual layers. - */ @SideOnly(Side.CLIENT) public class RenderAbilityZone extends Render { @@ -33,43 +29,35 @@ public class RenderAbilityZone extends Render { private static final int ACCENT_LINE_COUNT = 12; private static final Random RANDOM = new Random(); - // Track last particle spawn tick per entity to avoid duplicate spawns private final Map lastParticleTick = new HashMap<>(); - - // Persistent lightning state private final Map lightningStates = new HashMap<>(); - // ═══════════════════════════════════════════════════════════════════ - // RENDER CONTEXT - // ═══════════════════════════════════════════════════════════════════ + private static final int TRIGGER_REVEAL_TICKS = 10; private static class Ctx { EntityAbilityZone zone; ZoneShape shape; float radius, height; - float ir, ig, ib; // inner color + float ir, ig, ib; float outerR, outerG, outerB; boolean outerEnabled; float time, pulse, rotation, brightness; boolean flash, newTick; float particleDensity, particleScale, animSpeed, lightningDensity; - // Visual layer fields (always populated) boolean groundFill, rings, border, accents, lightning, particles; float groundAlpha, borderSpeed; int ringCount, accentStyle, particleMotion, particleSize; boolean particleGlow; String particleDir; + boolean trapVisible; + boolean triggerReveal; + float revealFade; } - // ═══════════════════════════════════════════════════════════════════ - // MAIN RENDER - // ═══════════════════════════════════════════════════════════════════ - @Override public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) { EntityAbilityZone zone = (EntityAbilityZone) entity; - // Build context Ctx c = new Ctx(); c.zone = zone; c.shape = zone.getShape(); @@ -92,7 +80,6 @@ public class RenderAbilityZone extends Render { c.animSpeed = zone.getAnimSpeed(); c.lightningDensity = zone.getLightningDensity(); - // Visual layer fields — always populated c.groundFill = zone.isGroundFill(); c.groundAlpha = zone.getGroundAlpha(); c.rings = zone.isRings(); @@ -113,7 +100,27 @@ public class RenderAbilityZone extends Render { c.rotation = c.time * 1.5f * c.animSpeed; c.brightness = 1.0f; + c.trapVisible = true; + c.triggerReveal = false; + c.revealFade = 0.0f; + if (zone.getZoneType() == ZoneType.TRAP) { + c.trapVisible = zone.isVisible(); + + // Trigger reveal: 10-tick full visual burst that fades out + if (zone.getTriggerFlashTick() >= 0) { + float ticksSinceTrigger = zone.ticksExisted - zone.getTriggerFlashTick() + partialTicks; + if (ticksSinceTrigger < TRIGGER_REVEAL_TICKS) { + c.triggerReveal = true; + c.revealFade = Math.max(0.0f, 1.0f - ticksSinceTrigger / TRIGGER_REVEAL_TICKS); + } + } + + // Skip render entirely if invisible and not in reveal + if (!c.trapVisible && !c.triggerReveal) { + return; + } + if (!zone.isArmed()) { c.brightness = 0.4f; } else { @@ -121,16 +128,14 @@ public class RenderAbilityZone extends Render { } } - c.flash = zone.getTriggerFlashTick() >= 0 - && zone.ticksExisted - zone.getTriggerFlashTick() < 4; + // White flash during first 2 ticks of reveal + c.flash = c.triggerReveal && c.revealFade > 0.8f; c.newTick = isNewTick(zone); - // Render setupRenderState(); GL11.glPushMatrix(); GL11.glTranslated(x, y, z); - GL11.glDepthMask(false); renderZone(c); @@ -161,14 +166,21 @@ public class RenderAbilityZone extends Render { return state; } - // ═══════════════════════════════════════════════════════════════════ - // UNIFIED RENDER — Composable visual layers - // ═══════════════════════════════════════════════════════════════════ - private void renderZone(Ctx c) { + // During trigger reveal, override visual layers for dramatic burst + if (c.triggerReveal) { + c.brightness = c.revealFade; + c.groundFill = true; + c.groundAlpha = 0.35f * c.revealFade; + c.rings = true; + c.ringCount = 3; + c.accents = true; + c.accentStyle = 2; // FLICKERING + c.border = true; + } + float rR = ringR(c), rG = ringG(c), rB = ringB(c); - // 1. Ground fill if (c.groundFill) { if (c.flash) { renderShapeFill(c.shape, c.radius, 1, 1, 1, 0.5f); @@ -178,7 +190,6 @@ public class RenderAbilityZone extends Render { } } - // 2. Pulse rings if (c.rings) { int count = Math.max(1, Math.min(5, c.ringCount)); for (int i = 0; i < count; i++) { @@ -188,18 +199,15 @@ public class RenderAbilityZone extends Render { renderRingLine(c.shape, c.radius * frac, rR, rG, rB, ra, yOff); } - // Always include outer edge ring float edgeA = (0.5f + 0.4f * sin(c.pulse + count * Math.PI / 3)) * c.brightness; renderRingLine(c.shape, c.radius, rR, rG, rB, edgeA, 0.02f + count * 0.01f); } - // 3. Rotating border if (c.border) { renderRotatingBorder(c.shape, c.radius, rR, rG, rB, 0.7f * c.brightness, c.rotation * c.borderSpeed, 0.05f); } - // 4. Vertical accents if (c.accents) { float aa = 0.5f * c.brightness * (0.3f + 0.2f * sin(c.pulse * 0.7f)); AccentStyle style = AccentStyle.values()[Math.min(c.accentStyle, AccentStyle.values().length - 1)]; @@ -216,7 +224,6 @@ public class RenderAbilityZone extends Render { } } - // 5. Lightning if (c.lightning) { int innerColor = c.zone.getInnerColor(); int outerColor = c.outerEnabled ? c.zone.getOuterColor() : innerColor; @@ -246,10 +253,9 @@ public class RenderAbilityZone extends Render { } } state.render(); - GL11.glDepthMask(false); // Reset after lightning + GL11.glDepthMask(false); } - // 6. Particles if (c.particles && c.newTick && c.particleDensity > 0) { if (c.particleDir != null && !c.particleDir.isEmpty()) { if (c.particleDir.startsWith("mc:")) { @@ -263,7 +269,6 @@ public class RenderAbilityZone extends Render { } } - /** Spawn vanilla Minecraft particles using mc: prefix (e.g. "mc:flame"). */ private void spawnVanillaParticle(Ctx c) { String particleName = c.particleDir.substring(3); int count = Math.round(3 * c.particleDensity); @@ -276,7 +281,6 @@ public class RenderAbilityZone extends Render { } } - /** Spawn CustomFX particles using a texture directory. */ private void spawnCustomFXParticle(Ctx c) { int count = Math.round(3 * c.particleDensity); for (int i = 0; i < count; i++) { @@ -308,7 +312,6 @@ public class RenderAbilityZone extends Render { } } - /** Spawn default ZoneParticleFX with motion style from ParticleMotion enum. */ private void spawnDefaultParticles(Ctx c) { int count = Math.round(3 * c.particleDensity); ParticleMotion motion = ParticleMotion.values()[Math.min(c.particleMotion, ParticleMotion.values().length - 1)]; @@ -346,7 +349,6 @@ public class RenderAbilityZone extends Render { } } - /** Get motion vector for a CustomFX particle based on ParticleMotion ordinal. */ private double[] getMotionForStyle(int motionOrdinal) { ParticleMotion motion = ParticleMotion.values()[Math.min(motionOrdinal, ParticleMotion.values().length - 1)]; switch (motion) { @@ -371,18 +373,12 @@ public class RenderAbilityZone extends Render { } } - // ═══════════════════════════════════════════════════════════════════ - // STYLE HELPERS - // ═══════════════════════════════════════════════════════════════════ - - /** Get the ring/outer color (falls back to inner if outer not enabled). */ private float ringR(Ctx c) { return c.outerEnabled ? c.outerR : c.ir; } private float ringG(Ctx c) { return c.outerEnabled ? c.outerG : c.ig; } private float ringB(Ctx c) { return c.outerEnabled ? c.outerB : c.ib; } private static float sin(double v) { return (float) Math.sin(v); } - /** Vertical accents with sinusoidal sway per line. */ private void renderSwayingAccents(Ctx c, float r, float g, float b, float a, float height) { if (height <= 0.1f) return; float rotRad = (float) Math.toRadians(c.rotation * 0.2f); @@ -436,7 +432,6 @@ public class RenderAbilityZone extends Render { } } - /** Vertical accents with per-line random alpha flicker. */ private void renderFlickeringAccents(Ctx c, float r, float g, float b, float baseAlpha, float height) { if (height <= 0.1f) return; float rotRad = (float) Math.toRadians(c.rotation * 0.4f); @@ -492,7 +487,6 @@ public class RenderAbilityZone extends Render { } } - /** Pick a random position within the zone's area. Returns {x, y, z} offsets from zone center. */ private double[] randomPosInZone(Ctx c) { if (c.shape == ZoneShape.SQUARE) { double px = (RANDOM.nextDouble() * 2 - 1) * c.radius; @@ -507,7 +501,6 @@ public class RenderAbilityZone extends Render { } } - /** Pick a random position on the edge of the zone. Returns {x, z}. */ private double[] randomEdgePos(Ctx c) { if (c.shape == ZoneShape.SQUARE) { int side = RANDOM.nextInt(4); @@ -525,7 +518,6 @@ public class RenderAbilityZone extends Render { } } - /** Spawn a zone particle at world position. */ private void spawnParticle(EntityAbilityZone zone, double offsetX, double offsetY, double offsetZ, double mx, double my, double mz, float r, float g, float b, float alpha, @@ -537,10 +529,6 @@ public class RenderAbilityZone extends Render { Minecraft.getMinecraft().effectRenderer.addEffect(particle); } - // ═══════════════════════════════════════════════════════════════════ - // GL STATE - // ═══════════════════════════════════════════════════════════════════ - private void setupRenderState() { GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); GL11.glDisable(GL11.GL_TEXTURE_2D); @@ -561,10 +549,6 @@ public class RenderAbilityZone extends Render { GL11.glPopAttrib(); } - // ═══════════════════════════════════════════════════════════════════ - // SHAPE FILL (Ground plane) - // ═══════════════════════════════════════════════════════════════════ - private void renderShapeFill(ZoneShape shape, float radius, float r, float g, float b, float a) { GL11.glEnable(GL11.GL_POLYGON_OFFSET_FILL); GL11.glPolygonOffset(-1.0f, -1.0f); @@ -602,10 +586,6 @@ public class RenderAbilityZone extends Render { tess.draw(); } - // ═══════════════════════════════════════════════════════════════════ - // RING LINES (Concentric pulse rings) - // ═══════════════════════════════════════════════════════════════════ - private void renderRingLine(ZoneShape shape, float radius, float r, float g, float b, float a, float yOffset) { switch (shape) { @@ -640,18 +620,13 @@ public class RenderAbilityZone extends Render { tess.draw(); } - // ═══════════════════════════════════════════════════════════════════ - // ROTATING BORDER - // ═══════════════════════════════════════════════════════════════════ - private void renderRotatingBorder(ZoneShape shape, float radius, float r, float g, float b, float a, float rotationDeg, float yOffset) { int dashCount = 16; if (shape == ZoneShape.SQUARE) { - // Dashes along 4 rectangle edges with offset - float perimeter = radius * 8; // total perimeter = 4 * (2*radius) + float perimeter = radius * 8; float dashLen = perimeter / dashCount; float offset = (rotationDeg % 360.0f) / 360.0f * perimeter; @@ -691,7 +666,6 @@ public class RenderAbilityZone extends Render { } } - /** Convert a distance along the square perimeter to an x,z position. */ private double[] perimeterPos(float dist, float radius) { float sideLen = radius * 2; if (dist < sideLen) { @@ -713,10 +687,6 @@ public class RenderAbilityZone extends Render { return new double[]{-radius, radius - dist}; } - // ═══════════════════════════════════════════════════════════════════ - // VERTICAL ACCENT LINES - // ═══════════════════════════════════════════════════════════════════ - private void renderVerticalAccents(ZoneShape shape, float radius, float r, float g, float b, float a, float height, float rotationDeg) { @@ -725,7 +695,6 @@ public class RenderAbilityZone extends Render { Tessellator tess = Tessellator.instance; if (shape == ZoneShape.SQUARE) { - // Lines at corners + distributed along edges int perSide = Math.max(1, ACCENT_LINE_COUNT / 4); for (int side = 0; side < 4; side++) { for (int j = 0; j < perSide; j++) { @@ -745,7 +714,6 @@ public class RenderAbilityZone extends Render { tess.draw(); } } - // Corner accent lines float[][] corners = {{-radius, -radius}, {radius, -radius}, {radius, radius}, {-radius, radius}}; for (float[] corner : corners) { tess.startDrawing(GL11.GL_LINES); diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHazard.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHazard.java index 26409d5fe..7ca81c95a 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHazard.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityHazard.java @@ -19,14 +19,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -/** - * Hazard ability: Creates persistent ground effect zones around the caster. - * Spawns one or more EntityAbilityZone entities in random positions - * within spawnRadius. Damage and zone logic handled by the entity. - */ public class AbilityHazard extends AbilityZone implements IAbilityHazard { - // Hazard-specific fields private float radius = 2.0f; private float damagePerSecond = 1.0f; private int damageInterval = 20; @@ -51,7 +45,6 @@ public class AbilityHazard extends AbilityZone implements IAbilityHazard { public void onExecute(EntityLivingBase caster, EntityLivingBase target) { activeEntities.clear(); - // Use pre-calculated positions from telegraph phase, or generate new ones List positions; if (!preCalculatedPositions.isEmpty() && preCalculatedPositions.size() == zoneCount) { positions = new ArrayList<>(preCalculatedPositions); @@ -93,10 +86,6 @@ public class AbilityHazard extends AbilityZone implements IAbilityHazard { } } - // ═══════════════════════════════════════════════════════════════════ - // NBT - // ═══════════════════════════════════════════════════════════════════ - @Override public void writeTypeNBT(NBTTagCompound nbt) { writeZoneNBT(nbt); @@ -117,10 +106,6 @@ public class AbilityHazard extends AbilityZone implements IAbilityHazard { this.affectsCaster = nbt.getBoolean("affectsCaster"); } - // ═══════════════════════════════════════════════════════════════════ - // HAZARD-SPECIFIC GETTERS & SETTERS - // ═══════════════════════════════════════════════════════════════════ - public float getRadius() { return radius; } public void setRadius(float radius) { this.radius = radius; } @@ -136,10 +121,6 @@ public class AbilityHazard extends AbilityZone implements IAbilityHazard { public boolean isAffectsCaster() { return affectsCaster; } public void setAffectsCaster(boolean affectsCaster) { this.affectsCaster = affectsCaster; } - // ═══════════════════════════════════════════════════════════════════ - // GUI FIELD DEFINITIONS - // ═══════════════════════════════════════════════════════════════════ - @SideOnly(Side.CLIENT) @Override public void getAbilityDefinitions(List defs) { diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTrap.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTrap.java index 7766c3d45..62471c051 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTrap.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityTrap.java @@ -1,16 +1,16 @@ package kamkeel.npcs.controllers.data.ability.type; +import kamkeel.npcs.controllers.data.ability.AbilityEffect; import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData; +import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs; import kamkeel.npcs.entity.EntityAbilityZone; import kamkeel.npcs.entity.EntityAbilityZone.ZoneShape; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; - - import noppes.npcs.api.ability.type.IAbilityTrap; - +import noppes.npcs.client.gui.SubGuiTrapPresetSelector; import noppes.npcs.client.gui.builder.FieldDef; -import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs; +import noppes.npcs.constants.EnumPotionType; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @@ -19,14 +19,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -/** - * Trap ability: Places proximity-triggered traps around the caster. - * Spawns one or more EntityAbilityZone entities in random positions - * within spawnRadius of the caster. Damage and trigger logic handled by the entity. - */ public class AbilityTrap extends AbilityZone implements IAbilityTrap { - // Trap-specific fields private float triggerRadius = 2.0f; private int armTime = 20; private int maxTriggers = 1; @@ -54,7 +48,6 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap { public void onExecute(EntityLivingBase caster, EntityLivingBase target) { activeEntities.clear(); - // Use pre-calculated positions from telegraph phase, or generate new ones List positions; if (!preCalculatedPositions.isEmpty() && preCalculatedPositions.size() == zoneCount) { positions = new ArrayList<>(preCalculatedPositions); @@ -81,6 +74,7 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap { colorData.innerColor, colorData.outerColor, colorData.outerColorEnabled, zoneHeight, particleDensity, particleScale, animSpeed, lightningDensity, + visible, getEffects()); applyVisualToEntity(entity); @@ -94,9 +88,81 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap { } } - // ═══════════════════════════════════════════════════════════════════ - // NBT - // ═══════════════════════════════════════════════════════════════════ + @SideOnly(Side.CLIENT) + public enum TrapPreset { + HIDDEN, VENOM, EXPLOSIVE, CURSED, SHOCK, SNARE; + + @Override + public String toString() { + return "ability.preset." + name().toLowerCase(); + } + } + + private void applyTrapPresetDefaults(String preset) { + effects.clear(); + particleDir = ""; + + groundFill = true; + rings = false; + ringCount = 1; + border = false; + borderSpeed = 1.0f; + accents = false; + accentStyle = 0; + lightning = false; + particleGlow = false; + + switch (preset) { + case "VENOM": + groundAlpha = 0.04f; + particles = true; particleDensity = 0.3f; particleScale = 0.5f; + particleMotion = 1; particleDir = "mc:mobSpell"; + colorData.innerColor = 0x44DD44; colorData.outerColor = 0x116611; + windUpColor = 0x6044DD44; activeColor = 0xC044FF44; + effects.add(new AbilityEffect(EnumPotionType.Poison, 100, 0)); + break; + case "EXPLOSIVE": + groundAlpha = 0.04f; + particles = true; particleDensity = 0.3f; particleScale = 0.6f; + particleMotion = 0; particleDir = "mc:smoke"; + colorData.innerColor = 0xFF6611; colorData.outerColor = 0xCC2200; + windUpColor = 0x60FF6611; activeColor = 0xC0FF4400; + effects.add(new AbilityEffect(EnumPotionType.Fire, 60, 0)); + break; + case "CURSED": + groundAlpha = 0.04f; + particles = true; particleDensity = 0.3f; particleScale = 0.5f; + particleMotion = 1; particleDir = "mc:portal"; + colorData.innerColor = 0xAA44FF; colorData.outerColor = 0x6622BB; + windUpColor = 0x60AA44FF; activeColor = 0xC0CC66FF; + effects.add(new AbilityEffect(EnumPotionType.Weakness, 100, 0)); + break; + case "SHOCK": + groundAlpha = 0.04f; + particles = true; particleDensity = 0.3f; particleScale = 0.4f; + particleMotion = 2; particleDir = "mc:enchantmenttable"; + colorData.innerColor = 0x4488FF; colorData.outerColor = 0x2244BB; + windUpColor = 0x604488FF; activeColor = 0xC066AAFF; + effects.add(new AbilityEffect(EnumPotionType.MiningFatigue, 80, 1)); + break; + case "SNARE": + groundAlpha = 0.04f; + particles = true; particleDensity = 0.3f; particleScale = 0.5f; + particleMotion = 1; particleDir = "mc:snowshovel"; + colorData.innerColor = 0x88CCFF; colorData.outerColor = 0x4488CC; + windUpColor = 0x6088CCFF; activeColor = 0xC0AADDFF; + effects.add(new AbilityEffect(EnumPotionType.Slowness, 100, 1)); + break; + case "HIDDEN": + default: + groundAlpha = 0.03f; + particles = false; particleDensity = 0.0f; particleScale = 1.0f; + particleMotion = 0; + colorData.innerColor = 0x888888; colorData.outerColor = 0x444444; + windUpColor = 0x60888888; activeColor = 0xC0AAAAAA; + break; + } + } @Override public void writeTypeNBT(NBTTagCompound nbt) { @@ -124,10 +190,6 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap { this.visible = nbt.getBoolean("visible"); } - // ═══════════════════════════════════════════════════════════════════ - // TRAP-SPECIFIC GETTERS & SETTERS - // ═══════════════════════════════════════════════════════════════════ - public float getTriggerRadius() { return triggerRadius; } public void setTriggerRadius(float triggerRadius) { this.triggerRadius = triggerRadius; } public int getArmTime() { return armTime; } @@ -145,9 +207,18 @@ public class AbilityTrap extends AbilityZone implements IAbilityTrap { public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } - // ═══════════════════════════════════════════════════════════════════ - // GUI FIELD DEFINITIONS - // ═══════════════════════════════════════════════════════════════════ + @SideOnly(Side.CLIENT) + @Override + protected void addPresetFieldDef(List defs) { + defs.add(FieldDef.subGuiField("gui.applyPreset", + SubGuiTrapPresetSelector::new, + gui -> { + SubGuiTrapPresetSelector selector = (SubGuiTrapPresetSelector) gui; + if (selector.selectedPreset != null) { + applyTrapPresetDefaults(selector.selectedPreset); + } + })); + } @SideOnly(Side.CLIENT) @Override diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityZone.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityZone.java index 438fd11d4..0d4b0ce8f 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityZone.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityZone.java @@ -31,27 +31,20 @@ import java.util.Iterator; import java.util.List; import java.util.Random; -/** - * Abstract base class for zone-based abilities (Trap, Hazard). - * Extracts shared fields: duration, shape, spawn, visual, lifecycle, and FieldDefs. - */ public abstract class AbilityZone extends Ability { - // Zone properties protected int durationTicks; protected ZoneShape zoneShape = ZoneShape.CIRCLE; protected float spawnRadius = 10.0f; protected int zoneCount = 3; protected float zoneHeight = 2.0f; - // Visual parameters protected float particleDensity = 1.0f; protected float particleScale = 1.0f; protected float animSpeed = 1.0f; protected float lightningDensity = 1.0f; protected EnergyDisplayData colorData; - // Visual layer fields (always active) protected boolean groundFill = true; protected float groundAlpha = 0.25f; protected boolean rings = true; @@ -67,7 +60,6 @@ public abstract class AbilityZone extends Ability { protected int particleSize = 32; protected boolean particleGlow = true; - // Runtime state protected static final Random RANDOM = new Random(); protected transient List activeEntities = new ArrayList<>(); protected transient List preCalculatedPositions = new ArrayList<>(); @@ -82,10 +74,6 @@ public abstract class AbilityZone extends Ability { this.allowedBy = UserType.NPC_ONLY; } - // ═════════════════════════════════════════════════════════════════ - // SHARED OVERRIDES - // ═════════════════════════════════════════════════════════════════ - @Override public boolean allowBurst() { return false; @@ -101,17 +89,12 @@ public abstract class AbilityZone extends Ability { return new TargetingMode[]{TargetingMode.AGGRO_TARGET}; } - /** - * Returns the actual zone effect radius for telegraph sizing. - * AbilityHazard returns its damage radius, AbilityTrap returns triggerRadius. - */ public abstract float getZoneRadius(); @Override public List createTelegraphs(EntityLivingBase caster, EntityLivingBase target) { if (!showTelegraph || windUpTicks <= 0) return new ArrayList<>(); - // Pre-calculate zone positions preCalculatedPositions.clear(); List placedPositions = new ArrayList<>(); float minSeparation = getZoneRadius() * 2.0f; @@ -122,7 +105,6 @@ public abstract class AbilityZone extends Ability { preCalculatedPositions.add(pos); } - // Create a telegraph for each zone position List telegraphs = new ArrayList<>(); float zoneRadius = getZoneRadius(); @@ -152,7 +134,6 @@ public abstract class AbilityZone extends Ability { @Override public void onActiveTick(EntityLivingBase caster, EntityLivingBase target, int tick) { - // Remove dead entities Iterator it = activeEntities.iterator(); while (it.hasNext()) { EntityAbilityZone entity = it.next(); @@ -192,13 +173,6 @@ public abstract class AbilityZone extends Ability { return durationTicks + 10; } - // ═════════════════════════════════════════════════════════════════ - // SHARED SPAWN HELPER - // ═════════════════════════════════════════════════════════════════ - - /** - * Find a non-overlapping spawn position within spawnRadius of the caster. - */ protected double[] findSpawnPosition(EntityLivingBase caster, List placedPositions, float minSeparation) { double spawnX = caster.posX; double spawnZ = caster.posZ; @@ -227,9 +201,6 @@ public abstract class AbilityZone extends Ability { return new double[]{spawnX, spawnZ}; } - /** - * Apply visual settings to a zone entity. - */ protected void applyVisualToEntity(EntityAbilityZone entity) { entity.applyVisual(groundFill, groundAlpha, rings, ringCount, border, borderSpeed, @@ -238,13 +209,6 @@ public abstract class AbilityZone extends Ability { particleSize, particleGlow); } - // ═════════════════════════════════════════════════════════════════ - // PRESET SUPPORT - // ═════════════════════════════════════════════════════════════════ - - /** - * Apply preset defaults by style name. - */ private void applyPresetDefaults(String styleName) { effects.clear(); particleDir = ""; @@ -307,10 +271,6 @@ public abstract class AbilityZone extends Ability { } } - /** - * Client-only preset enum for the GUI dropdown. - * Not persisted — presets simply set field values. - */ @SideOnly(Side.CLIENT) public enum ZonePreset { DEFAULT, TOXIC, INFERNO, ARCANE, ELECTRIC, FROST; @@ -321,10 +281,6 @@ public abstract class AbilityZone extends Ability { } } - // ═════════════════════════════════════════════════════════════════ - // SHARED NBT - // ═════════════════════════════════════════════════════════════════ - protected void writeZoneNBT(NBTTagCompound nbt) { nbt.setInteger("durationTicks", durationTicks); nbt.setString("zoneShape", zoneShape.name()); @@ -337,7 +293,6 @@ public abstract class AbilityZone extends Ability { nbt.setFloat("lightningDensity", lightningDensity); colorData.writeNBT(nbt); - // Visual layer fields — always written as top-level keys nbt.setBoolean("groundFill", groundFill); nbt.setFloat("groundAlpha", groundAlpha); nbt.setBoolean("rings", rings); @@ -367,7 +322,6 @@ public abstract class AbilityZone extends Ability { this.lightningDensity = nbt.getFloat("lightningDensity"); colorData.readNBT(nbt); - // Visual layer fields this.groundFill = nbt.getBoolean("groundFill"); this.groundAlpha = nbt.getFloat("groundAlpha"); this.rings = nbt.getBoolean("rings"); @@ -384,10 +338,6 @@ public abstract class AbilityZone extends Ability { this.particleGlow = nbt.getBoolean("particleGlow"); } - // ═════════════════════════════════════════════════════════════════ - // SHARED GETTERS & SETTERS - // ═════════════════════════════════════════════════════════════════ - public int getDurationTicks() { return durationTicks; } public void setDurationTicks(int durationTicks) { this.durationTicks = Math.max(1, durationTicks); } @@ -416,7 +366,6 @@ public abstract class AbilityZone extends Ability { public float getLightningDensity() { return lightningDensity; } public void setLightningDensity(float v) { this.lightningDensity = Math.max(0, Math.min(3, v)); } - // Color getters/setters public int getInnerColor() { return colorData.innerColor; } public void setInnerColor(int color) { colorData.innerColor = color; } public int getOuterColor() { return colorData.outerColor; } @@ -424,7 +373,6 @@ public abstract class AbilityZone extends Ability { public boolean isOuterColorEnabled() { return colorData.outerColorEnabled; } public void setOuterColorEnabled(boolean enabled) { colorData.outerColorEnabled = enabled; } - // Visual layer getters/setters public boolean isGroundFill() { return groundFill; } public void setGroundFill(boolean v) { this.groundFill = v; } public float getGroundAlpha() { return groundAlpha; } @@ -458,10 +406,6 @@ public abstract class AbilityZone extends Ability { public boolean isParticleGlow() { return particleGlow; } public void setParticleGlow(boolean v) { this.particleGlow = v; } - // ═════════════════════════════════════════════════════════════════ - // SHARED FIELD DEFINITIONS (client-only) - // ═════════════════════════════════════════════════════════════════ - @SideOnly(Side.CLIENT) protected void addPresetFieldDef(List defs) { defs.add(FieldDef.subGuiField("gui.applyPreset", @@ -477,42 +421,36 @@ public abstract class AbilityZone extends Ability { @SideOnly(Side.CLIENT) protected void addVisualFieldDefs(List defs) { defs.addAll(Arrays.asList( - // Ground Fill FieldDef.section("ability.section.ground").tab("ability.tab.visual"), FieldDef.row( FieldDef.boolField("gui.enabled", this::isGroundFill, this::setGroundFill), FieldDef.floatField("gui.alpha", this::getGroundAlpha, this::setGroundAlpha) ).tab("ability.tab.visual"), - // Rings FieldDef.section("ability.section.rings").tab("ability.tab.visual"), FieldDef.row( FieldDef.boolField("gui.enabled", this::isRings, this::setRings), FieldDef.intField("gui.count", this::getRingCount, this::setRingCount).range(1, 5) ).tab("ability.tab.visual"), - // Border FieldDef.section("ability.section.border").tab("ability.tab.visual"), FieldDef.row( FieldDef.boolField("gui.enabled", this::isBorder, this::setBorder), FieldDef.floatField("gui.speed", this::getBorderSpeed, this::setBorderSpeed) ).tab("ability.tab.visual"), - // Accents FieldDef.section("ability.section.accents").tab("ability.tab.visual"), FieldDef.row( FieldDef.boolField("gui.enabled", this::isAccents, this::setAccents), FieldDef.enumField("gui.style", AccentStyle.class, this::getAccentStyleEnum, this::setAccentStyleEnum) ).tab("ability.tab.visual"), - // Lightning FieldDef.section("ability.section.lightning").tab("ability.tab.visual"), FieldDef.row( FieldDef.boolField("gui.enabled", this::isLightning, this::setLightning), FieldDef.floatField("gui.density", this::getLightningDensity, this::setLightningDensity) ).tab("ability.tab.visual"), - // Particles FieldDef.section("ability.section.particles").tab("ability.tab.visual"), FieldDef.boolField("gui.enabled", this::isParticles, this::setParticles) .tab("ability.tab.visual"), @@ -527,12 +465,10 @@ public abstract class AbilityZone extends Ability { FieldDef.stringField("gui.texture", this::getParticleDir, this::setParticleDir) .tab("ability.tab.visual").visibleWhen(this::isParticles), - // Animation FieldDef.section("ability.section.animation").tab("ability.tab.visual"), FieldDef.floatField("gui.speed", this::getAnimSpeed, this::setAnimSpeed) .tab("ability.tab.visual"), - // Colors FieldDef.section("ability.section.colors").tab("ability.tab.visual"), FieldDef.colorSubGui("ability.innerColor", this::getInnerColor, this::setInnerColor) .tab("ability.tab.visual"), diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java index 2f2bc00e9..34156e362 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java @@ -26,7 +26,7 @@ import java.util.UUID; /** * Shared zone entity for Trap and Hazard abilities. - * Handles rendering, positioning, and damage logic. + * ZoneType distinguishes behavior: TRAP (proximity-triggered burst) vs HAZARD (persistent AoE). */ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnData { @@ -76,11 +76,11 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD } } - /** Tiny Y offset above ground to prevent z-clipping */ private static final double GROUND_OFFSET = 0.005; + private static final byte TRIGGER_FLASH_STATUS = 60; // ═══════════════════════════════════════════════════════════════════ - // COMMON PROPERTIES (synced via ByteBuf) + // COMMON PROPERTIES // ═══════════════════════════════════════════════════════════════════ private ZoneType zoneType = ZoneType.TRAP; @@ -99,13 +99,12 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD private float zoneHeight = 2.0f; - // Visual parameters (synced for renderer) private float particleDensity = 1.0f; private float particleScale = 1.0f; private float animSpeed = 1.0f; private float lightningDensity = 1.0f; - // Visual layer parameters (always active) + // Visual layer fields private boolean groundFill = true; private float groundAlpha = 0.25f; private boolean rings = true; @@ -113,18 +112,15 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD private boolean border = true; private float borderSpeed = 1.0f; private boolean accents = true; - private int accentStyle = 0; // AccentStyle ordinal + private int accentStyle = 0; private boolean lightning = false; private boolean particles = true; - private int particleMotion = 0; // ParticleMotion ordinal + private int particleMotion = 0; private String particleDir = ""; private int particleSize = 32; private boolean particleGlow = true; - // Lifetime private long deathWorldTime = -1; - - // Effects private List effects = new ArrayList<>(); // ═══════════════════════════════════════════════════════════════════ @@ -138,6 +134,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD private float damage = 6.0f; private float damageRadius = 0.0f; private float knockback = 0.5f; + private boolean visible = true; // ═══════════════════════════════════════════════════════════════════ // HAZARD-SPECIFIC PROPERTIES @@ -149,21 +146,18 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD private boolean affectsCaster = false; // ═══════════════════════════════════════════════════════════════════ - // RUNTIME STATE (transient) + // RUNTIME STATE // ═══════════════════════════════════════════════════════════════════ - // Trap runtime private transient boolean armed = false; private transient int triggerCount = 0; private transient int ticksSinceLastTrigger = 0; private transient Set triggeredEntities = new HashSet<>(); - private transient int triggerFlashTick = -1; // For visual flash on trigger + private transient int triggerFlashTick = -1; - // Hazard runtime private transient int ticksSinceDamage = 0; private transient Set damagedThisTick = new HashSet<>(); - // Preview mode private boolean previewMode = false; private EntityLivingBase previewOwner = null; @@ -179,11 +173,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD this.ignoreFrustumCheck = true; } - /** - * Full constructor for creating a zone entity. - * Use the static factory methods for cleaner creation. - * Position is snapped to ground level with a tiny offset to avoid z-clipping. - */ private EntityAbilityZone(World world, ZoneType type, EntityLivingBase owner, double x, double y, double z) { this(world); @@ -194,9 +183,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD this.setPosition(x, groundY, z); } - /** - * Create a Trap zone entity. - */ public static EntityAbilityZone createTrap(World world, EntityLivingBase owner, double x, double y, double z, ZoneShape shape, @@ -207,6 +193,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD float zoneHeight, float particleDensity, float particleScale, float animSpeed, float lightningDensity, + boolean visible, List effects) { EntityAbilityZone zone = new EntityAbilityZone(world, ZoneType.TRAP, owner, x, y, z); zone.shape = shape; @@ -228,19 +215,16 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD zone.particleScale = particleScale; zone.animSpeed = animSpeed; zone.lightningDensity = lightningDensity; + zone.visible = visible; if (effects != null) { for (AbilityEffect e : effects) { zone.effects.add(e.copy()); } } - // Ready to trigger immediately after arm time zone.ticksSinceLastTrigger = triggerCooldown; return zone; } - /** - * Create a Hazard zone entity. - */ public static EntityAbilityZone createHazard(World world, EntityLivingBase owner, double x, double y, double z, ZoneShape shape, @@ -275,7 +259,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD zone.effects.add(e.copy()); } } - // Ready to deal damage immediately zone.ticksSinceDamage = damageInterval; return zone; } @@ -285,8 +268,15 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD // ═══════════════════════════════════════════════════════════════════ @Override - protected void entityInit() { - // No DataWatcher needed + protected void entityInit() {} + + @Override + public void handleHealthUpdate(byte id) { + if (id == TRIGGER_FLASH_STATUS) { + this.triggerFlashTick = this.ticksExisted; + } else { + super.handleHealthUpdate(id); + } } @Override @@ -297,18 +287,15 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD super.onUpdate(); - // Set death time on first tick if (deathWorldTime < 0 && worldObj != null) { deathWorldTime = worldObj.getTotalWorldTime() + maxTicks; } - // Check lifetime if (deathWorldTime > 0 && worldObj.getTotalWorldTime() >= deathWorldTime) { this.setDead(); return; } - // Server handles damage if (!worldObj.isRemote && !previewMode) { switch (zoneType) { case TRAP: @@ -322,11 +309,10 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD } // ═══════════════════════════════════════════════════════════════════ - // TRAP LOGIC (server-only) + // TRAP LOGIC // ═══════════════════════════════════════════════════════════════════ private void tickTrap() { - // Arm time countdown if (!armed) { if (ticksExisted >= armTime) { armed = true; @@ -336,18 +322,15 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD ticksSinceLastTrigger++; - // All triggers used if (maxTriggers > 0 && triggerCount >= maxTriggers) { this.setDead(); return; } - // Cooldown between triggers if (ticksSinceLastTrigger < triggerCooldown) { return; } - // Proximity detection AxisAlignedBB box = AxisAlignedBB.getBoundingBox( posX - triggerRadius, posY - 0.5, posZ - triggerRadius, posX + triggerRadius, posY + zoneHeight, posZ + triggerRadius @@ -419,7 +402,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD anyDamaged = true; } - // Apply knockback only if damage landed if (hit && knockback > 0) { double dx = entity.posX - posX; double dz = entity.posZ - posZ; @@ -430,23 +412,22 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD } } - // Apply effects only if damage landed if (hit) { applyEffects(entity); } } - // Only count the trigger if at least one entity was actually damaged if (anyDamaged) { triggerCount++; ticksSinceLastTrigger = 0; triggeredEntities.add(triggerer.getUniqueID()); triggerFlashTick = ticksExisted; + worldObj.setEntityState(this, TRIGGER_FLASH_STATUS); } } // ═══════════════════════════════════════════════════════════════════ - // HAZARD LOGIC (server-only) + // HAZARD LOGIC // ═══════════════════════════════════════════════════════════════════ private void tickHazard() { @@ -545,12 +526,12 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD @Override @SideOnly(Side.CLIENT) public boolean isInRangeToRenderDist(double distance) { - return distance < 16384.0D; // 128 blocks + return distance < 16384.0D; } @Override public boolean shouldRenderInPass(int pass) { - return pass == 1; // Translucent pass + return pass == 1; } @Override @@ -576,6 +557,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD public boolean isOuterColorEnabled() { return outerColorEnabled; } public float getZoneHeight() { return zoneHeight; } public boolean isArmed() { return armed || ticksExisted >= armTime; } + public boolean isVisible() { return visible; } public int getTriggerFlashTick() { return triggerFlashTick; } public int getDurationTicks() { return durationTicks; } public int getMaxTicks() { return maxTicks; } @@ -584,7 +566,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD public float getAnimSpeed() { return animSpeed; } public float getLightningDensity() { return lightningDensity; } - // Visual layer getters public boolean isGroundFill() { return groundFill; } public float getGroundAlpha() { return groundAlpha; } public boolean isRings() { return rings; } @@ -600,9 +581,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD public int getParticleSize() { return particleSize; } public boolean isParticleGlow() { return particleGlow; } - /** - * Apply visual settings from ability data. - */ public void applyVisual(boolean groundFill, float groundAlpha, boolean rings, int ringCount, boolean border, float borderSpeed, @@ -626,44 +604,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD this.particleGlow = particleGlow; } - /** - * Apply preset defaults by old style name (for backward compatibility). - */ - private void applyPresetDefaults(String styleName) { - switch (styleName) { - case "TOXIC": - groundFill = true; groundAlpha = 0.30f; rings = true; ringCount = 3; - border = false; borderSpeed = 1.0f; accents = true; accentStyle = 1; - lightning = false; particles = true; particleMotion = 0; particleGlow = true; - break; - case "INFERNO": - groundFill = true; groundAlpha = 0.35f; rings = true; ringCount = 3; - border = true; borderSpeed = 2.0f; accents = true; accentStyle = 2; - lightning = false; particles = true; particleMotion = 0; particleGlow = true; - break; - case "ARCANE": - groundFill = true; groundAlpha = 0.20f; rings = true; ringCount = 1; - border = true; borderSpeed = 0.8f; accents = true; accentStyle = 0; - lightning = false; particles = true; particleMotion = 1; particleGlow = true; - break; - case "ELECTRIC": - groundFill = true; groundAlpha = 0.15f; rings = false; ringCount = 1; - border = false; borderSpeed = 1.0f; accents = false; accentStyle = 0; - lightning = true; particles = true; particleMotion = 2; particleGlow = true; - break; - case "FROST": - groundFill = true; groundAlpha = 0.25f; rings = true; ringCount = 3; - border = true; borderSpeed = 0.3f; accents = true; accentStyle = 0; - lightning = false; particles = true; particleMotion = 1; particleGlow = true; - break; - default: // DEFAULT - groundFill = true; groundAlpha = 0.25f; rings = true; ringCount = 3; - border = true; borderSpeed = 1.0f; accents = true; accentStyle = 0; - lightning = false; particles = false; particleMotion = 0; particleGlow = true; - break; - } - } - // ═══════════════════════════════════════════════════════════════════ // NBT SERIALIZATION // ═══════════════════════════════════════════════════════════════════ @@ -671,16 +611,9 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD @Override protected void readEntityFromNBT(NBTTagCompound nbt) { this.zoneType = ZoneType.values()[nbt.getInteger("ZoneType")]; - - // Shape: read new string key first, fall back to old ordinal (always CIRCLE for old RING/CONE) - if (nbt.hasKey("ShapeName")) { - try { - this.shape = ZoneShape.valueOf(nbt.getString("ShapeName")); - } catch (Exception e) { - this.shape = ZoneShape.CIRCLE; - } - } else { - // Legacy: old ordinal data — RING(1) and CONE(2) map to CIRCLE + try { + this.shape = ZoneShape.valueOf(nbt.getString("ShapeName")); + } catch (Exception e) { this.shape = ZoneShape.CIRCLE; } @@ -691,51 +624,28 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD this.innerColor = nbt.getInteger("InnerColor"); this.outerColor = nbt.getInteger("OuterColor"); this.outerColorEnabled = nbt.getBoolean("OuterColorEnabled"); - this.zoneHeight = nbt.hasKey("ZoneHeight") ? nbt.getFloat("ZoneHeight") : 2.0f; - this.particleDensity = nbt.hasKey("ParticleDensity") ? nbt.getFloat("ParticleDensity") : 1.0f; - this.particleScale = nbt.hasKey("ParticleScale") ? nbt.getFloat("ParticleScale") : 1.0f; - this.animSpeed = nbt.hasKey("AnimSpeed") ? nbt.getFloat("AnimSpeed") : 1.0f; - this.lightningDensity = nbt.hasKey("LightningDensity") ? nbt.getFloat("LightningDensity") : 1.0f; - this.deathWorldTime = nbt.hasKey("DeathWorldTime") ? nbt.getLong("DeathWorldTime") : -1; + this.zoneHeight = nbt.getFloat("ZoneHeight"); + this.particleDensity = nbt.getFloat("ParticleDensity"); + this.particleScale = nbt.getFloat("ParticleScale"); + this.animSpeed = nbt.getFloat("AnimSpeed"); + this.lightningDensity = nbt.getFloat("LightningDensity"); + this.deathWorldTime = nbt.getLong("DeathWorldTime"); - // Visual fields — backward compat with old ZoneStyle format - if (nbt.hasKey("ZoneStyle")) { - // OLD FORMAT: apply preset defaults, then overlay CustomVisual - applyPresetDefaults(nbt.getString("ZoneStyle")); - if (nbt.hasKey("CustomVisual")) { - NBTTagCompound cv = nbt.getCompoundTag("CustomVisual"); - this.groundFill = !cv.hasKey("GroundFill") || cv.getBoolean("GroundFill"); - this.groundAlpha = cv.hasKey("GroundAlpha") ? cv.getFloat("GroundAlpha") : 0.25f; - this.rings = !cv.hasKey("Rings") || cv.getBoolean("Rings"); - this.ringCount = cv.hasKey("RingCount") ? cv.getInteger("RingCount") : 3; - this.border = !cv.hasKey("Border") || cv.getBoolean("Border"); - this.borderSpeed = cv.hasKey("BorderSpeed") ? cv.getFloat("BorderSpeed") : 1.0f; - this.accents = !cv.hasKey("Accents") || cv.getBoolean("Accents"); - this.accentStyle = cv.hasKey("AccentStyle") ? cv.getInteger("AccentStyle") : 0; - this.lightning = cv.hasKey("Lightning") && cv.getBoolean("Lightning"); - this.particles = !cv.hasKey("Particles") || cv.getBoolean("Particles"); - this.particleMotion = cv.hasKey("ParticleMotion") ? cv.getInteger("ParticleMotion") : 0; - this.particleDir = cv.hasKey("ParticleDir") ? cv.getString("ParticleDir") : ""; - this.particleSize = cv.hasKey("ParticleSize") ? cv.getInteger("ParticleSize") : 32; - this.particleGlow = !cv.hasKey("ParticleGlow") || cv.getBoolean("ParticleGlow"); - } - } else { - // NEW FORMAT: read individual top-level keys - this.groundFill = !nbt.hasKey("GroundFill") || nbt.getBoolean("GroundFill"); - this.groundAlpha = nbt.hasKey("GroundAlpha") ? nbt.getFloat("GroundAlpha") : 0.25f; - this.rings = !nbt.hasKey("Rings") || nbt.getBoolean("Rings"); - this.ringCount = nbt.hasKey("RingCount") ? nbt.getInteger("RingCount") : 3; - this.border = !nbt.hasKey("Border") || nbt.getBoolean("Border"); - this.borderSpeed = nbt.hasKey("BorderSpeed") ? nbt.getFloat("BorderSpeed") : 1.0f; - this.accents = !nbt.hasKey("Accents") || nbt.getBoolean("Accents"); - this.accentStyle = nbt.hasKey("AccentStyle") ? nbt.getInteger("AccentStyle") : 0; - this.lightning = nbt.hasKey("Lightning") && nbt.getBoolean("Lightning"); - this.particles = !nbt.hasKey("Particles") || nbt.getBoolean("Particles"); - this.particleMotion = nbt.hasKey("ParticleMotion") ? nbt.getInteger("ParticleMotion") : 0; - this.particleDir = nbt.hasKey("ParticleDir") ? nbt.getString("ParticleDir") : ""; - this.particleSize = nbt.hasKey("ParticleSize") ? nbt.getInteger("ParticleSize") : 32; - this.particleGlow = !nbt.hasKey("ParticleGlow") || nbt.getBoolean("ParticleGlow"); - } + // Visual layer fields + this.groundFill = nbt.getBoolean("GroundFill"); + this.groundAlpha = nbt.getFloat("GroundAlpha"); + this.rings = nbt.getBoolean("Rings"); + this.ringCount = nbt.getInteger("RingCount"); + this.border = nbt.getBoolean("Border"); + this.borderSpeed = nbt.getFloat("BorderSpeed"); + this.accents = nbt.getBoolean("Accents"); + this.accentStyle = nbt.getInteger("AccentStyle"); + this.lightning = nbt.getBoolean("Lightning"); + this.particles = nbt.getBoolean("Particles"); + this.particleMotion = nbt.getInteger("ParticleMotion"); + this.particleDir = nbt.getString("ParticleDir"); + this.particleSize = nbt.getInteger("ParticleSize"); + this.particleGlow = nbt.getBoolean("ParticleGlow"); // Trap this.armTime = nbt.getInteger("ArmTime"); @@ -745,6 +655,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD this.damage = nbt.getFloat("Damage"); this.damageRadius = nbt.getFloat("DamageRadius"); this.knockback = nbt.getFloat("Knockback"); + this.visible = nbt.getBoolean("Visible"); // Hazard this.damagePerSecond = nbt.getFloat("DamagePerSecond"); @@ -783,7 +694,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD nbt.setFloat("LightningDensity", lightningDensity); nbt.setLong("DeathWorldTime", deathWorldTime); - // Visual fields — always written as top-level keys + // Visual layer fields nbt.setBoolean("GroundFill", groundFill); nbt.setFloat("GroundAlpha", groundAlpha); nbt.setBoolean("Rings", rings); @@ -807,6 +718,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD nbt.setFloat("Damage", damage); nbt.setFloat("DamageRadius", damageRadius); nbt.setFloat("Knockback", knockback); + nbt.setBoolean("Visible", visible); // Hazard nbt.setFloat("DamagePerSecond", damagePerSecond); @@ -843,7 +755,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD buffer.writeFloat(animSpeed); buffer.writeFloat(lightningDensity); - // Visual fields — always written individually + // Visual layer fields buffer.writeBoolean(groundFill); buffer.writeFloat(groundAlpha); buffer.writeBoolean(rings); @@ -859,10 +771,11 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD buffer.writeInt(particleSize); buffer.writeBoolean(particleGlow); - // Trap-specific (client needs armTime for visual state) + // Trap-specific buffer.writeInt(armTime); + buffer.writeBoolean(visible); - // Effects via NBT in buffer + // Effects NBTTagCompound effectsNbt = new NBTTagCompound(); NBTTagList effectList = new NBTTagList(); for (AbilityEffect effect : effects) { @@ -894,7 +807,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD this.animSpeed = buffer.readFloat(); this.lightningDensity = buffer.readFloat(); - // Visual fields — always read individually + // Visual layer fields this.groundFill = buffer.readBoolean(); this.groundAlpha = buffer.readFloat(); this.rings = buffer.readBoolean(); @@ -911,8 +824,9 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD this.particleGlow = buffer.readBoolean(); this.armTime = buffer.readInt(); + this.visible = buffer.readBoolean(); - // Effects via NBT + // Effects NBTTagCompound effectsNbt = ByteBufUtils.readTag(buffer); effects.clear(); if (effectsNbt != null && effectsNbt.hasKey("Effects")) { diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiTrapPresetSelector.java b/src/main/java/noppes/npcs/client/gui/SubGuiTrapPresetSelector.java new file mode 100644 index 000000000..b8fb9e376 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/SubGuiTrapPresetSelector.java @@ -0,0 +1,72 @@ +package noppes.npcs.client.gui; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.util.StatCollector; +import noppes.npcs.client.gui.util.GuiNpcButton; +import noppes.npcs.client.gui.util.SubGuiInterface; + +@SideOnly(Side.CLIENT) +public class SubGuiTrapPresetSelector extends SubGuiInterface { + + public String selectedPreset = null; + + private static final String[] PRESET_NAMES = {"HIDDEN", "VENOM", "EXPLOSIVE", "CURSED", "SHOCK", "SNARE"}; + private static final String[] PRESET_LABELS = { + "ability.preset.hidden", "ability.preset.venom", "ability.preset.explosive", + "ability.preset.cursed", "ability.preset.shock", "ability.preset.snare" + }; + private static final int[] PRESET_COLORS = {0x888888, 0x44DD44, 0xFF6611, 0xAA44FF, 0x4488FF, 0x88CCFF}; + + public SubGuiTrapPresetSelector() { + xSize = 170; + ySize = 135; + setBackground("menubg.png"); + } + + @Override + public void initGui() { + super.initGui(); + int btnW = 72; + int btnH = 20; + int gapY = 4; + int col1 = guiLeft + 7; + int col2 = col1 + btnW + 6; + int startY = guiTop + 22; + + for (int i = 0; i < 6; i++) { + int x = (i % 2 == 0) ? col1 : col2; + int y = startY + (i / 2) * (btnH + gapY); + addButton(new GuiNpcButton(i, x, y, btnW, btnH, PRESET_LABELS[i])); + } + + int cancelY = startY + 3 * (btnH + gapY) + 4; + addButton(new GuiNpcButton(10, guiLeft + xSize / 2 - 36, cancelY, 72, btnH, "gui.cancel")); + } + + @Override + protected void actionPerformed(GuiButton btn) { + super.actionPerformed(btn); + if (btn.id >= 0 && btn.id < 6) { + selectedPreset = PRESET_NAMES[btn.id]; + close(); + } else if (btn.id == 10) { + close(); + } + } + + @Override + public void drawScreen(int mouseX, int mouseY, float f) { + super.drawScreen(mouseX, mouseY, f); + drawCenteredString(fontRendererObj, StatCollector.translateToLocal("gui.applyPreset"), guiLeft + xSize / 2, guiTop + 8, 0xFFFFFF); + for (int i = 0; i < 6; i++) { + GuiNpcButton btn = getButton(i); + if (btn != null && btn.visible) { + int x = btn.xPosition + 4; + int y = btn.yPosition + 5; + drawRect(x, y, x + 10, y + 10, 0xFF000000 | PRESET_COLORS[i]); + } + } + } +} diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index f265f0fe9..eaaf48a45 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -1578,6 +1578,12 @@ ability.preset.inferno=Inferno ability.preset.arcane=Arcane ability.preset.electric=Electric ability.preset.frost=Frost +ability.preset.hidden=Hidden +ability.preset.venom=Venom +ability.preset.explosive=Explosive +ability.preset.cursed=Cursed +ability.preset.shock=Shock +ability.preset.snare=Snare # Accent Styles ability.accentStyle.static=Static