Fix Ability Cloning Behavior for Scripts

This commit is contained in:
Kam committed 2026-03-10 21:55:25 -04:00
1 parent 9b0c364e13
commit 0cc3a48f0b
2 files changed
+73 -15

No files matched your search

@@ -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);
}
}
@@ -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();