From bff45bd793525e30b8b62a57aa0f66b151ed896e Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 04:32:15 -0400 Subject: [PATCH 01/29] Save Overlays on Modification --- .../java/noppes/npcs/controllers/data/SkinOverlay.java | 1 + .../java/noppes/npcs/entity/data/DataSkinOverlays.java | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/noppes/npcs/controllers/data/SkinOverlay.java b/src/main/java/noppes/npcs/controllers/data/SkinOverlay.java index 01c236999..43a13a074 100644 --- a/src/main/java/noppes/npcs/controllers/data/SkinOverlay.java +++ b/src/main/java/noppes/npcs/controllers/data/SkinOverlay.java @@ -183,6 +183,7 @@ public class SkinOverlay implements ISkinOverlay { private void updateClient() { if (this.parent != null) { this.parent.updateClient(); + this.parent.save(); } } } diff --git a/src/main/java/noppes/npcs/entity/data/DataSkinOverlays.java b/src/main/java/noppes/npcs/entity/data/DataSkinOverlays.java index 38f48c944..569dafeb2 100644 --- a/src/main/java/noppes/npcs/entity/data/DataSkinOverlays.java +++ b/src/main/java/noppes/npcs/entity/data/DataSkinOverlays.java @@ -49,6 +49,12 @@ public class DataSkinOverlays implements IOverlayHandler { return nbttagcompound; } + public void save() { + if (parent instanceof PlayerData) { + ((PlayerData) parent).save(); + } + } + public void updateClient() { if (parent != null) { if (parent instanceof PlayerData && ((PlayerData) parent).player != null) { @@ -69,6 +75,7 @@ public class DataSkinOverlays implements IOverlayHandler { ((SkinOverlay) data).parent = this; this.overlayList.put(id, data); updateClient(); + save(); } public ISkinOverlay get(int id) { @@ -82,6 +89,7 @@ public class DataSkinOverlays implements IOverlayHandler { public boolean remove(int id) { boolean removed = this.overlayList.remove(id) != null; updateClient(); + save(); return removed; } @@ -92,5 +100,6 @@ public class DataSkinOverlays implements IOverlayHandler { public void clear() { this.overlayList.clear(); updateClient(); + save(); } } From d2a13d0e663b2043014d7e9f16c4d297d81c630b Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 14:28:38 -0400 Subject: [PATCH 02/29] Add Janino Support for Recipe, Effect and Linked Item Scripts --- .../java/noppes/npcs/controllers/data/EffectScript.java | 6 ++++++ .../java/noppes/npcs/controllers/data/LinkedItemScript.java | 6 ++++++ .../java/noppes/npcs/controllers/data/RecipeScript.java | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/main/java/noppes/npcs/controllers/data/EffectScript.java b/src/main/java/noppes/npcs/controllers/data/EffectScript.java index 457eb11d6..06b38af89 100644 --- a/src/main/java/noppes/npcs/controllers/data/EffectScript.java +++ b/src/main/java/noppes/npcs/controllers/data/EffectScript.java @@ -3,6 +3,7 @@ package noppes.npcs.controllers.data; import kamkeel.npcs.network.packets.request.script.EffectScriptPacket; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.constants.ScriptContext; +import noppes.npcs.janino.EventJaninoScript; /** * Script handler for CustomEffect scripts. @@ -35,6 +36,11 @@ public class EffectScript extends SingleScriptHandler implements IScriptHandlerP return ScriptContext.EFFECT; } + @Override + public IScriptUnit createJaninoScriptUnit() { + return new EventJaninoScript(ScriptContext.EFFECT); + } + @Override public String noticeString() { return effectId >= 0 ? "CustomEffect[" + effectId + "]" : "CustomEffect"; diff --git a/src/main/java/noppes/npcs/controllers/data/LinkedItemScript.java b/src/main/java/noppes/npcs/controllers/data/LinkedItemScript.java index a54c20727..841f6b063 100644 --- a/src/main/java/noppes/npcs/controllers/data/LinkedItemScript.java +++ b/src/main/java/noppes/npcs/controllers/data/LinkedItemScript.java @@ -3,6 +3,7 @@ package noppes.npcs.controllers.data; import kamkeel.npcs.network.packets.request.script.item.LinkedItemScriptPacket; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.constants.ScriptContext; +import noppes.npcs.janino.EventJaninoScript; /** * Script handler for LinkedItem scripts. @@ -35,6 +36,11 @@ public class LinkedItemScript extends SingleScriptHandler implements IScriptHand return ScriptContext.LINKED_ITEM; } + @Override + public IScriptUnit createJaninoScriptUnit() { + return new EventJaninoScript(ScriptContext.LINKED_ITEM); + } + @Override public String noticeString() { return "LinkedItem"; diff --git a/src/main/java/noppes/npcs/controllers/data/RecipeScript.java b/src/main/java/noppes/npcs/controllers/data/RecipeScript.java index 97f81df90..c2b8ba551 100644 --- a/src/main/java/noppes/npcs/controllers/data/RecipeScript.java +++ b/src/main/java/noppes/npcs/controllers/data/RecipeScript.java @@ -3,6 +3,7 @@ package noppes.npcs.controllers.data; import kamkeel.npcs.network.packets.request.script.RecipeScriptPacket; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.constants.ScriptContext; +import noppes.npcs.janino.EventJaninoScript; /** * Script handler for Recipe scripts (both carpentry and anvil). @@ -42,6 +43,11 @@ public class RecipeScript extends SingleScriptHandler implements IScriptHandlerP return ScriptContext.RECIPE; } + @Override + public IScriptUnit createJaninoScriptUnit() { + return new EventJaninoScript(ScriptContext.RECIPE); + } + @Override public String noticeString() { return "RecipeScript"; From 5700899beb8526461ef8d2b6d6484a38c0432964 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 15:26:39 -0400 Subject: [PATCH 03/29] Fix Job Spawner --- .../java/noppes/npcs/roles/JobSpawner.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/noppes/npcs/roles/JobSpawner.java b/src/main/java/noppes/npcs/roles/JobSpawner.java index 42a509e5a..11e71bc20 100644 --- a/src/main/java/noppes/npcs/roles/JobSpawner.java +++ b/src/main/java/noppes/npcs/roles/JobSpawner.java @@ -47,6 +47,7 @@ public class JobSpawner extends JobInterface { private EntityLivingBase target; + private boolean isResetting = false; public JobSpawner(EntityNPCInterface npc) { super(npc); @@ -314,16 +315,25 @@ public class JobSpawner extends JobInterface { @Override public void reset() { - number = 0; - if (spawned.isEmpty()) - spawned = getNearbySpawned(); + if (isResetting) + return; + isResetting = true; + try { + number = 0; + if (spawned.isEmpty()) + spawned = getNearbySpawned(); - target = null; - checkSpawns(); + target = null; + checkSpawns(); + } finally { + isResetting = false; + } } @Override public void delete() { + if (isResetting) + return; if (spawnType == 3 && npc.stats.spawnCycle == 3) { spawnEntity(compound1); spawnEntity(compound2); From aae0e64988c95627074e10e2d21348526daa84bf Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 15:27:44 -0400 Subject: [PATCH 04/29] Update dependencies.gradle --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 486ef7430..42245133a 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -35,7 +35,7 @@ */ def embedMixin = !project.hasProperty("nomixin"); dependencies { - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.5.4-GTNH:dev") +// runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.5.4-GTNH:dev") if (!project.hasProperty("noNashorn")) { runtimeOnlyNonPublishable("curse.maven:Nashorn-374413:2924946") } From f3676cd99e5f6db1a8edb0e0142e8405edbfe723 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 16:20:18 -0400 Subject: [PATCH 05/29] Update API --- build.gradle | 7 +++++++ src/api | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2d5e6e6d5..e3780cba8 100644 --- a/build.gradle +++ b/build.gradle @@ -56,5 +56,12 @@ sourceSets.main.java { srcDirs += apiDir } +// Register @hookName as a valid Javadoc tag +tasks.withType(Javadoc) { + options.tags = [ + 'hookName:a:Hook Name:' + ] +} + // Modify the existing 'build' task to depend on 'updateAPI' //tasks.apiClasses.dependsOn 'updateAPI' diff --git a/src/api b/src/api index a6c55ca33..319e77451 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit a6c55ca331112d8bbb55161a6070806213a1613b +Subproject commit 319e77451862a113b156c988a1234b2acbadfb2b From 029f460b75d2b2883164d344470c7750c9659f7f Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:07:31 -0400 Subject: [PATCH 06/29] Kill Energy Entities on Load Chunk --- .../kamkeel/npcs/entity/EntityAbilityZone.java | 9 --------- .../kamkeel/npcs/entity/EntityEnergyAbility.java | 16 ++++++++++++++++ .../kamkeel/npcs/entity/EntityEnergyDome.java | 9 --------- .../npcs/entity/EntityEnergyExplosion.java | 9 --------- .../kamkeel/npcs/entity/EntityEnergyPanel.java | 9 --------- .../npcs/entity/EntityEnergyProjectile.java | 9 --------- .../kamkeel/npcs/entity/EntityEnergySweeper.java | 9 --------- 7 files changed, 16 insertions(+), 54 deletions(-) diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java index 23120b80b..8bd722325 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java @@ -834,15 +834,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD // NBT SERIALIZATION // ═══════════════════════════════════════════════════════════════════ - @Override - protected void readEntityFromNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } - - @Override - protected void writeEntityToNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } // ═══════════════════════════════════════════════════════════════════ // SPAWN DATA (ByteBuf for client sync) diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyAbility.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyAbility.java index 46c2647e8..9d6e8ab88 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyAbility.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyAbility.java @@ -376,6 +376,22 @@ public abstract class EntityEnergyAbility extends Entity implements IEntityAddit magicData.readToNBT(nbt); } + // ==================== WORLD NBT (INTENTIONALLY EMPTY) ==================== + + /** + * Ability entities are transient — they must not survive world saves. + * If Minecraft deserializes one from a chunk, kill it immediately. + */ + @Override + protected void readEntityFromNBT(NBTTagCompound nbt) { + this.setDead(); + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbt) { + // Intentionally empty — ability entities are transient (not saved to world) + } + // ==================== SPAWN DATA ==================== /** diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java index c107b57f7..565a662d4 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java @@ -695,15 +695,6 @@ public class EntityEnergyDome extends EntityEnergyBarrier { // ==================== NBT ==================== - @Override - protected void readEntityFromNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } - - @Override - protected void writeEntityToNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } @Override protected void writeSpawnNBT(NBTTagCompound nbt) { diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyExplosion.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyExplosion.java index b23f1f269..8c69b6aa0 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyExplosion.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyExplosion.java @@ -289,15 +289,6 @@ public class EntityEnergyExplosion extends EntityEnergyAbility { // ==================== NBT ==================== - @Override - protected void readEntityFromNBT(NBTTagCompound nbt) { - // Intentionally empty - transient visual entity. - } - - @Override - protected void writeEntityToNBT(NBTTagCompound nbt) { - // Intentionally empty - transient visual entity. - } @Override protected void writeSpawnNBT(NBTTagCompound nbt) { diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java index 0ec982fe7..77c5c680d 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java @@ -730,15 +730,6 @@ public class EntityEnergyPanel extends EntityEnergyBarrier { // ==================== NBT ==================== - @Override - protected void readEntityFromNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } - - @Override - protected void writeEntityToNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } @Override protected void writeSpawnNBT(NBTTagCompound nbt) { diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java index 1d9c02fcc..c51949386 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java @@ -2413,15 +2413,6 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility { // ==================== NBT ==================== - @Override - protected void readEntityFromNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } - - @Override - protected void writeEntityToNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } @Override protected void writeSpawnNBT(NBTTagCompound nbt) { diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergySweeper.java b/src/main/java/kamkeel/npcs/entity/EntityEnergySweeper.java index d8596cf08..0c5c48c23 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergySweeper.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergySweeper.java @@ -423,15 +423,6 @@ public class EntityEnergySweeper extends EntityEnergyAbility { // ==================== NBT ==================== - @Override - protected void readEntityFromNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } - - @Override - protected void writeEntityToNBT(NBTTagCompound nbt) { - // Intentionally empty — ability entities are transient (not saved to world) - } @Override protected void writeSpawnNBT(NBTTagCompound nbt) { From 2f4ddb30ec46c31a83f1624deeea5d06496c640f Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:22:31 -0400 Subject: [PATCH 07/29] Fix Crossing Domes at Y Level --- .../kamkeel/npcs/entity/EntityEnergyDome.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java index 565a662d4..aa3e71759 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java @@ -351,8 +351,10 @@ public class EntityEnergyDome extends EntityEnergyBarrier { double radialVel = ent.motionX * nx + ent.motionY * ny + ent.motionZ * nz; if (wasInside != isInside) { - // Entity crossed the boundary this tick — push back to original side - double pushDist = wasInside ? (r - 0.3) : (r + 0.3); + // Entity crossed the boundary this tick — push back to original side. + // Use a larger buffer (1.0) to prevent flying entities from crossing + // again in a single tick (their AI hard-sets motionY every tick). + double pushDist = wasInside ? (r - 1.0) : (r + 0.3); double surfaceX = posX + nx * pushDist; double surfaceY = posY + ny * pushDist - ent.height * 0.5; double surfaceZ = posZ + nz * pushDist; @@ -366,13 +368,16 @@ public class EntityEnergyDome extends EntityEnergyBarrier { teleportEntity(ent, surfaceX, surfaceY, surfaceZ); - // Cancel radial velocity and add corrective push + // Cancel radial velocity and add corrective push (all 3 axes). + // The Y component is critical for the dome top where nx/nz ≈ 0: + // without it, flying entities get no inward push at the apex. ent.motionX -= radialVel * nx; ent.motionY -= radialVel * ny; ent.motionZ -= radialVel * nz; double pushForce = wasInside ? -0.15 : 0.15; - if (canPushInDirection(ent, nx * pushForce, 0, nz * pushForce)) { + if (canPushInDirection(ent, nx * pushForce, ny * pushForce, nz * pushForce)) { ent.motionX += nx * pushForce; + ent.motionY += ny * pushForce; ent.motionZ += nz * pushForce; } ent.velocityChanged = true; @@ -387,8 +392,9 @@ public class EntityEnergyDome extends EntityEnergyBarrier { ent.motionY -= radialVel * ny; ent.motionZ -= radialVel * nz; double pushForce = isInside ? -0.05 : 0.05; - if (canPushInDirection(ent, nx * pushForce, 0, nz * pushForce)) { + if (canPushInDirection(ent, nx * pushForce, ny * pushForce, nz * pushForce)) { ent.motionX += nx * pushForce; + ent.motionY += ny * pushForce; ent.motionZ += nz * pushForce; } ent.velocityChanged = true; From b960ef602c496cbc679ce868b62b1a8e25cb9d58 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:26:55 -0400 Subject: [PATCH 08/29] Add Absorbing Radius to Barriers --- src/api | 2 +- .../data/energy/EnergyBarrierData.java | 16 +++++++++ .../ability/type/energy/AbilityBarrier.java | 11 ++++++ .../npcs/entity/EntityEnergyBarrier.java | 36 +++++++++++++++++-- .../scripted/entity/ScriptEnergyBarrier.java | 8 +++++ .../assets/customnpcs/lang/en_US.lang | 2 ++ 6 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/api b/src/api index 319e77451..10f8cfdea 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit 319e77451862a113b156c988a1234b2acbadfb2b +Subproject commit 10f8cfdeaa961f52f3229e07c5fc9aa0fb15e734 diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/energy/EnergyBarrierData.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/energy/EnergyBarrierData.java index bfd48a3b3..40c30fe95 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/energy/EnergyBarrierData.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/energy/EnergyBarrierData.java @@ -32,6 +32,11 @@ public class EnergyBarrierData { // Absorbing: caster's damage is redirected to the barrier public boolean absorbing = false; + // Absorb radius: max distance from barrier's current position for absorption to work + // -1 = no radius limit (absorb regardless of distance) + // 0 = use barrier's own extent as the radius (dome radius, panel half-size, etc.) + // positive = owner must be within this many blocks of the barrier entity + public float absorbRadius = 0.0f; // Melee: barrier can be hit by melee attacks public boolean meleeEnabled = false; @@ -148,6 +153,14 @@ public class EnergyBarrierData { this.targetOwner = targetOwner; } + public float getAbsorbRadius() { + return absorbRadius; + } + + public void setAbsorbRadius(float absorbRadius) { + this.absorbRadius = absorbRadius; + } + // ==================== NBT ==================== public void writeNBT(NBTTagCompound nbt) { @@ -160,6 +173,7 @@ public class EnergyBarrierData { nbt.setBoolean("barrierKnockback", knockbackEnabled); nbt.setFloat("barrierKnockbackStr", knockbackStrength); nbt.setBoolean("barrierAbsorbing", absorbing); + nbt.setFloat("barrierAbsorbRadius", absorbRadius); nbt.setBoolean("barrierMelee", meleeEnabled); nbt.setFloat("barrierMeleeMult", meleeDamageMultiplier); nbt.setBoolean("barrierReflect", reflect); @@ -183,6 +197,7 @@ public class EnergyBarrierData { knockbackEnabled = nbt.hasKey("barrierKnockback") && nbt.getBoolean("barrierKnockback"); knockbackStrength = nbt.hasKey("barrierKnockbackStr") ? nbt.getFloat("barrierKnockbackStr") : 1.0f; absorbing = nbt.hasKey("barrierAbsorbing") && nbt.getBoolean("barrierAbsorbing"); + absorbRadius = nbt.hasKey("barrierAbsorbRadius") ? nbt.getFloat("barrierAbsorbRadius") : 0.0f; meleeEnabled = nbt.hasKey("barrierMelee") && nbt.getBoolean("barrierMelee"); meleeDamageMultiplier = nbt.hasKey("barrierMeleeMult") ? nbt.getFloat("barrierMeleeMult") : 1.0f; reflect = nbt.hasKey("barrierReflect") && nbt.getBoolean("barrierReflect"); @@ -207,6 +222,7 @@ public class EnergyBarrierData { copy.knockbackEnabled = knockbackEnabled; copy.knockbackStrength = knockbackStrength; copy.absorbing = absorbing; + copy.absorbRadius = absorbRadius; copy.meleeEnabled = meleeEnabled; copy.meleeDamageMultiplier = meleeDamageMultiplier; copy.reflect = reflect; diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/energy/AbilityBarrier.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/energy/AbilityBarrier.java index 5d12b4c11..1e3df2e02 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/energy/AbilityBarrier.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/energy/AbilityBarrier.java @@ -292,6 +292,14 @@ public abstract class AbilityBarrier extends AbilityEnergy { barrierData.absorbing = absorbing; } + public float getAbsorbRadius() { + return barrierData.absorbRadius; + } + + public void setAbsorbRadius(float radius) { + barrierData.absorbRadius = radius; + } + // Reflection data public boolean isReflect() { return barrierData.reflect; @@ -395,6 +403,9 @@ public abstract class AbilityBarrier extends AbilityEnergy { .range(0, 10).visibleWhen(this::isKnockbackEnabled)); defs.add(FieldDef.boolField("ability.absorbing", this::isAbsorbing, this::setAbsorbing) .hover("ability.hover.absorbing")); + defs.add(FieldDef.floatField("ability.absorbRadius", this::getAbsorbRadius, this::setAbsorbRadius) + .range(-1, 128).visibleWhen(this::isAbsorbing) + .hover("ability.hover.absorbRadius")); defs.add(FieldDef.boolField("ability.reflect", this::isReflect, this::setReflect) .hover("ability.hover.reflect")); defs.add(FieldDef.floatField("ability.reflectStrength", this::getReflectStrengthPct, this::setReflectStrengthPct) diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java index 872b7acb3..9683c2894 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java @@ -630,7 +630,13 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility { /** * Find a barrier that would absorb damage for the given entity (its caster). - * Returns the barrier if: absorbing is enabled AND entity is the barrier's owner. + * Returns the barrier if: absorbing is enabled, entity is the barrier's owner, + * and entity is within the barrier's absorb radius of the barrier's current position. + * + * Absorb radius behavior: + * -1 = no limit (absorb regardless of distance) + * 0 = uses the barrier's extent as the radius (dome radius, panel half-size, etc.) + * >0 = owner must be within this many blocks of the barrier entity */ public static EntityEnergyBarrier getAbsorbingBarrier(Entity entity) { if (entity == null || entity.worldObj == null) return null; @@ -638,7 +644,33 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility { for (EntityEnergyBarrier barrier : barriers) { if (barrier.isDead) continue; if (!barrier.barrierData.absorbing) continue; - if (barrier.ownerEntityId == entity.getEntityId()) { + if (barrier.ownerEntityId != entity.getEntityId()) continue; + + float absorbRadius = barrier.barrierData.absorbRadius; + + // -1 = unlimited range + if (absorbRadius < 0) { + return barrier; + } + + // 0 = use barrier's own extent as the effective radius + // Domes: dome radius. Panels: max(width, height) * 0.5 + 1.0, doubled. + float effectiveRadius; + if (absorbRadius > 0) { + effectiveRadius = absorbRadius; + } else if (barrier instanceof EntityEnergyDome) { + effectiveRadius = barrier.getMaxExtent(); + } else { + effectiveRadius = barrier.getMaxExtent() * 3.0f; + } + + // Distance check from barrier's current position to the owner + double dx = entity.posX - barrier.posX; + double dy = (entity.posY + entity.height * 0.5) - barrier.posY; + double dz = entity.posZ - barrier.posZ; + double distSq = dx * dx + dy * dy + dz * dz; + + if (distSq <= (double) effectiveRadius * effectiveRadius) { return barrier; } } diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyBarrier.java b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyBarrier.java index f18b86320..16d96ddc1 100644 --- a/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyBarrier.java +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyBarrier.java @@ -121,6 +121,14 @@ public class ScriptEnergyBarrier extends ScriptEn entity.getBarrierData().absorbing = absorbing; } + public float getAbsorbRadius() { + return entity.getBarrierData().absorbRadius; + } + + public void setAbsorbRadius(float radius) { + entity.getBarrierData().absorbRadius = radius; + } + public boolean isReflect() { return entity.getBarrierData().reflect; } diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index 913a3a257..f375c56c2 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -2165,6 +2165,7 @@ ability.solid=Solid ability.knockbackEnabled=Knockback ability.knockbackStrength=Knockback Strength ability.absorbing=Absorbing +ability.absorbRadius=Absorb Radius ability.reflect=Reflect ability.reflectStrength=Strength (%) ability.meleeEnabled=Allows Melee @@ -2175,6 +2176,7 @@ ability.hover.followCaster=Dome stays centered on the caster as they move ability.hover.solid=Entities cannot pass through the barrier boundary ability.hover.knockbackEnabled=Repulsion field pushing entities away from barrier surface ability.hover.absorbing=Damage dealt to the caster is redirected to the barrier's health +ability.hover.absorbRadius=Max distance from the barrier for absorption to work. -1 = unlimited, 0 = barrier extent (dome radius or panel size x3), positive = block radius ability.hover.reflect=Rebounds non-breaking projectiles and reduces their damage by Strength ability.targetOwner=Target Owner ability.hover.targetOwner=Sets the reflected projectile's target to the original caster. With homing enabled, the projectile will home toward its former caster. From 993c9d09c3141ccd4e3e5ad7b9f1aadf2ce5919c Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:27:11 -0400 Subject: [PATCH 09/29] Kill Zones on Chunk Load / World Load --- .../java/kamkeel/npcs/entity/EntityAbilityZone.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java index 8bd722325..25e56cbd8 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java @@ -321,6 +321,16 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD protected void entityInit() { } + @Override + protected void readEntityFromNBT(NBTTagCompound nbt) { + this.setDead(); + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbt) { + // Intentionally empty — ability entities are transient (not saved to world) + } + @Override public void handleHealthUpdate(byte id) { if (id == TRIGGER_FLASH_STATUS) { From 5bb0d6db58e2aa02eb4ca03e5d82056c4a410326 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:32:58 -0400 Subject: [PATCH 10/29] Update dependencies.gradle --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 42245133a..33dc51ee2 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -35,7 +35,7 @@ */ def embedMixin = !project.hasProperty("nomixin"); dependencies { -// runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.5.4-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.44-GTNH:dev") if (!project.hasProperty("noNashorn")) { runtimeOnlyNonPublishable("curse.maven:Nashorn-374413:2924946") } From 0977f7422afca525336cf203742ba61963d50485 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:36:55 -0400 Subject: [PATCH 11/29] Renaming Scheme for Energy Handler and Energy Extender --- src/api | 2 +- .../npcs/controllers/EnergyController.java | 22 +++++++++---------- ...nergyHandler.java => IEnergyExtender.java} | 2 +- .../java/noppes/npcs/scripted/NpcAPI.java | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) rename src/main/java/kamkeel/npcs/controllers/data/energy/{IEnergyHandler.java => IEnergyExtender.java} (98%) diff --git a/src/api b/src/api index 10f8cfdea..4ee1c41d0 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit 10f8cfdeaa961f52f3229e07c5fc9aa0fb15e734 +Subproject commit 4ee1c41d061eb4638b923cd3a430c7b63767ebdb diff --git a/src/main/java/kamkeel/npcs/controllers/EnergyController.java b/src/main/java/kamkeel/npcs/controllers/EnergyController.java index ffd25c400..5b6bc123a 100644 --- a/src/main/java/kamkeel/npcs/controllers/EnergyController.java +++ b/src/main/java/kamkeel/npcs/controllers/EnergyController.java @@ -1,12 +1,12 @@ package kamkeel.npcs.controllers; -import kamkeel.npcs.controllers.data.energy.IEnergyHandler; +import kamkeel.npcs.controllers.data.energy.IEnergyExtender; import kamkeel.npcs.entity.*; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; -import noppes.npcs.api.IEnergyController; +import noppes.npcs.api.IEnergyHandler; import noppes.npcs.api.IWorld; import noppes.npcs.api.entity.*; import noppes.npcs.api.entity.IEnergyExplosion; @@ -22,22 +22,22 @@ import java.util.List; * damage through registered handlers when entities have customDamageData * but no sourceAbility. */ -public class EnergyController implements IEnergyController { +public class EnergyController implements IEnergyHandler { public static EnergyController Instance = new EnergyController(); - private final List handlers = new ArrayList<>(); + private final List extenders = new ArrayList<>(); // ═══════════════════════════════════════════════════════════════════ // HANDLER REGISTRATION // ═══════════════════════════════════════════════════════════════════ - public void registerHandler(IEnergyHandler handler) { - handlers.add(handler); + public void registerExtender(IEnergyExtender handler) { + extenders.add(handler); } - public List getHandlers() { - return handlers; + public List getExtenders() { + return extenders; } // ═══════════════════════════════════════════════════════════════════ @@ -53,7 +53,7 @@ public class EnergyController implements IEnergyController { double kbDirX, double kbDirZ, float damageMultiplier, NBTTagCompound damageData) { - for (IEnergyHandler handler : handlers) { + for (IEnergyExtender handler : extenders) { if (handler.onEnergyDamage(energyEntity, owner, target, damage, knockback, knockbackUp, kbDirX, kbDirZ, damageMultiplier, damageData)) { return true; @@ -68,7 +68,7 @@ public class EnergyController implements IEnergyController { public float fireModifyEnergyDamage(Entity energyEntity, EntityLivingBase owner, float baseDamage, NBTTagCompound damageData) { float damage = baseDamage; - for (IEnergyHandler handler : handlers) { + for (IEnergyExtender handler : extenders) { damage = handler.modifyEnergyDamage(energyEntity, owner, damage, damageData); } return damage; @@ -150,7 +150,7 @@ public class EnergyController implements IEnergyController { } // ═══════════════════════════════════════════════════════════════════ - // IEnergyController (Script API methods) + // IEnergyHandler (Script API methods) // ═══════════════════════════════════════════════════════════════════ @Override diff --git a/src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyHandler.java b/src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyExtender.java similarity index 98% rename from src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyHandler.java rename to src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyExtender.java index bd2e39a74..2dcd2fe43 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyHandler.java +++ b/src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyExtender.java @@ -9,7 +9,7 @@ import net.minecraft.nbt.NBTTagCompound; * Registered with EnergyController. DBC Addon implements this to route * script-created energy entity damage through the DBC damage pipeline. */ -public interface IEnergyHandler { +public interface IEnergyExtender { /** * Handle damage for an energy entity with custom damage data. diff --git a/src/main/java/noppes/npcs/scripted/NpcAPI.java b/src/main/java/noppes/npcs/scripted/NpcAPI.java index fd301b802..1b0d392b2 100644 --- a/src/main/java/noppes/npcs/scripted/NpcAPI.java +++ b/src/main/java/noppes/npcs/scripted/NpcAPI.java @@ -63,7 +63,7 @@ import noppes.npcs.api.IBlock; import noppes.npcs.api.ICommand; import noppes.npcs.api.IContainer; import noppes.npcs.api.IDamageSource; -import noppes.npcs.api.IEnergyController; +import noppes.npcs.api.IEnergyHandler; import noppes.npcs.api.INbt; import noppes.npcs.api.IParticle; import noppes.npcs.api.IPos; @@ -623,7 +623,7 @@ public class NpcAPI extends AbstractNpcAPI { } @Override - public IEnergyController getEnergyController() { + public IEnergyHandler getEnergyHandler() { return EnergyController.Instance; } From c423ab43f1d62317e828cff9c14d019f6835f455 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:38:59 -0400 Subject: [PATCH 12/29] 1.11 Pre Release 2 --- build.gradle | 2 +- src/main/java/noppes/npcs/CustomNpcs.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index e3780cba8..6826ce5d5 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'idea' } -version = "1.11-prerelease1" +version = "1.11-prerelease2" group= "kamkeel.customnpc-plus" archivesBaseName = "CustomNPC-Plus" diff --git a/src/main/java/noppes/npcs/CustomNpcs.java b/src/main/java/noppes/npcs/CustomNpcs.java index be2be429a..67a55f58d 100644 --- a/src/main/java/noppes/npcs/CustomNpcs.java +++ b/src/main/java/noppes/npcs/CustomNpcs.java @@ -124,7 +124,7 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; -@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-prerelease1") +@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-prerelease2") public class CustomNpcs { @SidedProxy(clientSide = "noppes.npcs.client.ClientProxy", serverSide = "noppes.npcs.CommonProxy") From 75fcf542a0d942410a01885bd3358111af750eec Mon Sep 17 00:00:00 2001 From: Vidal Date: Mon, 9 Mar 2026 18:44:15 -0300 Subject: [PATCH 13/29] Square telegraph --- src/api | 2 +- .../java/kamkeel/npcs/controllers/TelegraphController.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api b/src/api index 319e77451..88c90ef0e 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit 319e77451862a113b156c988a1234b2acbadfb2b +Subproject commit 88c90ef0e43e650fb2d8f44c6e2369ec72ffb7ac diff --git a/src/main/java/kamkeel/npcs/controllers/TelegraphController.java b/src/main/java/kamkeel/npcs/controllers/TelegraphController.java index 5449245a6..abce5dc05 100644 --- a/src/main/java/kamkeel/npcs/controllers/TelegraphController.java +++ b/src/main/java/kamkeel/npcs/controllers/TelegraphController.java @@ -308,6 +308,11 @@ public class TelegraphController implements ITelegraphHandler { return new ScriptTelegraph(Telegraph.cone(length, angle)); } + @Override + public ITelegraph createSquare(float radius) { + return new ScriptTelegraph(Telegraph.square(radius)); + } + @Override public ITelegraph createPoint() { return new ScriptTelegraph(Telegraph.point()); From d8b18d7058a9c5b766e04f533b2aafa7d84c8163 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:47:13 -0400 Subject: [PATCH 14/29] Allow Null Owners in Energy --- .../java/kamkeel/npcs/entity/EntityAbilityZone.java | 6 +++--- .../java/kamkeel/npcs/entity/EntityEnergyDome.java | 2 +- .../java/kamkeel/npcs/entity/EntityEnergyPanel.java | 2 +- .../kamkeel/npcs/entity/EntityEnergyProjectile.java | 2 +- .../java/kamkeel/npcs/entity/EntityEnergySweeper.java | 10 ++++++---- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java index 25e56cbd8..7913386c6 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityZone.java @@ -201,7 +201,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD double x, double y, double z) { this(world); this.zoneType = type; - this.ownerEntityId = owner.getEntityId(); + this.ownerEntityId = owner != null ? owner.getEntityId() : -1; // Snap to ground with tiny offset to prevent z-clipping double groundY = Ability.findGroundLevel(world, x, y, z) + GROUND_OFFSET; this.setPosition(x, groundY, z); @@ -213,7 +213,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD */ public void initAsHazard(Entity owner, double x, double y, double z) { this.zoneType = ZoneType.HAZARD; - this.ownerEntityId = owner.getEntityId(); + this.ownerEntityId = owner != null ? owner.getEntityId() : -1; double groundY = Ability.findGroundLevel(worldObj, x, y, z) + GROUND_OFFSET; this.setPosition(x, groundY, z); this.ticksSinceDamage = damageInterval; @@ -225,7 +225,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD */ public void initAsTrap(Entity owner, double x, double y, double z) { this.zoneType = ZoneType.TRAP; - this.ownerEntityId = owner.getEntityId(); + this.ownerEntityId = owner != null ? owner.getEntityId() : -1; double groundY = Ability.findGroundLevel(worldObj, x, y, z) + GROUND_OFFSET; this.setPosition(x, groundY, z); this.ticksSinceLastTrigger = triggerCooldown; diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java index aa3e71759..697fa4c16 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java @@ -51,7 +51,7 @@ public class EntityEnergyDome extends EntityEnergyBarrier { this(world); this.noClip = true; this.stepHeight = 0.0F; - this.ownerEntityId = owner.getEntityId(); + this.ownerEntityId = owner != null ? owner.getEntityId() : -1; this.setDomeRadius(domeRadius); // Defensive copy: entities must never share data objects with the source ability. this.displayData = display != null ? display.copy() : new EnergyDisplayData(); diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java index 77c5c680d..96949bbd4 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java @@ -55,7 +55,7 @@ public class EntityEnergyPanel extends EntityEnergyBarrier { EnergyBarrierData barrier, EnergyPanelData panel) { this(world); this.setPosition(x, y + panel.heightOffset, z); - this.ownerEntityId = owner.getEntityId(); + this.ownerEntityId = owner != null ? owner.getEntityId() : -1; this.panelYaw = yaw; this.prevPanelYaw = yaw; this.mode = mode; diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java index c51949386..1527f644d 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java @@ -184,7 +184,7 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility { this.startY = y; this.startZ = z; - this.ownerEntityId = owner.getEntityId(); + this.ownerEntityId = owner != null ? owner.getEntityId() : -1; this.targetEntityId = target != null ? target.getEntityId() : -1; // Visual diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergySweeper.java b/src/main/java/kamkeel/npcs/entity/EntityEnergySweeper.java index 0c5c48c23..263ecd36c 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergySweeper.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergySweeper.java @@ -78,7 +78,7 @@ public class EntityEnergySweeper extends EntityEnergyAbility { boolean lockOnTarget) { this(world); - this.ownerEntityId = owner.getEntityId(); + this.ownerEntityId = owner != null ? owner.getEntityId() : -1; this.targetEntityId = target != null ? target.getEntityId() : -1; this.beamLength = beamLength; this.beamWidth = beamWidth; @@ -95,16 +95,18 @@ public class EntityEnergySweeper extends EntityEnergyAbility { this.maxTicks = (int) ((360.0f * numberOfRotations) / sweepSpeed) + 10; // Calculate base yaw - if (lockOnTarget && target != null) { + if (lockOnTarget && target != null && owner != null) { double dx = target.posX - owner.posX; double dz = target.posZ - owner.posZ; this.baseYaw = (float) Math.toDegrees(Math.atan2(-dx, dz)); - } else { + } else if (owner != null) { this.baseYaw = owner.rotationYaw; } // Position at beam center (bottom at feet, top at feet + beamHeight) - this.setPosition(owner.posX, owner.posY + beamHeight / 2.0, owner.posZ); + if (owner != null) { + this.setPosition(owner.posX, owner.posY + beamHeight / 2.0, owner.posZ); + } // Ready to deal damage immediately this.ticksSinceDamage = damageInterval; From 0244db1e5cc2c5730c634731848f990762d4cd5c Mon Sep 17 00:00:00 2001 From: Vidal Date: Mon, 9 Mar 2026 18:47:51 -0300 Subject: [PATCH 15/29] Ooopsie --- src/api | 2 +- .../java/kamkeel/npcs/controllers/TelegraphController.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api b/src/api index 4ee1c41d0..c7ef60136 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit 4ee1c41d061eb4638b923cd3a430c7b63767ebdb +Subproject commit c7ef60136c04d399b9f28f2d25195d68f08d9f4b diff --git a/src/main/java/kamkeel/npcs/controllers/TelegraphController.java b/src/main/java/kamkeel/npcs/controllers/TelegraphController.java index 5449245a6..abce5dc05 100644 --- a/src/main/java/kamkeel/npcs/controllers/TelegraphController.java +++ b/src/main/java/kamkeel/npcs/controllers/TelegraphController.java @@ -308,6 +308,11 @@ public class TelegraphController implements ITelegraphHandler { return new ScriptTelegraph(Telegraph.cone(length, angle)); } + @Override + public ITelegraph createSquare(float radius) { + return new ScriptTelegraph(Telegraph.square(radius)); + } + @Override public ITelegraph createPoint() { return new ScriptTelegraph(Telegraph.point()); From 2dd34b21e6268d6f1cfa5cfd6e6bc58f1db93506 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 17:50:46 -0400 Subject: [PATCH 16/29] Documentation to Square Telegraph --- src/api | 2 +- .../java/kamkeel/npcs/controllers/TelegraphController.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api b/src/api index c7ef60136..efce1a99b 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit c7ef60136c04d399b9f28f2d25195d68f08d9f4b +Subproject commit efce1a99b75dc6931e406b77bf188115befb54e2 diff --git a/src/main/java/kamkeel/npcs/controllers/TelegraphController.java b/src/main/java/kamkeel/npcs/controllers/TelegraphController.java index abce5dc05..7956f4ff6 100644 --- a/src/main/java/kamkeel/npcs/controllers/TelegraphController.java +++ b/src/main/java/kamkeel/npcs/controllers/TelegraphController.java @@ -337,6 +337,9 @@ public class TelegraphController implements ITelegraphHandler { case CONE: telegraph = Telegraph.cone(5.0f, 45.0f); break; + case SQUARE: + telegraph = Telegraph.square(3.0f); + break; case POINT: telegraph = Telegraph.point(); break; From b013f412b23efe59979d388b39becaae288add8e Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 19:54:29 -0400 Subject: [PATCH 17/29] Six Overlays in Singleplayer --- .../java/kamkeel/npcs/controllers/SyncController.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/kamkeel/npcs/controllers/SyncController.java b/src/main/java/kamkeel/npcs/controllers/SyncController.java index 6403a2c1b..d0ee7b16c 100644 --- a/src/main/java/kamkeel/npcs/controllers/SyncController.java +++ b/src/main/java/kamkeel/npcs/controllers/SyncController.java @@ -184,6 +184,14 @@ public class SyncController { DBCAddon.instance.syncPlayer(player); syncPlayerData(player, false); PartyInfoPacket.sendPartyData(player); + + // Sync skin overlays after full handshake to ensure the client is ready. + // Overlays are not part of getSyncNBTFull(), and packets sent during + // PlayerLoggedInEvent can arrive before the client entity exists. + PlayerData data = PlayerData.get(player); + if (data != null) { + data.skinOverlays.updateClient(); + } } private static EnumMap getServerRevisionSnapshot() { From 676c0626d19900e94aeb9d2d5d569ca765f1d4c6 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 19:56:25 -0400 Subject: [PATCH 18/29] Fix Some Bounding Box Issues!!!!! --- src/main/java/noppes/npcs/entity/EntityCustomNpc.java | 3 ++- src/main/java/noppes/npcs/entity/EntityNPCGolem.java | 4 +++- src/main/java/noppes/npcs/entity/EntityNPCInterface.java | 3 ++- src/main/java/noppes/npcs/entity/EntityNpcDragon.java | 4 +++- src/main/java/noppes/npcs/entity/EntityNpcSlime.java | 4 +++- src/main/java/noppes/npcs/entity/old/EntityNPCEnderman.java | 4 +++- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/noppes/npcs/entity/EntityCustomNpc.java b/src/main/java/noppes/npcs/entity/EntityCustomNpc.java index b1a3c44f3..e1ff3544d 100644 --- a/src/main/java/noppes/npcs/entity/EntityCustomNpc.java +++ b/src/main/java/noppes/npcs/entity/EntityCustomNpc.java @@ -86,7 +86,8 @@ public class EntityCustomNpc extends EntityNPCFlying { worldObj.MAX_ENTITY_RADIUS = newWidth / 2; } - setSize(newWidth, newHeight); + this.width = newWidth; + this.height = newHeight; this.setPosition(posX, posY, posZ); } } diff --git a/src/main/java/noppes/npcs/entity/EntityNPCGolem.java b/src/main/java/noppes/npcs/entity/EntityNPCGolem.java index bb6544071..5eb111e4d 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCGolem.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCGolem.java @@ -37,7 +37,9 @@ public class EntityNPCGolem extends EntityNPCInterface { newWidth = 1.4f; newHeight = 2.5f; } - setSize(newWidth, newHeight); + this.width = newWidth; + this.height = newHeight; + this.setPosition(posX, posY, posZ); if (width / 2 > World.MAX_ENTITY_RADIUS) { World.MAX_ENTITY_RADIUS = width / 2; } diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index c21e13c8b..77927d4b1 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -1285,7 +1285,8 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti World.MAX_ENTITY_RADIUS = newWidth / 2; } - setSize(newWidth, newHeight); + this.width = newWidth; + this.height = newHeight; this.setPosition(posX, posY, posZ); } diff --git a/src/main/java/noppes/npcs/entity/EntityNpcDragon.java b/src/main/java/noppes/npcs/entity/EntityNpcDragon.java index a9df9da99..c5952fa3c 100644 --- a/src/main/java/noppes/npcs/entity/EntityNpcDragon.java +++ b/src/main/java/noppes/npcs/entity/EntityNpcDragon.java @@ -115,7 +115,9 @@ public class EntityNpcDragon extends EntityNPCInterface { // } @Override public void updateHitbox() { - setSize(1.8f, 1.4f); + this.width = 1.8f; + this.height = 1.4f; + this.setPosition(posX, posY, posZ); if (width / 2 > World.MAX_ENTITY_RADIUS) { World.MAX_ENTITY_RADIUS = width / 2; } diff --git a/src/main/java/noppes/npcs/entity/EntityNpcSlime.java b/src/main/java/noppes/npcs/entity/EntityNpcSlime.java index 9794de069..93bdc5a7d 100644 --- a/src/main/java/noppes/npcs/entity/EntityNpcSlime.java +++ b/src/main/java/noppes/npcs/entity/EntityNpcSlime.java @@ -17,7 +17,9 @@ public class EntityNpcSlime extends EntityNPCInterface { @Override public void updateHitbox() { - setSize(0.8f, 0.8f); + this.width = 0.8f; + this.height = 0.8f; + this.setPosition(posX, posY, posZ); if (width / 2 > World.MAX_ENTITY_RADIUS) { World.MAX_ENTITY_RADIUS = width / 2; } diff --git a/src/main/java/noppes/npcs/entity/old/EntityNPCEnderman.java b/src/main/java/noppes/npcs/entity/old/EntityNPCEnderman.java index 958237c17..6479f90c7 100644 --- a/src/main/java/noppes/npcs/entity/old/EntityNPCEnderman.java +++ b/src/main/java/noppes/npcs/entity/old/EntityNPCEnderman.java @@ -40,7 +40,9 @@ public class EntityNPCEnderman extends EntityNpcEnderchibi { newHeight = (newHeight / 5f) * display.modelSize; newWidth = Math.max(newWidth, 0.00001f); newHeight = Math.max(newHeight, 0.00001f); - setSize(newWidth, newHeight); + this.width = newWidth; + this.height = newHeight; + this.setPosition(posX, posY, posZ); if (width / 2 > World.MAX_ENTITY_RADIUS) { World.MAX_ENTITY_RADIUS = width / 2; } From b0168c1d55d7a6636929ee5490fd8b6f412e1ec2 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 22:15:22 -0400 Subject: [PATCH 19/29] Fix Some issues with Lang Setup, Button Widths, Moving System --- .../gui/global/GuiAbilityDirectory.java | 59 ++++++++++++++----- .../gui/global/GuiAnimationDirectory.java | 26 ++++---- .../client/gui/global/GuiEffectDirectory.java | 3 +- .../gui/global/GuiLinkedItemDirectory.java | 3 +- .../gui/util/GuiDirectoryCategorized.java | 23 ++++++-- .../npcs/client/gui/util/GuiNpcButton.java | 57 ++++++++++++++++-- .../assets/customnpcs/lang/en_US.lang | 11 ++++ 7 files changed, 141 insertions(+), 41 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java index 8c37c1e39..7599bb18e 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java @@ -269,7 +269,24 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized @Override protected void onCloneItem() { - // Abilities don't support cloning through this mechanism + if (currentIsBuiltIn) return; + if (isChainedMode() && selectedChain != null) { + ChainedAbility clone = new ChainedAbility(); + clone.readNBT(selectedChain.writeNBT(false)); + String cloneName = selectedChain.getName() + "_copy"; + clone.setName(cloneName); + PacketClient.sendClient(new ChainedAbilitySavePacket(clone.writeNBT(false))); + if (selectedCatId >= 0) requestItemsInCategory(selectedCatId); + } else if (selectedAbility != null) { + Ability clone = AbilityController.Instance.fromNBT(selectedAbility.writeNBT(false)); + if (clone != null) { + clone.setId(UUID.randomUUID().toString()); + String cloneName = selectedAbility.getName() + "_copy"; + clone.setName(cloneName); + PacketClient.sendClient(new CustomAbilitySavePacket(clone.writeNBT(false))); + if (selectedCatId >= 0 && isCustomMode()) requestItemsInCategory(selectedCatId); + } + } } @Override @@ -672,10 +689,10 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized if (!isPlaying || isPaused) { String statusKey = isPaused ? "animation.paused" : "animation.stopped"; - addLabel(new GuiNpcLabel(90, statusKey, btnX, playY + 5)); + addLabel(new GuiNpcLabel(90, statusKey, btnX, playY + 5, 0xFFFFFF)); addButton(new GuiTexturedButton(91, "", btnX + 65, playY, 11, 20, animTexture, 18, 71)); } else { - addLabel(new GuiNpcLabel(90, "animation.playing", btnX, playY + 5)); + addLabel(new GuiNpcLabel(90, "animation.playing", btnX, playY + 5, 0xFFFFFF)); addButton(new GuiTexturedButton(92, "", btnX + 65, playY, 14, 20, animTexture, 0, 71)); } if (isActive) { @@ -683,13 +700,19 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized } } - // Edit + Remove at bottom - int editY = contentY + contentH - btnH * 2 - gap; - GuiNpcButton editBtn = new GuiNpcButton(51, rightX, editY, rightPanelW, btnH, "gui.edit"); + // Edit + Copy on one row, Remove below + int btnY = contentY + contentH - btnH * 2 - gap; + int halfW = (rightPanelW - gap) / 2; + + GuiNpcButton editBtn = new GuiNpcButton(51, rightX, btnY, halfW, btnH, "gui.edit"); editBtn.enabled = hasSelectedItem() && movePhase == 0 && !currentIsBuiltIn; addButton(editBtn); - int removeY = editY + btnH + gap; + GuiNpcButton cloneBtn = new GuiNpcButton(52, rightX + halfW + gap, btnY, halfW, btnH, "gui.copy"); + cloneBtn.enabled = hasSelectedItem() && movePhase == 0 && !currentIsBuiltIn; + addButton(cloneBtn); + + int removeY = btnY + btnH + gap; GuiNpcButton removeBtn = new GuiNpcButton(53, rightX, removeY, rightPanelW, btnH, "gui.remove"); removeBtn.enabled = hasSelectedItem() && movePhase == 0 && !currentIsBuiltIn; removeBtn.setTextColor(0xFF5555); @@ -783,6 +806,8 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); GL11.glDisable(GL11.GL_TEXTURE_2D); OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + GL11.glDisable(GL11.GL_COLOR_MATERIAL); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); GL11.glDisable(GL11.GL_SCISSOR_TEST); GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); @@ -836,6 +861,8 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); GL11.glDisable(GL11.GL_TEXTURE_2D); OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + GL11.glDisable(GL11.GL_COLOR_MATERIAL); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); GL11.glPopMatrix(); } @@ -959,32 +986,32 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized if (isChainedMode() && selectedChain != null) { fontRendererObj.drawString(selectedChain.getDisplayName(), x, y, 0xFFFFFF, true); y += 14; - fontRendererObj.drawString("Entries: " + selectedChain.getEntries().size(), x, y, 0xB5B5B5, false); + fontRendererObj.drawString(StatCollector.translateToLocal("ability.entries") + ": " + selectedChain.getEntries().size(), x, y, 0xB5B5B5, false); y += 12; - fontRendererObj.drawString("Cooldown: " + selectedChain.getCooldownTicks() + "t", x, y, 0xB5B5B5, false); + fontRendererObj.drawString(StatCollector.translateToLocal("ability.cooldown") + ": " + selectedChain.getCooldownTicks() + "t", x, y, 0xB5B5B5, false); y += 12; UserType ut = selectedChain.getAllowedBy(); - fontRendererObj.drawString("Allowed: " + ut.name(), x, y, 0xB5B5B5, false); + fontRendererObj.drawString(StatCollector.translateToLocal("ability.allowed") + ": " + ut.name(), x, y, 0xB5B5B5, false); } else if (selectedAbility != null) { fontRendererObj.drawString(selectedAbility.getDisplayName(), x, y, 0xFFFFFF, true); y += 14; String typeId = selectedAbility.getTypeId(); String typeName = StatCollector.translateToLocal(typeId); - fontRendererObj.drawString("Type: " + typeName, x, y, 0xFFAE0D, false); + fontRendererObj.drawString(StatCollector.translateToLocal("gui.type") + ": " + typeName, x, y, 0xFFAE0D, false); y += 12; - fontRendererObj.drawString("Cooldown: " + selectedAbility.getCooldownTicks() + "t", x, y, 0xB5B5B5, false); + fontRendererObj.drawString(StatCollector.translateToLocal("ability.cooldown") + ": " + selectedAbility.getCooldownTicks() + "t", x, y, 0xB5B5B5, false); y += 12; - fontRendererObj.drawString("Windup: " + selectedAbility.getWindUpTicks() + "t", x, y, 0xB5B5B5, false); + fontRendererObj.drawString(StatCollector.translateToLocal("ability.windup") + ": " + selectedAbility.getWindUpTicks() + "t", x, y, 0xB5B5B5, false); y += 12; UserType ut = selectedAbility.getAllowedBy(); - fontRendererObj.drawString("Allowed: " + ut.name(), x, y, 0xB5B5B5, false); + fontRendererObj.drawString(StatCollector.translateToLocal("ability.allowed") + ": " + ut.name(), x, y, 0xB5B5B5, false); if (selectedAbility.hasDamage()) { y += 12; - fontRendererObj.drawString("Damage: " + selectedAbility.getDisplayDamage(), x, y, 0xFF5555, false); + fontRendererObj.drawString(StatCollector.translateToLocal("ability.damage") + ": " + selectedAbility.getDisplayDamage(), x, y, 0xFF5555, false); } if (currentIsBuiltIn) { y += 12; - fontRendererObj.drawString("(Built-in)", x, y, 0x55FF55, false); + fontRendererObj.drawString(StatCollector.translateToLocal("gui.builtin.tag"), x, y, 0x55FF55, false); } } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java index 5cc629a4a..e62c2dc81 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java @@ -19,6 +19,7 @@ import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.StatCollector; import noppes.npcs.client.NoppesUtil; import noppes.npcs.client.gui.util.GuiCustomScroll; import noppes.npcs.client.gui.util.GuiDirectoryCategorized; @@ -263,10 +264,13 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized { @Override protected void initRightPanel(int startY) { - // Calculate layout: playback row + edit/copy + remove rows + // Calculate layout: playback rows + edit/copy + remove rows int bottomRows = 2; // edit+copy, remove boolean hasPlayback = animation != null && !animation.frames.isEmpty() && hasSelectedItem(); - if (hasPlayback) bottomRows++; // playback row + if (hasPlayback) { + bottomRows++; // playback controls row + if (playingAnimation) bottomRows++; // frame info row + } int bottomH = bottomRows * (btnH + gap) + 14; previewX = rightX; @@ -283,15 +287,17 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized { if (!playingAnimation || (data.animation != null && data.animation.paused)) { String statusKey = (data.animation != null && data.animation.paused) ? "animation.paused" : "animation.stopped"; - addLabel(new GuiNpcLabel(90, statusKey, btnX, playY + 5)); + addLabel(new GuiNpcLabel(90, statusKey, btnX, playY + 5, 0xFFFFFF)); addButton(new GuiTexturedButton(91, "", btnX + 65, playY, 11, 20, animTexture, 18, 71)); } else { - addLabel(new GuiNpcLabel(90, "animation.playing", btnX, playY + 5)); - addLabel(new GuiNpcLabel(94, "", btnX + 60, playY + 5)); + addLabel(new GuiNpcLabel(90, "animation.playing", btnX, playY + 5, 0xFFFFFF)); addButton(new GuiTexturedButton(92, "", btnX + 65, playY, 14, 20, animTexture, 0, 71)); } if (playingAnimation) { addButton(new GuiTexturedButton(93, "", btnX + 85, playY, 14, 20, animTexture, 33, 71)); + // Frame info on its own line below playback controls + int frameInfoY = playY + btnH + gap; + addLabel(new GuiNpcLabel(94, "", btnX, frameInfoY + 5, 0xFFFFFF)); } } @@ -498,15 +504,15 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized { fontRendererObj.drawString(animation.name, x, y, 0xFFFFFF, true); y += 14; - fontRendererObj.drawString("Frames: " + animation.frames.size(), x, y, 0xB5B5B5, false); + fontRendererObj.drawString(StatCollector.translateToLocal("animation.frames") + ": " + animation.frames.size(), x, y, 0xB5B5B5, false); y += 12; - fontRendererObj.drawString("Speed: " + animation.speed, x, y, 0xB5B5B5, false); + fontRendererObj.drawString(StatCollector.translateToLocal("stats.speed") + ": " + animation.speed, x, y, 0xB5B5B5, false); y += 12; - String loopStr = animation.loop == 0 ? "None" : animation.loop == 1 ? "Loop" : "Mirror"; - fontRendererObj.drawString("Loop: " + loopStr, x, y, 0xB5B5B5, false); + String loopStr = animation.loop == 0 ? StatCollector.translateToLocal("gui.none") : animation.loop == 1 ? StatCollector.translateToLocal("animation.loop") : StatCollector.translateToLocal("animation.mirror"); + fontRendererObj.drawString(StatCollector.translateToLocal("animation.loop") + ": " + loopStr, x, y, 0xB5B5B5, false); if (currentIsBuiltIn) { y += 12; - fontRendererObj.drawString("(Built-in)", x, y, 0x55FF55, false); + fontRendererObj.drawString(StatCollector.translateToLocal("gui.builtin.tag"), x, y, 0x55FF55, false); } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java index ef1ad314d..c6af6597d 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java @@ -15,7 +15,6 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector; import noppes.npcs.client.ClientCacheHandler; -import noppes.npcs.client.CustomNpcResourceListener; import noppes.npcs.client.gui.SubGuiEffectGeneral; import noppes.npcs.client.gui.util.GuiDirectoryCategorized; import noppes.npcs.client.gui.util.SubGuiInterface; @@ -180,7 +179,7 @@ public class GuiEffectDirectory extends GuiDirectoryCategorized { if (effect == null || effect.id == -1) return; String drawString = effect.getMenuName(); - fontRendererObj.drawString(drawString, x, y, CustomNpcResourceListener.DefaultTextColor, true); + fontRendererObj.drawString(drawString, x, y, 0xFFFFFF, true); y += 14; fontRendererObj.drawString(StatCollector.translateToLocal("gui.name") + ": " + effect.name, x, y, 0xFFFFFF, false); diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java index 86199b818..2b9744758 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java @@ -17,7 +17,6 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector; import noppes.npcs.client.ClientCacheHandler; -import noppes.npcs.client.CustomNpcResourceListener; import net.minecraft.client.gui.GuiButton; import noppes.npcs.client.gui.item.SubGuiLinkedItem; import noppes.npcs.client.gui.util.GuiDirectoryCategorized; @@ -215,7 +214,7 @@ public class GuiLinkedItemDirectory extends GuiDirectoryCategorized { protected void drawItemDetails(int x, int y, int w) { if (linkedItem == null || linkedItem.id < 0) return; - fontRendererObj.drawString(linkedItem.name, x, y, CustomNpcResourceListener.DefaultTextColor, true); + fontRendererObj.drawString(linkedItem.name, x, y, 0xFFFFFF, true); y += 14; int labelColor = 0xffae0d; diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java b/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java index 5c49d3dcc..38c508756 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java @@ -184,7 +184,7 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory x += topBtnW + 2; // MOVE - String moveLabel = movePhase > 0 ? "Moving" : "Move"; + String moveLabel = movePhase > 0 ? "gui.moving" : "gui.move"; GuiNpcButton moveBtn = new GuiNpcButton(54, x, topBtnY, topBtnW, btnH, moveLabel); moveBtn.enabled = selectedCatId >= 0 && !leftCollapsed && hasCategories(); addButton(moveBtn); @@ -192,8 +192,9 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory // Confirm (only during move phase 1) if (movePhase == 1) { - GuiNpcButton confirmBtn = new GuiNpcButton(55, x, topBtnY, topBtnW, btnH, "Confirm"); + GuiNpcButton confirmBtn = new GuiNpcButton(55, x, topBtnY, topBtnW, btnH, "gui.confirm"); confirmBtn.enabled = !moveSelection.isEmpty(); + confirmBtn.setTextColor(0x55FFFF); addButton(confirmBtn); x += topBtnW + 2; } @@ -230,6 +231,13 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory catScroll.setSelected(prevCatName); catScroll.scrollY = Math.max(0, Math.min(savedCatScrollY, catScroll.maxScrollY)); + // Phase 1: categories are locked (RED labels, not clickable) + if (movePhase == 1) { + catScroll.setSelectable(false); + for (String name : navNames) { + catScroll.colors.put(name, 0xFF5555); + } + } // Phase 2: ensure scroll is clickable for destination selection if (movePhase == 2) { catScroll.setSelectable(true); @@ -391,19 +399,19 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory // Draw ID (skip if -1, e.g. abilities use names not IDs) if (hasSelectedItem() && getSelectedItemId() >= 0) { - String idStr = "ID: " + getSelectedItemId(); - int idY = contentY + contentH - btnH - 14; + String idStr = StatCollector.translateToLocal("gui.id") + ": " + getSelectedItemId(); + int idY = contentY + contentH - btnH * 2 - gap - 12; fontRendererObj.drawString(idStr, previewX + 4, idY, 0xFFFFFF, true); } } // Move phase overlay text if (movePhase == 1) { - String text = "Select Items to Move (max " + MAX_MOVE_ITEMS + ")"; + String text = String.format(StatCollector.translateToLocal("gui.move.selectItems"), MAX_MOVE_ITEMS); int textW = fontRendererObj.getStringWidth(text); fontRendererObj.drawStringWithShadow(text, width / 2 - textW / 2, height - pad - 10, 0x55FF55); } else if (movePhase == 2) { - String text = "Select Destination Category"; + String text = StatCollector.translateToLocal("gui.move.selectCategory"); int textW = fontRendererObj.getStringWidth(text); fontRendererObj.drawStringWithShadow(text, width / 2 - textW / 2, height - pad - 10, 0xFF5555); } @@ -592,6 +600,9 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) { // Category scroll if (guiCustomScroll.id == 2) { + // Block category clicks during move phase 1 (selecting items) + if (movePhase == 1) return; + String selected = catScroll.getSelected(); if (selected == null) return; diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java b/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java index 2c0d51a18..489069610 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java @@ -5,6 +5,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector; @@ -166,8 +167,13 @@ public class GuiNpcButton extends GuiButton { } } } - // First, draw the button normally using the superclass method. - super.drawButton(mc, mouseX, mouseY); + // Wide buttons (>396px) break the vanilla 200px button texture. + // Draw them with a three-patch approach instead. + if (this.width > 396) { + drawWideButton(mc, mouseX, mouseY); + } else { + super.drawButton(mc, mouseX, mouseY); + } // Restore original display string this.displayString = original; // Then, if an icon texture is set, draw it. @@ -175,17 +181,58 @@ public class GuiNpcButton extends GuiButton { mc.getTextureManager().bindTexture(iconTexture); GL11.glPushMatrix(); RenderHelper.enableGUIStandardItemLighting(); - // Calculate the icon position so that it's centered in the button. - // (Assumes the button's (xPosition,yPosition) is its top left.) int iconX = this.xPosition + (this.width - iconWidth) / 2; int iconY = this.yPosition + (this.height - iconHeight) / 2; - // Draw the texture; we assume texture coordinates start at (0,0) with the desired size. this.drawTexturedModalRect(iconX, iconY, iconPosX, iconPosY, iconWidth, iconHeight); RenderHelper.disableStandardItemLighting(); GL11.glPopMatrix(); } } + /** + * Draws a button wider than the vanilla 200px texture allows. + * Uses three-patch rendering: left cap, tiled center, right cap. + */ + private void drawWideButton(Minecraft mc, int mouseX, int mouseY) { + FontRenderer fontrenderer = mc.fontRenderer; + mc.getTextureManager().bindTexture(buttonTextures); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + this.field_146123_n = mouseX >= this.xPosition && mouseY >= this.yPosition + && mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height; + int k = this.getHoverState(this.field_146123_n); + GL11.glEnable(GL11.GL_BLEND); + OpenGlHelper.glBlendFunc(770, 771, 1, 0); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + int texY = 46 + k * 20; + int cap = 100; + + // Left cap (first 100px of texture) + this.drawTexturedModalRect(this.xPosition, this.yPosition, 0, texY, cap, this.height); + // Right cap (last 100px of texture) + this.drawTexturedModalRect(this.xPosition + this.width - cap, this.yPosition, 200 - cap, texY, cap, this.height); + // Tile the center column to fill the gap + int gapStart = this.xPosition + cap; + int gapEnd = this.xPosition + this.width - cap; + for (int x = gapStart; x < gapEnd; x += 2) { + int drawW = Math.min(2, gapEnd - x); + this.drawTexturedModalRect(x, this.yPosition, 99, texY, drawW, this.height); + } + + this.mouseDragged(mc, mouseX, mouseY); + + int l = 14737632; + if (packedFGColour != 0) { + l = packedFGColour; + } else if (!this.enabled) { + l = 10526880; + } else if (this.field_146123_n) { + l = 16777120; + } + this.drawCenteredString(fontrenderer, this.displayString, + this.xPosition + this.width / 2, this.yPosition + (this.height - 8) / 2, l); + } + public void drawHover(int i, int j, boolean hasSubGui) { if (hasSubGui || !visible || hoverableText.isEmpty()) return; diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index f375c56c2..d94408caa 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -2459,3 +2459,14 @@ global.auction.manage.confirm.claim=Left-click again to confirm claim global.auction.manage.error.noItem=Stage an item first global.auction.manage.error.invalidStart=Enter a valid starting price global.auction.manage.error.invalidBuyout=Buyout must be zero or >= start + +# Directory Fullscreen +gui.move=Move +gui.moving=Moving +gui.confirm=Confirm +gui.move.selectItems=Select Items to Move (max %d) Press Confirm when Ready +gui.move.selectCategory=Select Destination Category +animation.mirror=Mirror +gui.builtin.tag=(Built-in) +ability.damage=Damage +ability.allowed=Allowed From 07ebe90437c34fbe3e1e5447b5caf5135329fa10 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 22:56:09 -0400 Subject: [PATCH 20/29] Fix Barrier Collision Logic --- src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java index 9683c2894..4ccb01269 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java @@ -741,6 +741,13 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility { double px = ent.posX + dirX * probe; double py = ent.posY + dirY * probe; double pz = ent.posZ + dirZ * probe; + // Never probe below the entity's feet — a downward probe always detects the + // block the entity is already standing/hovering above (false positive). + // MC's own collision system prevents entities from falling through floors, + // so the downward check is redundant. Upward probes (ceiling detection) still work. + if (py < ent.posY) { + py = ent.posY; + } AxisAlignedBB box = AxisAlignedBB.getBoundingBox( px - halfW, py, pz - halfW, px + halfW, py + ent.height, pz + halfW From 248b56ec1f79831e992db2cd3698b1d0674c72c9 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 22:56:22 -0400 Subject: [PATCH 21/29] Move Confirmation --- .../gui/global/GuiAbilityDirectory.java | 2 + .../gui/global/GuiAnimationDirectory.java | 5 ++ .../gui/util/GuiDirectoryCategorized.java | 30 +++++++++--- .../gui/util/SubGuiCategoryMoveConfirm.java | 48 +++++++++++++++++++ .../assets/customnpcs/lang/en_US.lang | 2 + 5 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 src/main/java/noppes/npcs/client/gui/util/SubGuiCategoryMoveConfirm.java diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java index 7599bb18e..d9c82f45a 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java @@ -847,9 +847,11 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized GL11.glTranslatef(0.0F, entity.yOffset, 1F); RenderManager.instance.playerViewY = 180F; + ClientEventHandler.renderingEntityInGUI = true; try { RenderManager.instance.renderEntityWithPosYaw(entity, 0.0, 0.0, 0.0, 0.0F, 1.0F); } catch (Exception ignored) {} + ClientEventHandler.renderingEntityInGUI = false; entity.prevRenderYawOffset = entity.renderYawOffset = f2; entity.prevRotationYaw = entity.rotationYaw = f3; diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java index e62c2dc81..6a5aeb478 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java @@ -35,6 +35,7 @@ import noppes.npcs.controllers.data.AnimationData; import noppes.npcs.controllers.data.Category; import noppes.npcs.controllers.data.Frame; import noppes.npcs.controllers.data.FramePart; +import noppes.npcs.client.ClientEventHandler; import noppes.npcs.entity.EntityCustomNpc; import noppes.npcs.entity.EntityNPCInterface; import org.lwjgl.opengl.GL11; @@ -478,10 +479,12 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized { GL11.glTranslatef(0.0F, entity.yOffset, 1F); RenderManager.instance.playerViewY = 180F; + ClientEventHandler.renderingEntityInGUI = true; try { RenderManager.instance.renderEntityWithPosYaw(entity, 0.0, 0.0, 0.0, 0.0F, 1.0F); } catch (Exception ignored) { } + ClientEventHandler.renderingEntityInGUI = false; entity.prevRenderYawOffset = entity.renderYawOffset = f2; entity.prevRotationYaw = entity.rotationYaw = f3; @@ -493,6 +496,8 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized { OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); GL11.glDisable(GL11.GL_TEXTURE_2D); OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + GL11.glDisable(GL11.GL_COLOR_MATERIAL); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); GL11.glPopMatrix(); } diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java b/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java index 38c508756..04717d8ed 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java @@ -63,6 +63,10 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory // ========== New Item Category ========== protected int pendingNewItemCatId = -1; + // ========== Pending Move Destination ========== + private int pendingMoveDestCatId = -1; + private String pendingMoveDestName = ""; + // ========== Category Data Loaded ========== private boolean itemDataLoaded = false; @@ -231,16 +235,16 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory catScroll.setSelected(prevCatName); catScroll.scrollY = Math.max(0, Math.min(savedCatScrollY, catScroll.maxScrollY)); - // Phase 1: categories are locked (RED labels, not clickable) + // Phase 1: categories locked (not clickable) if (movePhase == 1) { catScroll.setSelectable(false); - for (String name : navNames) { - catScroll.colors.put(name, 0xFF5555); - } } - // Phase 2: ensure scroll is clickable for destination selection + // Phase 2: color nav items orange (selectable) or red (current/source category) if (movePhase == 2) { catScroll.setSelectable(true); + for (String name : navNames) { + catScroll.colors.put(name, name.equals(prevCatName) ? 0xFF5555 : 0xFFAA00); + } } addScroll(catScroll); @@ -606,12 +610,14 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory String selected = catScroll.getSelected(); if (selected == null) return; - // Move phase 2: destination selected + // Move phase 2: destination selected — show confirmation if (movePhase == 2) { if (!selected.equals(prevCatName)) { Integer destCatId = catData.get(selected); if (destCatId != null) { - executeMoveItems(destCatId); + pendingMoveDestCatId = destCatId; + pendingMoveDestName = selected; + setSubGui(new SubGuiCategoryMoveConfirm(moveSelection.size(), selected)); } } return; @@ -772,6 +778,16 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory } } } + if (subgui instanceof SubGuiCategoryMoveConfirm) { + SubGuiCategoryMoveConfirm confirm = (SubGuiCategoryMoveConfirm) subgui; + if (confirm.confirmed && !moveSelection.isEmpty() && pendingMoveDestCatId >= 0) { + executeMoveItems(pendingMoveDestCatId); + } else { + // Cancelled — stay in phase 2 + } + pendingMoveDestCatId = -1; + pendingMoveDestName = ""; + } onSubGuiClosed(subgui); } diff --git a/src/main/java/noppes/npcs/client/gui/util/SubGuiCategoryMoveConfirm.java b/src/main/java/noppes/npcs/client/gui/util/SubGuiCategoryMoveConfirm.java new file mode 100644 index 000000000..d4b7e79ea --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/util/SubGuiCategoryMoveConfirm.java @@ -0,0 +1,48 @@ +package noppes.npcs.client.gui.util; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.util.StatCollector; + +public class SubGuiCategoryMoveConfirm extends SubGuiInterface { + public boolean confirmed = false; + + private final int count; + public final String destCategoryName; + + public SubGuiCategoryMoveConfirm(int count, String destCategoryName) { + this.count = count; + this.destCategoryName = destCategoryName; + xSize = 220; + ySize = 90; + closeOnEsc = true; + } + + @Override + public void initGui() { + super.initGui(); + + String line1 = String.format(StatCollector.translateToLocal("gui.move.confirmLine"), count); + addLabel(new GuiNpcLabel(0, line1, guiLeft + 10, guiTop + 10, 0xFFFFFF)); + + String dest = StatCollector.translateToLocal("gui.category") + ": \"" + destCategoryName + "\""; + addLabel(new GuiNpcLabel(1, " " + dest, guiLeft + 10, guiTop + 25, 0xFFFFFF)); + + addButton(new GuiNpcButton(0, guiLeft + 10, guiTop + 55, 95, 20, "gui.yes")); + addButton(new GuiNpcButton(1, guiLeft + 115, guiTop + 55, 95, 20, "gui.cancel")); + } + + @Override + protected void actionPerformed(GuiButton guibutton) { + if (guibutton.id == 0) { + confirmed = true; + close(); + } + if (guibutton.id == 1) { + close(); + } + } + + @Override + public void save() { + } +} diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index d94408caa..13fa4fc07 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -2470,3 +2470,5 @@ animation.mirror=Mirror gui.builtin.tag=(Built-in) ability.damage=Damage ability.allowed=Allowed +gui.move.confirmLine=Move %d item(s) to: +gui.category=Category From b6b6eb554896d075a1ad8fe93ff5bdc4e9424083 Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 23:03:53 -0400 Subject: [PATCH 22/29] Fix Some Selection Logic and Such in the Fullscreen Move Systems --- .../gui/GuiNpcMobSpawnerFullscreen.java | 2 +- .../advanced/SubGuiAbilityVariantSelect.java | 5 +++- .../npcs/client/gui/util/GuiCustomScroll.java | 23 +++++++++++-------- .../gui/util/GuiDirectoryCategorized.java | 2 ++ 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerFullscreen.java b/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerFullscreen.java index 80e0a971f..eabc33fee 100644 --- a/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerFullscreen.java +++ b/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerFullscreen.java @@ -199,7 +199,7 @@ public class GuiNpcMobSpawnerFullscreen extends GuiDirectory implements IGuiData navScroll.setSelected(getNavSelection()); navScroll.scrollY = Math.min(savedNavScroll, Math.max(0, navScroll.maxScrollY)); - // Phase 2: color nav items orange (selectable) or red (current location, not selectable) + // Phase 2: color nav items orange (selectable) or red (current location) if (movePhase == 2) { String currentNav = getNavSelection(); for (String name : navNames) { diff --git a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityVariantSelect.java b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityVariantSelect.java index 1c65d0dc6..be7ba5645 100644 --- a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityVariantSelect.java +++ b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityVariantSelect.java @@ -88,12 +88,15 @@ public class SubGuiAbilityVariantSelect extends SubGuiInterface implements ICust scroll.setSize(190, 155); // Mark blank separators and group headers as non-interactive colored entries + scroll.nonInteractive.add(""); scroll.colors.put("", 0x000000); if (lastGroup != null) { for (int i = 0; i < variants.size(); i++) { String group = variants.get(i).getGroup(); - if (group != null) + if (group != null) { + scroll.nonInteractive.add(group); scroll.colors.put(group, 0xFFFF00); // Yellow + } } } diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiCustomScroll.java b/src/main/java/noppes/npcs/client/gui/util/GuiCustomScroll.java index f9b3df568..fc8bf30eb 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiCustomScroll.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiCustomScroll.java @@ -19,6 +19,7 @@ public class GuiCustomScroll extends GuiScreen { public static final ResourceLocation resource = new ResourceLocation("customnpcs", "textures/gui/misc.png"); public List list; public final HashMap colors = new HashMap<>(); + public final HashSet nonInteractive = new HashSet<>(); public int id; public int guiLeft = 0; @@ -217,21 +218,25 @@ public class GuiCustomScroll extends GuiScreen { } else text = displayString; - // Entries with a custom color are non-interactive (headers/separators) + // Non-interactive entries (headers/separators) — colored, no hover/selection Integer customColor = colors.get(rawEntry); - if (customColor != null) { + if (nonInteractive.contains(rawEntry)) { if (!text.isEmpty()) - fontRendererObj.drawString(text, j, k, customColor); + fontRendererObj.drawString(text, j, k, customColor != null ? customColor : 0xffffff); } else if ((multipleSelection && selectedList.contains(text)) || (!multipleSelection && selected == i)) { drawVerticalLine(j - 2, k - 4, k + 10, 0xffffffff); drawVerticalLine(j + xSize - 18 + xOffset, k - 4, k + 10, 0xffffffff); drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k - 3, 0xffffffff); drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k + 10, 0xffffffff); - fontRendererObj.drawString(text, j, k, 0xffffff); + int selColor = customColor != null ? customColor : 0xffffff; + fontRendererObj.drawString(text, j, k, selColor); } else if (i == hover) { - fontRendererObj.drawString(text, j, k, 0x00ff00); - } else - fontRendererObj.drawString(text, j, k, 0xffffff); + int hoverColor = customColor != null ? customColor : 0x00ff00; + fontRendererObj.drawString(text, j, k, hoverColor); + } else { + int defaultColor = customColor != null ? customColor : 0xffffff; + fontRendererObj.drawString(text, j, k, defaultColor); + } } } @@ -252,8 +257,8 @@ public class GuiCustomScroll extends GuiScreen { continue; } - // Skip non-interactive entries (colored headers/separators) - if (colors.containsKey(list.get(j1))) { + // Skip non-interactive entries (headers/separators) + if (nonInteractive.contains(list.get(j1))) { continue; } diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java b/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java index 04717d8ed..e8be1249a 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java @@ -245,6 +245,8 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory for (String name : navNames) { catScroll.colors.put(name, name.equals(prevCatName) ? 0xFF5555 : 0xFFAA00); } + } else if (movePhase == 0) { + catScroll.setSelectable(true); } addScroll(catScroll); From 5fda8a2de9297ab26c8cf58576e2457372f52c4b Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 23:40:56 -0400 Subject: [PATCH 23/29] Fix Rendering / Selection --- .../npcs/client/gui/global/GuiAbilityDirectory.java | 8 +++----- .../npcs/client/gui/global/GuiAnimationDirectory.java | 4 +--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java index d9c82f45a..0fc52f08b 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java @@ -741,6 +741,8 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized * Matches GuiAbilityInterface's rendering pipeline. */ private void drawAbilityPreview(float partialTicks) { + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + // Set NPC facing direction npc.prevRenderYawOffset = npc.renderYawOffset = NPC_FACING_YAW; npc.prevRotationYaw = npc.rotationYaw = NPC_FACING_YAW; @@ -806,8 +808,6 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); GL11.glDisable(GL11.GL_TEXTURE_2D); OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); - GL11.glDisable(GL11.GL_COLOR_MATERIAL); - GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); GL11.glDisable(GL11.GL_SCISSOR_TEST); GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); @@ -857,16 +857,14 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized entity.prevRotationYaw = entity.rotationYaw = f3; entity.rotationPitch = f4; entity.prevRotationYawHead = entity.rotationYawHead = f7; + GL11.glPopMatrix(); RenderHelper.disableStandardItemLighting(); GL11.glDisable(GL12.GL_RESCALE_NORMAL); OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); GL11.glDisable(GL11.GL_TEXTURE_2D); OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); - GL11.glDisable(GL11.GL_COLOR_MATERIAL); - GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); - GL11.glPopMatrix(); } private void renderPreviewEntities(float partialTicks) { diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java index 6a5aeb478..df6cd58b2 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAnimationDirectory.java @@ -490,16 +490,14 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized { entity.prevRotationYaw = entity.rotationYaw = f3; entity.rotationPitch = f4; entity.prevRotationYawHead = entity.rotationYawHead = f7; + GL11.glPopMatrix(); RenderHelper.disableStandardItemLighting(); GL11.glDisable(GL12.GL_RESCALE_NORMAL); OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); GL11.glDisable(GL11.GL_TEXTURE_2D); OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); - GL11.glDisable(GL11.GL_COLOR_MATERIAL); - GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); - GL11.glPopMatrix(); } @Override From 3d1a0b5418fbeb39d23e05dbef0f468dc741895f Mon Sep 17 00:00:00 2001 From: Kam Date: Mon, 9 Mar 2026 23:56:12 -0400 Subject: [PATCH 24/29] 1.11 Pre Release 3 --- build.gradle | 2 +- src/main/java/noppes/npcs/CustomNpcs.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 6826ce5d5..9c56b2e2e 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'idea' } -version = "1.11-prerelease2" +version = "1.11-prerelease3" group= "kamkeel.customnpc-plus" archivesBaseName = "CustomNPC-Plus" diff --git a/src/main/java/noppes/npcs/CustomNpcs.java b/src/main/java/noppes/npcs/CustomNpcs.java index 67a55f58d..cfd3ad7bb 100644 --- a/src/main/java/noppes/npcs/CustomNpcs.java +++ b/src/main/java/noppes/npcs/CustomNpcs.java @@ -124,7 +124,7 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; -@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-prerelease2") +@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-prerelease3") public class CustomNpcs { @SidedProxy(clientSide = "noppes.npcs.client.ClientProxy", serverSide = "noppes.npcs.CommonProxy") From b05a0281378cf5d5d6e77790877e0a2283f2b142 Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 10 Mar 2026 01:37:46 -0400 Subject: [PATCH 25/29] Fix Close on ESC Outline and Tag Select --- src/main/java/noppes/npcs/client/gui/SubGuiTagSelect.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiTagSelect.java b/src/main/java/noppes/npcs/client/gui/SubGuiTagSelect.java index 2e424038d..ee55e1b63 100644 --- a/src/main/java/noppes/npcs/client/gui/SubGuiTagSelect.java +++ b/src/main/java/noppes/npcs/client/gui/SubGuiTagSelect.java @@ -38,6 +38,7 @@ public class SubGuiTagSelect extends SubGuiInterface implements IScrollData, ICu public SubGuiTagSelect(HashSet tagUUIDs) { this.tagUUIDs = tagUUIDs; setBackground("menubg.png"); + closeOnEsc = true; xSize = 430; ySize = 220; @@ -142,6 +143,10 @@ public class SubGuiTagSelect extends SubGuiInterface implements IScrollData, ICu } public void keyTyped(char c, int i) { + if (i == 1) { + close(); + return; + } super.keyTyped(c, i); if (getTextField(4) != null) { if (search.equals(getTextField(4).getText())) From ba9ae62e514e7b96a30783582a081deb1ad1e75f Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 10 Mar 2026 02:45:52 -0400 Subject: [PATCH 26/29] Cloning for Scripts --- .../controllers/data/ability/AbilityAction.java | 11 +++++++++-- .../data/ability/data/ChainedAbility.java | 1 + .../ability/data/entry/ChainedAbilityEntry.java | 6 +++++- .../npcs/client/gui/global/GuiAbilityDirectory.java | 13 ++++++++----- .../npcs/client/gui/global/GuiEffectDirectory.java | 2 +- .../client/gui/global/GuiLinkedItemDirectory.java | 2 +- .../npcs/controllers/CustomEffectController.java | 6 ++++++ .../npcs/controllers/LinkedItemController.java | 6 ++++++ 8 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityAction.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityAction.java index 5c156a056..e9909bcf9 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityAction.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityAction.java @@ -5,6 +5,8 @@ import kamkeel.npcs.controllers.data.ability.data.ChainedAbility; import kamkeel.npcs.controllers.data.ability.data.IAbilityAction; import net.minecraft.nbt.NBTTagCompound; +import java.util.UUID; + /** * Unified slot for storing any combat action: inline ability, ability reference, or chain reference. * Support for all {@link IAbilityAction} types. @@ -218,7 +220,9 @@ public class AbilityAction { Ability resolved = getAbility(); if (resolved == null) return false; - inlineAbility = AbilityController.Instance.fromNBT(resolved.writeNBT(true)); + NBTTagCompound nbt = resolved.writeNBT(true); + nbt.setString("id", UUID.randomUUID().toString()); + inlineAbility = AbilityController.Instance.fromNBT(nbt); slotType = SlotType.INLINE_ABILITY; referenceId = null; cachedAction = null; @@ -230,7 +234,10 @@ public class AbilityAction { ChainedAbility resolved = getChainedAbility(); if (resolved == null) return false; - inlineChain = resolved.deepCopy(); + inlineChain = new ChainedAbility(); + NBTTagCompound chainNbt = resolved.writeNBT(true); + chainNbt.setString("Id", UUID.randomUUID().toString()); + inlineChain.readNBT(chainNbt); slotType = SlotType.INLINE_CHAIN; referenceId = null; cachedAction = null; diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/ChainedAbility.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/ChainedAbility.java index bdf95508f..fea8005d3 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/ChainedAbility.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/ChainedAbility.java @@ -15,6 +15,7 @@ import kamkeel.npcs.util.FileNameHelper; import java.util.ArrayList; import java.util.List; +import java.util.UUID; /** * A chained ability is an ordered sequence of ability references that execute diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/entry/ChainedAbilityEntry.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/entry/ChainedAbilityEntry.java index 0e7b4bf97..ec7fade19 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/entry/ChainedAbilityEntry.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/entry/ChainedAbilityEntry.java @@ -5,6 +5,8 @@ import kamkeel.npcs.controllers.data.ability.Ability; import kamkeel.npcs.controllers.data.ability.data.ChainedAbility; import net.minecraft.nbt.NBTTagCompound; +import java.util.UUID; + /** * A single entry in a {@link ChainedAbility}, representing one ability * to execute in sequence along with its delay configuration. @@ -155,7 +157,9 @@ public class ChainedAbilityEntry { Ability resolved = ctrl.resolveAbility(abilityReference); if (resolved == null) return false; - inlineAbility = ctrl.fromNBT(resolved.writeNBT(true)); + NBTTagCompound nbt = resolved.writeNBT(true); + nbt.setString("id", UUID.randomUUID().toString()); + inlineAbility = ctrl.fromNBT(nbt); entryType = EntryType.INLINE; abilityReference = ""; return true; diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java index 0fc52f08b..b16bccd81 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java @@ -272,18 +272,21 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized if (currentIsBuiltIn) return; if (isChainedMode() && selectedChain != null) { ChainedAbility clone = new ChainedAbility(); - clone.readNBT(selectedChain.writeNBT(false)); + NBTTagCompound chainNbt = selectedChain.writeNBT(true); + chainNbt.setString("Id", UUID.randomUUID().toString()); + clone.readNBT(chainNbt); String cloneName = selectedChain.getName() + "_copy"; clone.setName(cloneName); - PacketClient.sendClient(new ChainedAbilitySavePacket(clone.writeNBT(false))); + PacketClient.sendClient(new ChainedAbilitySavePacket(clone.writeNBT(true))); if (selectedCatId >= 0) requestItemsInCategory(selectedCatId); } else if (selectedAbility != null) { - Ability clone = AbilityController.Instance.fromNBT(selectedAbility.writeNBT(false)); + NBTTagCompound abilityNbt = selectedAbility.writeNBT(true); + abilityNbt.setString("id", UUID.randomUUID().toString()); + Ability clone = AbilityController.Instance.fromNBT(abilityNbt); if (clone != null) { - clone.setId(UUID.randomUUID().toString()); String cloneName = selectedAbility.getName() + "_copy"; clone.setName(cloneName); - PacketClient.sendClient(new CustomAbilitySavePacket(clone.writeNBT(false))); + PacketClient.sendClient(new CustomAbilitySavePacket(clone.writeNBT(true))); if (selectedCatId >= 0 && isCustomMode()) requestItemsInCategory(selectedCatId); } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java index c6af6597d..dedce0052 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java @@ -91,7 +91,7 @@ public class GuiEffectDirectory extends GuiDirectoryCategorized { if (effect != null && effect.id >= 0) { CustomEffect clone = effect.cloneEffect(); while (itemData.containsKey(clone.name)) clone.name += "_"; - PacketClient.sendClient(new EffectSavePacket(clone.writeToNBT(false), "")); + PacketClient.sendClient(new EffectSavePacket(clone.writeToNBT(true), "")); } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java index 2b9744758..92715a8fc 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java @@ -93,7 +93,7 @@ public class GuiLinkedItemDirectory extends GuiDirectoryCategorized { LinkedItem clone = linkedItem.clone(); while (itemData.containsKey(clone.name)) clone.name += "_"; clone.id = -1; - PacketClient.sendClient(new LinkedItemSavePacket(clone.writeToNBT(false), "")); + PacketClient.sendClient(new LinkedItemSavePacket(clone.writeToNBT(true), "")); } } diff --git a/src/main/java/noppes/npcs/controllers/CustomEffectController.java b/src/main/java/noppes/npcs/controllers/CustomEffectController.java index 528a3397d..c5f083788 100644 --- a/src/main/java/noppes/npcs/controllers/CustomEffectController.java +++ b/src/main/java/noppes/npcs/controllers/CustomEffectController.java @@ -673,7 +673,13 @@ public class CustomEffectController implements ICustomEffectHandler { @Override public ICustomEffect saveEffect(ICustomEffect customEffect) { if (customEffect.getID() < 0) { + int oldId = customEffect.getID(); customEffect.setID(getUnusedId()); + // Move script handler from old ID to new ID (e.g. during clone) + EffectScript handler = customEffectScriptHandlers.remove(oldId); + if (handler != null) { + customEffectScriptHandlers.put(customEffect.getID(), handler); + } while (has(customEffect.getName())) customEffect.setName(customEffect.getName() + "_"); } diff --git a/src/main/java/noppes/npcs/controllers/LinkedItemController.java b/src/main/java/noppes/npcs/controllers/LinkedItemController.java index 54f676fc8..1820a21ac 100644 --- a/src/main/java/noppes/npcs/controllers/LinkedItemController.java +++ b/src/main/java/noppes/npcs/controllers/LinkedItemController.java @@ -209,7 +209,13 @@ public class LinkedItemController implements ILinkedItemHandler { public ILinkedItem saveLinkedItem(ILinkedItem linkedItem) { if (linkedItem.getId() < 0) { + int oldId = linkedItem.getId(); linkedItem.setId(getUnusedId()); + // Move script handler from old ID to new ID (e.g. during clone) + LinkedItemScript handler = linkedItemsScripts.remove(oldId); + if (handler != null) { + linkedItemsScripts.put(linkedItem.getId(), handler); + } while (hasName(linkedItem.getName())) linkedItem.setName(linkedItem.getName() + "_"); } From 18d788974a1e328c4b5c2749ace6fa1d661d5642 Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 10 Mar 2026 03:21:47 -0400 Subject: [PATCH 27/29] Small Fix --- .../npcs/client/gui/global/GuiEffectDirectory.java | 9 ++++++--- .../npcs/client/gui/global/GuiLinkedItemDirectory.java | 10 ++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java index dedce0052..5fa69021b 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java @@ -89,9 +89,12 @@ public class GuiEffectDirectory extends GuiDirectoryCategorized { @Override protected void onCloneItem() { if (effect != null && effect.id >= 0) { - CustomEffect clone = effect.cloneEffect(); - while (itemData.containsKey(clone.name)) clone.name += "_"; - PacketClient.sendClient(new EffectSavePacket(clone.writeToNBT(true), "")); + NBTTagCompound cloneNbt = effect.writeToNBT(true); + cloneNbt.setInteger("ID", -1); + String name = cloneNbt.getString("name"); + while (itemData.containsKey(name)) name += "_"; + cloneNbt.setString("name", name); + PacketClient.sendClient(new EffectSavePacket(cloneNbt, "")); } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java index 92715a8fc..117cb60c6 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java @@ -90,10 +90,12 @@ public class GuiLinkedItemDirectory extends GuiDirectoryCategorized { @Override protected void onCloneItem() { if (linkedItem != null && linkedItem.id >= 0) { - LinkedItem clone = linkedItem.clone(); - while (itemData.containsKey(clone.name)) clone.name += "_"; - clone.id = -1; - PacketClient.sendClient(new LinkedItemSavePacket(clone.writeToNBT(true), "")); + NBTTagCompound cloneNbt = linkedItem.writeToNBT(true); + cloneNbt.setInteger("Id", -1); + String name = cloneNbt.getString("Name"); + while (itemData.containsKey(name)) name += "_"; + cloneNbt.setString("Name", name); + PacketClient.sendClient(new LinkedItemSavePacket(cloneNbt, "")); } } From 270cb26460d64f1b6a0d5378815a56151e117c64 Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 10 Mar 2026 03:47:07 -0400 Subject: [PATCH 28/29] Better Copy Handling --- .../npcs/controllers/AbilityController.java | 36 ++++++++++ .../data/ability/data/ChainedAbility.java | 1 - .../kamkeel/npcs/network/PacketHandler.java | 8 +++ .../npcs/network/enums/EnumRequestPacket.java | 5 ++ .../ability/ChainedAbilityClonePacket.java | 71 +++++++++++++++++++ .../ability/CustomAbilityClonePacket.java | 71 +++++++++++++++++++ .../request/effects/EffectClonePacket.java | 69 ++++++++++++++++++ .../request/linked/LinkedItemClonePacket.java | 69 ++++++++++++++++++ .../gui/global/GuiAbilityDirectory.java | 21 ++---- .../client/gui/global/GuiEffectDirectory.java | 8 +-- .../gui/global/GuiLinkedItemDirectory.java | 8 +-- .../gui/global/GuiNPCManageEffects.java | 8 +-- .../client/gui/global/GuiNPCManageLinked.java | 9 +-- .../controllers/CustomEffectController.java | 25 +++++-- .../controllers/LinkedItemController.java | 25 +++++-- 15 files changed, 382 insertions(+), 52 deletions(-) create mode 100644 src/main/java/kamkeel/npcs/network/packets/request/ability/ChainedAbilityClonePacket.java create mode 100644 src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityClonePacket.java create mode 100644 src/main/java/kamkeel/npcs/network/packets/request/effects/EffectClonePacket.java create mode 100644 src/main/java/kamkeel/npcs/network/packets/request/linked/LinkedItemClonePacket.java diff --git a/src/main/java/kamkeel/npcs/controllers/AbilityController.java b/src/main/java/kamkeel/npcs/controllers/AbilityController.java index 44445da04..003f58fca 100644 --- a/src/main/java/kamkeel/npcs/controllers/AbilityController.java +++ b/src/main/java/kamkeel/npcs/controllers/AbilityController.java @@ -401,6 +401,24 @@ public class AbilityController implements IAbilityHandler { } } + public Ability cloneCustomAbility(String originalName) { + Ability original = customAbilities.get(originalName); + if (original == null || original.isBuiltIn()) return null; + + NBTTagCompound nbt = original.writeNBT(true); + nbt.setString("id", UUID.randomUUID().toString()); + + Ability clone = fromNBT(nbt); + if (clone == null) return null; + + String name = clone.getName(); + while (customAbilities.containsKey(name)) name += "_"; + clone.setName(name); + + saveCustomAbility(clone); + return clone; + } + public boolean deleteCustomAbility(String name) { if (name == null || name.isEmpty()) return false; @@ -748,6 +766,24 @@ public class AbilityController implements IAbilityHandler { } } + public ChainedAbility cloneChainedAbility(String originalName) { + ChainedAbility original = chainedAbilities.get(originalName); + if (original == null) return null; + + NBTTagCompound nbt = original.writeNBT(true); + nbt.setString("Id", UUID.randomUUID().toString()); + + ChainedAbility clone = new ChainedAbility(); + clone.readNBT(nbt); + + String name = clone.getName(); + while (chainedAbilities.containsKey(name)) name += "_"; + clone.setName(name); + + saveChainedAbility(clone); + return clone; + } + public boolean deleteChainedAbility(String name) { if (name == null || name.isEmpty()) return false; diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/data/ChainedAbility.java b/src/main/java/kamkeel/npcs/controllers/data/ability/data/ChainedAbility.java index fea8005d3..bdf95508f 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/data/ChainedAbility.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/data/ChainedAbility.java @@ -15,7 +15,6 @@ import kamkeel.npcs.util.FileNameHelper; import java.util.ArrayList; import java.util.List; -import java.util.UUID; /** * A chained ability is an ordered sequence of ability references that execute diff --git a/src/main/java/kamkeel/npcs/network/PacketHandler.java b/src/main/java/kamkeel/npcs/network/PacketHandler.java index e9521b86e..c0616e89c 100644 --- a/src/main/java/kamkeel/npcs/network/PacketHandler.java +++ b/src/main/java/kamkeel/npcs/network/PacketHandler.java @@ -90,6 +90,7 @@ import kamkeel.npcs.network.packets.request.ability.AbilitiesNpcGetPacket; import kamkeel.npcs.network.packets.request.ability.AbilitiesNpcSavePacket; import kamkeel.npcs.network.packets.request.ability.BuiltInAbilityGetPacket; import kamkeel.npcs.network.packets.request.ability.ChainedAbilitiesGetPacket; +import kamkeel.npcs.network.packets.request.ability.ChainedAbilityClonePacket; import kamkeel.npcs.network.packets.request.ability.ChainedAbilityGetPacket; import kamkeel.npcs.network.packets.request.ability.ChainedAbilityRemovePacket; import kamkeel.npcs.network.packets.request.ability.ChainedAbilitySavePacket; @@ -100,6 +101,7 @@ import kamkeel.npcs.network.packets.request.category.CategoryListRequestPacket; import kamkeel.npcs.network.packets.request.category.CategoryMoveItemPacket; import kamkeel.npcs.network.packets.request.category.CategoryRemovePacket; import kamkeel.npcs.network.packets.request.category.CategorySavePacket; +import kamkeel.npcs.network.packets.request.ability.CustomAbilityClonePacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilityGetPacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilityRemovePacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilitySavePacket; @@ -136,6 +138,7 @@ import kamkeel.npcs.network.packets.request.dialog.DialogNpcSetPacket; import kamkeel.npcs.network.packets.request.dialog.DialogRemovePacket; import kamkeel.npcs.network.packets.request.dialog.DialogSavePacket; import kamkeel.npcs.network.packets.request.dialog.DialogsGetPacket; +import kamkeel.npcs.network.packets.request.effects.EffectClonePacket; import kamkeel.npcs.network.packets.request.effects.EffectGetPacket; import kamkeel.npcs.network.packets.request.effects.EffectRemovePacket; import kamkeel.npcs.network.packets.request.effects.EffectSavePacket; @@ -156,6 +159,7 @@ import kamkeel.npcs.network.packets.request.jobs.JobSpawnerRemovePacket; import kamkeel.npcs.network.packets.request.linked.LinkedGetAllPacket; import kamkeel.npcs.network.packets.request.linked.LinkedGetPacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemBuildPacket; +import kamkeel.npcs.network.packets.request.linked.LinkedItemClonePacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemRemovePacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemSavePacket; import kamkeel.npcs.network.packets.request.linked.LinkedNPCAddPacket; @@ -353,6 +357,7 @@ public class PacketHandler { REQUEST_PACKET.registerPacket(new LinkedItemSavePacket()); REQUEST_PACKET.registerPacket(new LinkedItemRemovePacket()); REQUEST_PACKET.registerPacket(new LinkedItemBuildPacket()); + REQUEST_PACKET.registerPacket(new LinkedItemClonePacket()); // Bank Packets @@ -509,6 +514,7 @@ public class PacketHandler { REQUEST_PACKET.registerPacket(new EffectsGetPacket()); REQUEST_PACKET.registerPacket(new EffectSavePacket()); REQUEST_PACKET.registerPacket(new EffectRemovePacket()); + REQUEST_PACKET.registerPacket(new EffectClonePacket()); // Magic Packets REQUEST_PACKET.registerPacket(new MagicCycleRemovePacket()); @@ -533,6 +539,8 @@ public class PacketHandler { REQUEST_PACKET.registerPacket(new ChainedAbilityGetPacket()); REQUEST_PACKET.registerPacket(new ChainedAbilitySavePacket()); REQUEST_PACKET.registerPacket(new ChainedAbilityRemovePacket()); + REQUEST_PACKET.registerPacket(new CustomAbilityClonePacket()); + REQUEST_PACKET.registerPacket(new ChainedAbilityClonePacket()); // Category Packets REQUEST_PACKET.registerPacket(new CategorySavePacket()); diff --git a/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java b/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java index 11b330762..c76e5adb9 100644 --- a/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java +++ b/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java @@ -223,4 +223,9 @@ public enum EnumRequestPacket { CategoryListRequest, CategoryItemsRequest, AbilityCategoryMoveItem, + + CustomAbilityClone, + ChainedAbilityClone, + LinkedItemClone, + EffectClone, } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/ability/ChainedAbilityClonePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/ability/ChainedAbilityClonePacket.java new file mode 100644 index 000000000..07ab7d3d1 --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/ability/ChainedAbilityClonePacket.java @@ -0,0 +1,71 @@ +package kamkeel.npcs.network.packets.request.ability; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +import kamkeel.npcs.controllers.AbilityController; +import kamkeel.npcs.controllers.data.ability.data.ChainedAbility; +import kamkeel.npcs.network.AbstractPacket; +import kamkeel.npcs.network.PacketChannel; +import kamkeel.npcs.network.PacketHandler; +import kamkeel.npcs.network.PacketUtil; +import kamkeel.npcs.network.enums.EnumItemPacketType; +import kamkeel.npcs.network.enums.EnumRequestPacket; +import kamkeel.npcs.network.packets.data.large.GuiDataPacket; +import kamkeel.npcs.util.ByteBufUtils; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import noppes.npcs.CustomNpcsPermissions; +import noppes.npcs.NoppesUtilServer; + +import java.io.IOException; + +public final class ChainedAbilityClonePacket extends AbstractPacket { + public static String packetName = "Request|ChainedAbilityClone"; + + private String name; + + public ChainedAbilityClonePacket() { + } + + public ChainedAbilityClonePacket(String name) { + this.name = name; + } + + @Override + public Enum getType() { + return EnumRequestPacket.ChainedAbilityClone; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.REQUEST_PACKET; + } + + @Override + public CustomNpcsPermissions.Permission getPermission() { + return CustomNpcsPermissions.GLOBAL_ABILITY; + } + + @SideOnly(Side.CLIENT) + @Override + public void sendData(ByteBuf out) throws IOException { + ByteBufUtils.writeString(out, name); + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + if (!(player instanceof EntityPlayerMP)) + return; + + if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player)) + return; + + String chainName = ByteBufUtils.readString(in); + ChainedAbility clone = AbilityController.Instance.cloneChainedAbility(chainName); + if (clone != null) { + NoppesUtilServer.sendChainedAbilitiesData((EntityPlayerMP) player); + GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeNBT(false)); + } + } +} diff --git a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityClonePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityClonePacket.java new file mode 100644 index 000000000..03045a25f --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityClonePacket.java @@ -0,0 +1,71 @@ +package kamkeel.npcs.network.packets.request.ability; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +import kamkeel.npcs.controllers.AbilityController; +import kamkeel.npcs.controllers.data.ability.Ability; +import kamkeel.npcs.network.AbstractPacket; +import kamkeel.npcs.network.PacketChannel; +import kamkeel.npcs.network.PacketHandler; +import kamkeel.npcs.network.PacketUtil; +import kamkeel.npcs.network.enums.EnumItemPacketType; +import kamkeel.npcs.network.enums.EnumRequestPacket; +import kamkeel.npcs.network.packets.data.large.GuiDataPacket; +import kamkeel.npcs.util.ByteBufUtils; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import noppes.npcs.CustomNpcsPermissions; +import noppes.npcs.NoppesUtilServer; + +import java.io.IOException; + +public final class CustomAbilityClonePacket extends AbstractPacket { + public static String packetName = "Request|CustomAbilityClone"; + + private String name; + + public CustomAbilityClonePacket() { + } + + public CustomAbilityClonePacket(String name) { + this.name = name; + } + + @Override + public Enum getType() { + return EnumRequestPacket.CustomAbilityClone; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.REQUEST_PACKET; + } + + @Override + public CustomNpcsPermissions.Permission getPermission() { + return CustomNpcsPermissions.GLOBAL_ABILITY; + } + + @SideOnly(Side.CLIENT) + @Override + public void sendData(ByteBuf out) throws IOException { + ByteBufUtils.writeString(out, name); + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + if (!(player instanceof EntityPlayerMP)) + return; + + if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player)) + return; + + String abilityName = ByteBufUtils.readString(in); + Ability clone = AbilityController.Instance.cloneCustomAbility(abilityName); + if (clone != null) { + NoppesUtilServer.sendCustomAbilitiesData((EntityPlayerMP) player); + GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeNBT(false)); + } + } +} diff --git a/src/main/java/kamkeel/npcs/network/packets/request/effects/EffectClonePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/effects/EffectClonePacket.java new file mode 100644 index 000000000..b946e0f0f --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/effects/EffectClonePacket.java @@ -0,0 +1,69 @@ +package kamkeel.npcs.network.packets.request.effects; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +import kamkeel.npcs.network.AbstractPacket; +import kamkeel.npcs.network.PacketChannel; +import kamkeel.npcs.network.PacketHandler; +import kamkeel.npcs.network.PacketUtil; +import kamkeel.npcs.network.enums.EnumItemPacketType; +import kamkeel.npcs.network.enums.EnumRequestPacket; +import kamkeel.npcs.network.packets.data.large.GuiDataPacket; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import noppes.npcs.CustomNpcsPermissions; +import noppes.npcs.NoppesUtilServer; +import noppes.npcs.controllers.CustomEffectController; +import noppes.npcs.controllers.data.CustomEffect; + +import java.io.IOException; + +public final class EffectClonePacket extends AbstractPacket { + public static String packetName = "Request|EffectClone"; + + private int id; + + public EffectClonePacket() { + } + + public EffectClonePacket(int id) { + this.id = id; + } + + @Override + public Enum getType() { + return EnumRequestPacket.EffectClone; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.REQUEST_PACKET; + } + + @Override + public CustomNpcsPermissions.Permission getPermission() { + return CustomNpcsPermissions.GLOBAL_EFFECT; + } + + @SideOnly(Side.CLIENT) + @Override + public void sendData(ByteBuf out) throws IOException { + out.writeInt(this.id); + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + if (!(player instanceof EntityPlayerMP)) + return; + if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player)) + return; + + int effectId = in.readInt(); + CustomEffect clone = CustomEffectController.getInstance().cloneEffect(effectId); + if (clone != null) { + NoppesUtilServer.sendCustomEffectDataAll((EntityPlayerMP) player); + GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeToNBT(false)); + } + } +} diff --git a/src/main/java/kamkeel/npcs/network/packets/request/linked/LinkedItemClonePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/linked/LinkedItemClonePacket.java new file mode 100644 index 000000000..5eda0f5d0 --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/linked/LinkedItemClonePacket.java @@ -0,0 +1,69 @@ +package kamkeel.npcs.network.packets.request.linked; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +import kamkeel.npcs.network.AbstractPacket; +import kamkeel.npcs.network.PacketChannel; +import kamkeel.npcs.network.PacketHandler; +import kamkeel.npcs.network.PacketUtil; +import kamkeel.npcs.network.enums.EnumItemPacketType; +import kamkeel.npcs.network.enums.EnumRequestPacket; +import kamkeel.npcs.network.packets.data.large.GuiDataPacket; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import noppes.npcs.CustomNpcsPermissions; +import noppes.npcs.NoppesUtilServer; +import noppes.npcs.controllers.LinkedItemController; +import noppes.npcs.controllers.data.LinkedItem; + +import java.io.IOException; + +public final class LinkedItemClonePacket extends AbstractPacket { + public static String packetName = "Request|LinkedItemClone"; + + private int id; + + public LinkedItemClonePacket() { + } + + public LinkedItemClonePacket(int id) { + this.id = id; + } + + @Override + public Enum getType() { + return EnumRequestPacket.LinkedItemClone; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.REQUEST_PACKET; + } + + @Override + public CustomNpcsPermissions.Permission getPermission() { + return CustomNpcsPermissions.GLOBAL_LINKED; + } + + @SideOnly(Side.CLIENT) + @Override + public void sendData(ByteBuf out) throws IOException { + out.writeInt(this.id); + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + if (!(player instanceof EntityPlayerMP)) + return; + if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player)) + return; + + int linkedId = in.readInt(); + LinkedItem clone = LinkedItemController.getInstance().cloneLinkedItem(linkedId); + if (clone != null) { + NoppesUtilServer.sendLinkedItemDataAll((EntityPlayerMP) player); + GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeToNBT(false)); + } + } +} diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java index b16bccd81..8985c80da 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java @@ -11,10 +11,12 @@ import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance; import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.packets.request.ability.BuiltInAbilityGetPacket; import kamkeel.npcs.network.packets.request.ability.ChainedAbilityGetPacket; +import kamkeel.npcs.network.packets.request.ability.ChainedAbilityClonePacket; import kamkeel.npcs.network.packets.request.ability.ChainedAbilityRemovePacket; import kamkeel.npcs.network.packets.request.ability.ChainedAbilitySavePacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilitiesGetPacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilityGetPacket; +import kamkeel.npcs.network.packets.request.ability.CustomAbilityClonePacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilityRemovePacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilitySavePacket; import kamkeel.npcs.network.packets.request.category.AbilityCategoryMovePacket; @@ -271,24 +273,11 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized protected void onCloneItem() { if (currentIsBuiltIn) return; if (isChainedMode() && selectedChain != null) { - ChainedAbility clone = new ChainedAbility(); - NBTTagCompound chainNbt = selectedChain.writeNBT(true); - chainNbt.setString("Id", UUID.randomUUID().toString()); - clone.readNBT(chainNbt); - String cloneName = selectedChain.getName() + "_copy"; - clone.setName(cloneName); - PacketClient.sendClient(new ChainedAbilitySavePacket(clone.writeNBT(true))); + PacketClient.sendClient(new ChainedAbilityClonePacket(selectedChain.getName())); if (selectedCatId >= 0) requestItemsInCategory(selectedCatId); } else if (selectedAbility != null) { - NBTTagCompound abilityNbt = selectedAbility.writeNBT(true); - abilityNbt.setString("id", UUID.randomUUID().toString()); - Ability clone = AbilityController.Instance.fromNBT(abilityNbt); - if (clone != null) { - String cloneName = selectedAbility.getName() + "_copy"; - clone.setName(cloneName); - PacketClient.sendClient(new CustomAbilitySavePacket(clone.writeNBT(true))); - if (selectedCatId >= 0 && isCustomMode()) requestItemsInCategory(selectedCatId); - } + PacketClient.sendClient(new CustomAbilityClonePacket(selectedAbility.getName())); + if (selectedCatId >= 0 && isCustomMode()) requestItemsInCategory(selectedCatId); } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java index 5fa69021b..4133082d0 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiEffectDirectory.java @@ -8,6 +8,7 @@ import kamkeel.npcs.network.packets.request.category.CategoryRemovePacket; import kamkeel.npcs.network.packets.request.category.CategorySavePacket; import kamkeel.npcs.network.packets.request.effects.EffectGetPacket; import kamkeel.npcs.network.packets.request.effects.EffectRemovePacket; +import kamkeel.npcs.network.packets.request.effects.EffectClonePacket; import kamkeel.npcs.network.packets.request.effects.EffectSavePacket; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.renderer.texture.TextureManager; @@ -89,12 +90,7 @@ public class GuiEffectDirectory extends GuiDirectoryCategorized { @Override protected void onCloneItem() { if (effect != null && effect.id >= 0) { - NBTTagCompound cloneNbt = effect.writeToNBT(true); - cloneNbt.setInteger("ID", -1); - String name = cloneNbt.getString("name"); - while (itemData.containsKey(name)) name += "_"; - cloneNbt.setString("name", name); - PacketClient.sendClient(new EffectSavePacket(cloneNbt, "")); + PacketClient.sendClient(new EffectClonePacket(effect.id)); } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java index 117cb60c6..20f92a51f 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiLinkedItemDirectory.java @@ -9,6 +9,7 @@ import kamkeel.npcs.network.packets.request.category.CategorySavePacket; import kamkeel.npcs.network.packets.request.linked.LinkedGetPacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemBuildPacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemRemovePacket; +import kamkeel.npcs.network.packets.request.linked.LinkedItemClonePacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemSavePacket; import kamkeel.npcs.util.ColorUtil; import net.minecraft.client.gui.GuiScreen; @@ -90,12 +91,7 @@ public class GuiLinkedItemDirectory extends GuiDirectoryCategorized { @Override protected void onCloneItem() { if (linkedItem != null && linkedItem.id >= 0) { - NBTTagCompound cloneNbt = linkedItem.writeToNBT(true); - cloneNbt.setInteger("Id", -1); - String name = cloneNbt.getString("Name"); - while (itemData.containsKey(name)) name += "_"; - cloneNbt.setString("Name", name); - PacketClient.sendClient(new LinkedItemSavePacket(cloneNbt, "")); + PacketClient.sendClient(new LinkedItemClonePacket(linkedItem.id)); } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageEffects.java b/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageEffects.java index 741b7d2d8..80fe7b943 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageEffects.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageEffects.java @@ -3,6 +3,7 @@ package noppes.npcs.client.gui.global; import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.packets.request.effects.EffectGetPacket; import kamkeel.npcs.network.packets.request.effects.EffectRemovePacket; +import kamkeel.npcs.network.packets.request.effects.EffectClonePacket; import kamkeel.npcs.network.packets.request.effects.EffectSavePacket; import kamkeel.npcs.network.packets.request.effects.EffectsGetPacket; import net.minecraft.client.gui.GuiButton; @@ -106,10 +107,9 @@ public class GuiNPCManageEffects extends GuiNPCInterface2 implements ICustomScro displayGuiScreen(guiyesno); } } else if (button.id == 2) { - CustomEffect effect = this.effect.cloneEffect(); - while (data.containsKey(effect.name)) - effect.name += "_"; - PacketClient.sendClient(new EffectSavePacket(effect.writeToNBT(false), "")); + if (effect != null && effect.id >= 0) { + PacketClient.sendClient(new EffectClonePacket(effect.id)); + } } if (button.id == 66) { diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageLinked.java b/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageLinked.java index eeeb69a77..ea200e7e3 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageLinked.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageLinked.java @@ -5,6 +5,7 @@ import kamkeel.npcs.network.packets.request.linked.LinkedGetAllPacket; import kamkeel.npcs.network.packets.request.linked.LinkedGetPacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemBuildPacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemRemovePacket; +import kamkeel.npcs.network.packets.request.linked.LinkedItemClonePacket; import kamkeel.npcs.network.packets.request.linked.LinkedItemSavePacket; import kamkeel.npcs.network.packets.request.linked.LinkedNPCAddPacket; import kamkeel.npcs.network.packets.request.linked.LinkedNPCRemovePacket; @@ -288,13 +289,7 @@ public class GuiNPCManageLinked extends GuiNPCInterface2 implements IScrollData, } if (button.id == 4) { if (data.containsKey(scroll.getSelected()) && linkedItem != null && linkedItem.id >= 0) { - String name = linkedItem.name; - while (data.containsKey(name)) - name += "_"; - LinkedItem linkedItemClone = this.linkedItem.clone(); - linkedItemClone.name = name; - linkedItemClone.id = -1; - PacketClient.sendClient(new LinkedItemSavePacket(linkedItemClone.writeToNBT(false), "")); + PacketClient.sendClient(new LinkedItemClonePacket(this.linkedItem.id)); } } if (button.id == 5) { diff --git a/src/main/java/noppes/npcs/controllers/CustomEffectController.java b/src/main/java/noppes/npcs/controllers/CustomEffectController.java index c5f083788..bff6b64bd 100644 --- a/src/main/java/noppes/npcs/controllers/CustomEffectController.java +++ b/src/main/java/noppes/npcs/controllers/CustomEffectController.java @@ -673,13 +673,7 @@ public class CustomEffectController implements ICustomEffectHandler { @Override public ICustomEffect saveEffect(ICustomEffect customEffect) { if (customEffect.getID() < 0) { - int oldId = customEffect.getID(); customEffect.setID(getUnusedId()); - // Move script handler from old ID to new ID (e.g. during clone) - EffectScript handler = customEffectScriptHandlers.remove(oldId); - if (handler != null) { - customEffectScriptHandlers.put(customEffect.getID(), handler); - } while (has(customEffect.getName())) customEffect.setName(customEffect.getName() + "_"); } @@ -714,4 +708,23 @@ public class CustomEffectController implements ICustomEffectHandler { return getCustomEffects().get(customEffect.getID()); } + public CustomEffect cloneEffect(int originalId) { + CustomEffect original = getCustomEffects().get(originalId); + if (original == null) return null; + + NBTTagCompound nbt = original.writeToNBT(true); + int newId = getUnusedId(); + nbt.setInteger("ID", newId); + + CustomEffect clone = new CustomEffect(); + clone.readFromNBT(nbt); + + String name = clone.getName(); + while (has(name)) name += "_"; + clone.name = name; + + saveEffect(clone); + return clone; + } + } diff --git a/src/main/java/noppes/npcs/controllers/LinkedItemController.java b/src/main/java/noppes/npcs/controllers/LinkedItemController.java index 1820a21ac..e0aeccb71 100644 --- a/src/main/java/noppes/npcs/controllers/LinkedItemController.java +++ b/src/main/java/noppes/npcs/controllers/LinkedItemController.java @@ -209,13 +209,7 @@ public class LinkedItemController implements ILinkedItemHandler { public ILinkedItem saveLinkedItem(ILinkedItem linkedItem) { if (linkedItem.getId() < 0) { - int oldId = linkedItem.getId(); linkedItem.setId(getUnusedId()); - // Move script handler from old ID to new ID (e.g. during clone) - LinkedItemScript handler = linkedItemsScripts.remove(oldId); - if (handler != null) { - linkedItemsScripts.put(linkedItem.getId(), handler); - } while (hasName(linkedItem.getName())) linkedItem.setName(linkedItem.getName() + "_"); } @@ -247,6 +241,25 @@ public class LinkedItemController implements ILinkedItemHandler { return linkedItems.get(linkedItem.getId()); } + public LinkedItem cloneLinkedItem(int originalId) { + LinkedItem original = linkedItems.get(originalId); + if (original == null) return null; + + NBTTagCompound nbt = original.writeToNBT(true); + int newId = getUnusedId(); + nbt.setInteger("Id", newId); + + LinkedItem clone = new LinkedItem(); + clone.readFromNBT(nbt); + + String name = clone.getName(); + while (hasName(name)) name += "_"; + clone.name = name; + + saveLinkedItem(clone); + return clone; + } + private boolean hasOther(String name, int id) { for (LinkedItem linkedItem : linkedItems.values()) { if (linkedItem.getId() != id && linkedItem.getName().equalsIgnoreCase(name)) From d1d4155c889b02dfcbc7490b4d30598857ed9eb3 Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 10 Mar 2026 04:50:28 -0400 Subject: [PATCH 29/29] Some GUI and Packet Fixes --- .../category/AbilityCategoryMovePacket.java | 6 ++ .../category/CategoryMoveItemPacket.java | 6 ++ .../gui/global/GuiAbilityDirectory.java | 67 ++++++++++--------- .../gui/util/GuiDirectoryCategorized.java | 4 +- 4 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/main/java/kamkeel/npcs/network/packets/request/category/AbilityCategoryMovePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/category/AbilityCategoryMovePacket.java index e498ed4a2..4912bcd28 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/category/AbilityCategoryMovePacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/category/AbilityCategoryMovePacket.java @@ -14,6 +14,7 @@ import kamkeel.npcs.util.ByteBufUtils; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import noppes.npcs.constants.EnumCategoryType; +import noppes.npcs.controllers.CategoryManager; import java.io.IOException; @@ -63,6 +64,11 @@ public final class AbilityCategoryMovePacket extends AbstractPacket { String itemName = ByteBufUtils.readString(in); int cat = in.readInt(); + // Validate target category exists (0 = Uncategorized is always valid) + CategoryManager cm = CategorySavePacket.getManager(type); + if (cm == null) return; + if (cat != CategoryManager.UNCATEGORIZED_ID && cm.getCategory(cat) == null) return; + switch (type) { case EnumCategoryType.ABILITY: AbilityController.Instance.moveCustomAbilityToCategory(itemName, cat); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/category/CategoryMoveItemPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/category/CategoryMoveItemPacket.java index 0aed23e7e..4953bcf0c 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/category/CategoryMoveItemPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/category/CategoryMoveItemPacket.java @@ -14,6 +14,7 @@ import net.minecraft.entity.player.EntityPlayerMP; import kamkeel.npcs.controllers.AbilityController; import noppes.npcs.constants.EnumCategoryType; import noppes.npcs.controllers.AnimationController; +import noppes.npcs.controllers.CategoryManager; import noppes.npcs.controllers.CustomEffectController; import noppes.npcs.controllers.LinkedItemController; @@ -65,6 +66,11 @@ public final class CategoryMoveItemPacket extends AbstractPacket { int item = in.readInt(); int cat = in.readInt(); + // Validate target category exists (0 = Uncategorized is always valid) + CategoryManager cm = CategorySavePacket.getManager(type); + if (cm == null) return; + if (cat != CategoryManager.UNCATEGORIZED_ID && cm.getCategory(cat) == null) return; + switch (type) { case EnumCategoryType.EFFECT: CustomEffectController.getInstance().moveItemToCategory(item, cat); break; case EnumCategoryType.ANIMATION: AnimationController.getInstance().moveItemToCategory(item, cat); break; diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java index 8985c80da..0684218c2 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiAbilityDirectory.java @@ -764,44 +764,47 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized GL11.glEnable(GL11.GL_SCISSOR_TEST); setScissorClip(previewX, previewY, previewW, previewH); - GL11.glEnable(GL11.GL_COLOR_MATERIAL); - GL11.glPushMatrix(); - - GL11.glTranslatef(npcScreenX, npcScreenY, 500F); - GL11.glScalef(-renderZoom, renderZoom, renderZoom); - GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); - GL11.glRotatef(CAMERA_PITCH, 1.0F, 0.0F, 0.0F); - GL11.glRotatef(rotation, 0.0F, 1.0F, 0.0F); - - GL11.glRotatef(135F, 0.0F, 1.0F, 0.0F); - RenderHelper.enableStandardItemLighting(); - GL11.glRotatef(-135F, 0.0F, 1.0F, 0.0F); - - GL11.glTranslatef(0.0F, npc.yOffset, 0.0F); - RenderManager.instance.playerViewY = 180F; - ClientEventHandler.renderingEntityInGUI = true; - - // Render NPC at its position delta try { - RenderManager.instance.renderEntityWithPosYaw(npc, npcDeltaX, npcDeltaY, npcDeltaZ, 0.0F, partialTicks); - } catch (Exception ignored) {} + GL11.glEnable(GL11.GL_COLOR_MATERIAL); + GL11.glPushMatrix(); - // Render preview entities - renderPreviewEntities(partialTicks); + GL11.glTranslatef(npcScreenX, npcScreenY, 500F); + GL11.glScalef(-renderZoom, renderZoom, renderZoom); + GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(CAMERA_PITCH, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(rotation, 0.0F, 1.0F, 0.0F); - // Render telegraph - renderPreviewTelegraph(partialTicks); + GL11.glRotatef(135F, 0.0F, 1.0F, 0.0F); + RenderHelper.enableStandardItemLighting(); + GL11.glRotatef(-135F, 0.0F, 1.0F, 0.0F); - ClientEventHandler.renderingEntityInGUI = false; - GL11.glPopMatrix(); + GL11.glTranslatef(0.0F, npc.yOffset, 0.0F); + RenderManager.instance.playerViewY = 180F; + ClientEventHandler.renderingEntityInGUI = true; - RenderHelper.disableStandardItemLighting(); - GL11.glDisable(GL12.GL_RESCALE_NORMAL); - OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); - GL11.glDisable(GL11.GL_TEXTURE_2D); - OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + // Render NPC at its position delta + try { + RenderManager.instance.renderEntityWithPosYaw(npc, npcDeltaX, npcDeltaY, npcDeltaZ, 0.0F, partialTicks); + } catch (Exception ignored) {} - GL11.glDisable(GL11.GL_SCISSOR_TEST); + // Render preview entities + renderPreviewEntities(partialTicks); + + // Render telegraph + renderPreviewTelegraph(partialTicks); + + ClientEventHandler.renderingEntityInGUI = false; + GL11.glPopMatrix(); + + RenderHelper.disableStandardItemLighting(); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GL11.glDisable(GL11.GL_TEXTURE_2D); + OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + } finally { + ClientEventHandler.renderingEntityInGUI = false; + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); } diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java b/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java index e8be1249a..b27429ec1 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiDirectoryCategorized.java @@ -771,7 +771,7 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory if (subgui instanceof SubGuiEditText) { if (!((SubGuiEditText) subgui).cancelled && selectedCatId > 0) { String name = ((SubGuiEditText) subgui).text; - if (name != null && !name.isEmpty() && !catData.containsKey(name)) { + if (name != null && !name.isEmpty() && !name.equals(prevCatName) && !catData.containsKey(name)) { Category cat = new Category(selectedCatId, name); onSaveCategory(cat); catData.remove(prevCatName); @@ -779,6 +779,8 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory prevCatName = name; } } + // Restore category selection to current name (handles validation failure) + catScroll.setSelected(prevCatName); } if (subgui instanceof SubGuiCategoryMoveConfirm) { SubGuiCategoryMoveConfirm confirm = (SubGuiCategoryMoveConfirm) subgui;