From 0cc3a48f0bc96b100c418de2564b459dd0bd6778 Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 10 Mar 2026 21:55:25 -0400 Subject: [PATCH] Fix Ability Cloning Behavior for Scripts --- .../ability/CopyAbilityScriptsPacket.java | 70 +++++++++++++++---- .../client/gui/advanced/GuiNPCAbilities.java | 18 +++++ 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/src/main/java/kamkeel/npcs/network/packets/request/ability/CopyAbilityScriptsPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/ability/CopyAbilityScriptsPacket.java index b4d3813ec..628b761f8 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/ability/CopyAbilityScriptsPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/ability/CopyAbilityScriptsPacket.java @@ -5,6 +5,7 @@ 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.controllers.data.ability.data.ChainedAbility; import kamkeel.npcs.network.AbstractPacket; import kamkeel.npcs.network.PacketChannel; import kamkeel.npcs.network.PacketHandler; @@ -14,29 +15,41 @@ import kamkeel.npcs.network.enums.EnumRequestPacket; import kamkeel.npcs.util.ByteBufUtils; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import noppes.npcs.CustomNpcsPermissions; import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.CustomNpcsPermissions; import noppes.npcs.controllers.data.AbilityScript; +import noppes.npcs.controllers.data.ChainedAbilityScript; import java.io.IOException; /** - * Copies script data from a source ability to a target ability UUID. - * Used when cloning an ability into an NPC as inline - the clone gets a new UUID + * Copies script data from a source ability/chain to a target UUID. + * Used when cloning into an NPC as inline - the clone gets a new UUID * but needs the source's script handler copied server-side (scripts are never sent to client). + * + * Mode 0 = ability scripts, Mode 1 = chained ability scripts. */ public final class CopyAbilityScriptsPacket extends AbstractPacket { public static String packetName = "Request|CopyAbilityScripts"; - private String sourceAbilityName; - private String targetAbilityId; + public static final int MODE_ABILITY = 0; + public static final int MODE_CHAINED = 1; + + private int mode; + private String sourceName; + private String targetId; public CopyAbilityScriptsPacket() { } - public CopyAbilityScriptsPacket(String sourceAbilityName, String targetAbilityId) { - this.sourceAbilityName = sourceAbilityName; - this.targetAbilityId = targetAbilityId; + public CopyAbilityScriptsPacket(String sourceName, String targetId) { + this(MODE_ABILITY, sourceName, targetId); + } + + public CopyAbilityScriptsPacket(int mode, String sourceName, String targetId) { + this.mode = mode; + this.sourceName = sourceName; + this.targetId = targetId; } @Override @@ -57,8 +70,9 @@ public final class CopyAbilityScriptsPacket extends AbstractPacket { @SideOnly(Side.CLIENT) @Override public void sendData(ByteBuf out) throws IOException { - ByteBufUtils.writeString(out, sourceAbilityName); - ByteBufUtils.writeString(out, targetAbilityId); + out.writeByte(mode); + ByteBufUtils.writeString(out, sourceName); + ByteBufUtils.writeString(out, targetId); } @Override @@ -69,22 +83,48 @@ public final class CopyAbilityScriptsPacket extends AbstractPacket { if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player)) return; + int mode = in.readByte(); String sourceName = ByteBufUtils.readString(in); String targetId = ByteBufUtils.readString(in); if (targetId == null || targetId.isEmpty()) return; - // Look up source ability's script handler - Ability source = AbilityController.Instance.getCustomAbility(sourceName); - if (source == null) return; + if (mode == MODE_CHAINED) { + copyChainedAbilityScripts(sourceName, targetId); + } else { + copyAbilityScripts(sourceName, targetId); + } + } - AbilityScript sourceHandler = source.getScriptHandler(); + private void copyAbilityScripts(String sourceKey, String targetId) { + // Try name-based lookup first + Ability source = AbilityController.Instance.getCustomAbility(sourceKey); + + AbilityScript sourceHandler; + if (source != null) { + sourceHandler = source.getScriptHandler(); + } else { + // sourceKey might be a UUID - try direct handler lookup + sourceHandler = AbilityController.Instance.abilityScriptHandlers.get(sourceKey); + } if (sourceHandler == null) return; - // Clone the script handler for the target UUID via NBT round-trip NBTTagCompound scriptNbt = sourceHandler.writeToNBT(new NBTTagCompound()); AbilityScript targetHandler = new AbilityScript(targetId); targetHandler.readFromNBT(scriptNbt); AbilityController.Instance.abilityScriptHandlers.put(targetId, targetHandler); } + + private void copyChainedAbilityScripts(String sourceName, String targetId) { + ChainedAbility source = AbilityController.Instance.getChainedAbility(sourceName); + if (source == null) return; + + ChainedAbilityScript sourceHandler = source.getScriptHandler(); + if (sourceHandler == null) return; + + NBTTagCompound scriptNbt = sourceHandler.writeToNBT(new NBTTagCompound()); + ChainedAbilityScript targetHandler = new ChainedAbilityScript(targetId); + targetHandler.readFromNBT(scriptNbt); + AbilityController.Instance.chainedAbilityScriptHandlers.put(targetId, targetHandler); + } } 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 7c6e411ed..c6129b12a 100644 --- a/src/main/java/noppes/npcs/client/gui/advanced/GuiNPCAbilities.java +++ b/src/main/java/noppes/npcs/client/gui/advanced/GuiNPCAbilities.java @@ -994,12 +994,18 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC if (slot.isChain() && selectedEntryIndex == -1) { // Chain header (must be CHAIN_REFERENCE) if (mode == SubGuiAbilityEditMode.MODE_CLONE_MODIFY) { + String sourceChainName = slot.getReferenceId(); if (slot.convertToInline()) { ChainedAbility chain = slot.getInlineChain(); if (chain != null) { pendingChain = chain; pendingChainSlotIdx = selectedSlotIndex; setSubGui(new SubGuiChainedAbilityConfig(chain, this, true, npcSlots)); + // Copy chain scripts server-side (client never has script handlers) + if (sourceChainName != null && !sourceChainName.isEmpty()) { + PacketClient.sendClient(new CopyAbilityScriptsPacket( + CopyAbilityScriptsPacket.MODE_CHAINED, sourceChainName, chain.getId())); + } } updateNpcAbilitiesList(); save(); @@ -1028,6 +1034,8 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC ChainedAbility chain = slot.getInlineChain(); if (chain != null && selectedEntryIndex < chain.getEntries().size()) { ChainedAbilityEntry entry = chain.getEntries().get(selectedEntryIndex); + // Capture source ability reference before convertToInline clears it + String sourceAbilityRef = entry.getAbilityReference(); if (entry.convertToInline()) { Ability a = entry.getInlineAbility(); if (a != null && !a.isBuiltIn()) { @@ -1036,6 +1044,10 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC editChainEntryIdx = selectedEntryIndex; a.setNpcInlineEdit(true); setSubGui(a.createConfigGui(this)); + // Copy ability scripts server-side (client never has script handlers) + if (sourceAbilityRef != null && !sourceAbilityRef.isEmpty()) { + PacketClient.sendClient(new CopyAbilityScriptsPacket(sourceAbilityRef, a.getId())); + } } } } @@ -1056,12 +1068,18 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC // Standalone ability reference → convert to inline Ability preCheck = slot.getAbility(); if (preCheck != null && preCheck.isBuiltIn()) return; + // Capture source reference before convertToInline clears it + String sourceAbilityRef = slot.getReferenceId(); if (slot.convertToInline()) { Ability ability = slot.getAbility(); if (ability != null && !ability.isBuiltIn()) { // UUID already generated by convertToInline() - don't reassign or script handler is orphaned ability.setNpcInlineEdit(true); setSubGui(ability.createConfigGui(this)); + // Copy ability scripts server-side (client never has script handlers) + if (sourceAbilityRef != null && !sourceAbilityRef.isEmpty()) { + PacketClient.sendClient(new CopyAbilityScriptsPacket(sourceAbilityRef, ability.getId())); + } } updateNpcAbilitiesList(); save();