From ce24c0a4ff4d63676d53396f096fb72df2390ab8 Mon Sep 17 00:00:00 2001 From: Kam Date: Sun, 15 Feb 2026 19:21:30 -0500 Subject: [PATCH] Enhance ability selection and display: add toggleable ability handling, improve ability name color coding, and update ability selection messaging --- .../kamkeel/npcs/command/AbilityCommand.java | 4 +-- .../data/ability/AbilityController.java | 18 +++++++--- .../controllers/data/ability/AbilitySlot.java | 34 ++++++++++++++++++- .../npcs/entity/EntityAbilityBeam.java | 20 +++++++++++ .../npcs/entity/EntityAbilityProjectile.java | 16 +++++---- .../noppes/npcs/client/ClientTickHandler.java | 4 ++- .../client/gui/advanced/GuiNPCAbilities.java | 6 ++-- 7 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/main/java/kamkeel/npcs/command/AbilityCommand.java b/src/main/java/kamkeel/npcs/command/AbilityCommand.java index 004274fc3..90d20ea1e 100644 --- a/src/main/java/kamkeel/npcs/command/AbilityCommand.java +++ b/src/main/java/kamkeel/npcs/command/AbilityCommand.java @@ -306,8 +306,8 @@ public class AbilityCommand extends CommandKamkeelBase { } /** - * Get list of player-usable ability keys (for tab completion). - * Uses registry keys for built-in abilities (no spaces) and UUIDs for custom. + * Get list of player-usable ability names (for tab completion). + * Uses registry keys for built-in abilities and display names for custom abilities. * Used by CommandKamkeel for usage token. */ public static List getPlayerAbilityNames() { diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java index 992c56cd9..43af10b7c 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java @@ -304,7 +304,13 @@ public class AbilityController implements IAbilityHandler { keys.add(id); } } - keys.addAll(customAbilities.keySet()); + // Use ability names for custom abilities (instead of UUIDs) for readable tab completion + for (Ability ability : customAbilities.values()) { + String name = ability.getName(); + if (name != null && !name.isEmpty()) { + keys.add(name); + } + } return keys; } @@ -318,9 +324,13 @@ public class AbilityController implements IAbilityHandler { } } } - for (Map.Entry entry : customAbilities.entrySet()) { - if (entry.getValue().getAllowedBy().allowsPlayer()) { - keys.add(entry.getKey()); + // Use ability names for custom abilities (instead of UUIDs) for readable tab completion + for (Ability ability : customAbilities.values()) { + if (ability.getAllowedBy().allowsPlayer()) { + String name = ability.getName(); + if (name != null && !name.isEmpty()) { + keys.add(name); + } } } return keys; diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilitySlot.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilitySlot.java index 5fde66586..4849e4dd4 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilitySlot.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilitySlot.java @@ -14,6 +14,9 @@ public class AbilitySlot { private Ability inlineAbility; private String referenceId; + /** Per-slot enabled override for reference slots (null = use master's default). */ + private Boolean enabledOverride; + private transient Ability cachedAbility; private transient int cachedVersion = -1; @@ -67,10 +70,32 @@ public class AbilitySlot { } cachedAbility = controller.resolveAbility(referenceId); + // Apply per-slot enabled override so NPC-specific enable/disable persists + if (cachedAbility != null && enabledOverride != null) { + cachedAbility.setEnabled(enabledOverride); + } cachedVersion = currentVersion; return cachedAbility; } + /** + * Set the per-slot enabled override for reference slots. + * For inline slots, delegates directly to the ability. + */ + public void setEnabled(boolean enabled) { + if (!isReference()) { + if (inlineAbility != null) { + inlineAbility.setEnabled(enabled); + } + } else { + this.enabledOverride = enabled; + // Also update the cached ability immediately if present + if (cachedAbility != null) { + cachedAbility.setEnabled(enabled); + } + } + } + /** * Get the inline ability directly (null for reference slots). */ @@ -104,6 +129,9 @@ public class AbilitySlot { if (isReference()) { NBTTagCompound nbt = new NBTTagCompound(); nbt.setString("Reference", referenceId); + if (enabledOverride != null) { + nbt.setBoolean("RefEnabled", enabledOverride); + } return nbt; } return inlineAbility != null ? inlineAbility.writeNBT() : new NBTTagCompound(); @@ -113,7 +141,11 @@ public class AbilitySlot { if (nbt == null) return null; if (nbt.hasKey("Reference")) { - return reference(nbt.getString("Reference")); + AbilitySlot slot = reference(nbt.getString("Reference")); + if (nbt.hasKey("RefEnabled")) { + slot.enabledOverride = nbt.getBoolean("RefEnabled"); + } + return slot; } // Inline ability diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java index 0fb58bcfe..6f5b3a64d 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java @@ -696,6 +696,7 @@ public class EntityAbilityBeam extends EntityAbilityProjectile { // Read trail points (relative to origin) trailPoints.clear(); + trailPointAges.clear(); if (nbt.hasKey("Trail")) { NBTTagList trailList = nbt.getTagList("Trail", 10); for (int i = 0; i < trailList.tagCount(); i++) { @@ -707,6 +708,18 @@ public class EntityAbilityBeam extends EntityAbilityProjectile { )); } } + + // Read trail point ages (must match trailPoints size for fading trail) + if (nbt.hasKey("TrailAges")) { + int[] ages = nbt.getIntArray("TrailAges"); + for (int age : ages) { + trailPointAges.add(age); + } + } + // If trail points exist but ages don't (legacy data), initialize ages to 0 + while (trailPointAges.size() < trailPoints.size()) { + trailPointAges.add(0); + } } @Override @@ -733,5 +746,12 @@ public class EntityAbilityBeam extends EntityAbilityProjectile { trailList.appendTag(pointNbt); } nbt.setTag("Trail", trailList); + + // Write trail point ages for fading trail sync + int[] ages = new int[trailPointAges.size()]; + for (int i = 0; i < trailPointAges.size(); i++) { + ages[i] = trailPointAges.get(i); + } + nbt.setIntArray("TrailAges", ages); } } diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java index dee2ed292..9368c5269 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java @@ -896,10 +896,10 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP this.motionY = nbt.getDouble("MotionY"); this.motionZ = nbt.getDouble("MotionZ"); - if (this.worldObj != null && this.worldObj.isRemote) { - this.renderCurrentSize = this.size; - this.prevRenderSize = this.size; - } + // Always initialize render size from NBT size so entities + // loading in from outside render distance render correctly + this.renderCurrentSize = this.size; + this.prevRenderSize = this.size; anchorData.readNBT(nbt); combatData.readNBT(nbt); @@ -965,7 +965,9 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP NBTTagCompound compound = new NBTTagCompound(); this.writeEntityToNBT(compound); cpw.mods.fml.common.network.ByteBufUtils.writeTag(buffer, compound); - } catch (Exception ignored) {} + } catch (Exception e) { + noppes.npcs.LogWriter.error("Error writing ability projectile spawn data", e); + } } @Override @@ -975,7 +977,9 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP if (compound != null) { this.readEntityFromNBT(compound); } - } catch (Exception ignored) {} + } catch (Exception e) { + noppes.npcs.LogWriter.error("Error reading ability projectile spawn data", e); + } } // ==================== COLLISION SETTINGS ==================== diff --git a/src/main/java/noppes/npcs/client/ClientTickHandler.java b/src/main/java/noppes/npcs/client/ClientTickHandler.java index b4e8738c8..7d82ca0f1 100644 --- a/src/main/java/noppes/npcs/client/ClientTickHandler.java +++ b/src/main/java/noppes/npcs/client/ClientTickHandler.java @@ -63,7 +63,9 @@ public class ClientTickHandler { if (event.phase == Phase.START) { EntityPlayer player = mc.thePlayer; if (player != null) { - boolean specialKeyDown = ClientProxy.SpecialKey != null && Keyboard.isKeyDown(ClientProxy.SpecialKey.getKeyCode()); + // Only process special key when no GUI screen is open + boolean specialKeyDown = mc.currentScreen == null + && ClientProxy.SpecialKey != null && Keyboard.isKeyDown(ClientProxy.SpecialKey.getKeyCode()); if (specialKeyDown != lastSpecialKeyDown) { PlayerData data = CustomNpcs.proxy.getPlayerData(player); if (data != null) { diff --git a/src/main/java/noppes/npcs/client/gui/advanced/GuiNPCAbilities.java b/src/main/java/noppes/npcs/client/gui/advanced/GuiNPCAbilities.java index f8ed53c7c..e927b088f 100644 --- a/src/main/java/noppes/npcs/client/gui/advanced/GuiNPCAbilities.java +++ b/src/main/java/noppes/npcs/client/gui/advanced/GuiNPCAbilities.java @@ -317,9 +317,11 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC // Toggle selected ability on/off if (id == 77) { if (selectedAbilityIndex >= 0 && selectedAbilityIndex < npcSlots.size()) { - Ability ability = npcSlots.get(selectedAbilityIndex).getAbility(); + AbilitySlot slot = npcSlots.get(selectedAbilityIndex); + Ability ability = slot.getAbility(); if (ability != null) { - ability.setEnabled(((GuiNpcButton) guibutton).getValue() == 1); + boolean enabled = ((GuiNpcButton) guibutton).getValue() == 1; + slot.setEnabled(enabled); updateNpcAbilitiesList(); selectAbilityByIndex(selectedAbilityIndex); initGui();