diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java index 44bb6beb2..b2743f629 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java @@ -1278,6 +1278,10 @@ public abstract class Ability implements IAbility { return typeId; } + public boolean isBuiltIn() { + return false; + } + public int getWeight() { return weight; } 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 294f9a765..01ddb7b58 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java @@ -48,6 +48,9 @@ public class AbilityController implements IAbilityHandler { /** External variant definitions registered by addons, keyed by typeId. */ private final Map> externalVariants = new LinkedHashMap<>(); + /** TypeIds that produce BuiltInAbility instances (non-editable in GUI). */ + private final Set builtInTypeIds = new HashSet<>(); + public AbilityController() { registerBuiltinTypes(); } @@ -173,7 +176,9 @@ public class AbilityController implements IAbilityHandler { if (abilities.containsKey(name)) { LogWriter.info("AbilityController: Overwriting built-in ability: " + name); } - ability.setName(name); + if (!ability.isBuiltIn()) { + ability.setName(name); + } abilities.put(name, ability); LogWriter.info("Registered ability: " + name); } @@ -281,12 +286,22 @@ public class AbilityController implements IAbilityHandler { // ACCESSORS // ═══════════════════════════════════════════════════════════════════════════ - /** Get a built-in ability by name. Returns the stored instance (not a copy). */ + /** Get a built-in ability by map key. Returns the stored instance (not a copy). */ public Ability getAbility(String name) { return abilities.get(name); } - /** Get all built-in ability names. */ + /** Get a built-in ability by its display name (ability.getName()). */ + public Ability getAbilityByDisplayName(String displayName) { + for (Ability ability : abilities.values()) { + if (ability.getName().equals(displayName)) { + return ability; + } + } + return null; + } + + /** Get all built-in ability map keys. */ public Set getAbilityNames() { return new LinkedHashSet<>(abilities.keySet()); } @@ -338,6 +353,10 @@ public class AbilityController implements IAbilityHandler { return typeFactories.keySet().toArray(new String[0]); } + public boolean isBuiltInType(String typeId) { + return builtInTypeIds.contains(typeId); + } + @Override public boolean hasType(String typeId) { return typeFactories.containsKey(typeId); @@ -386,6 +405,9 @@ public class AbilityController implements IAbilityHandler { LogWriter.info("AbilityController: Overwriting type: " + typeId); } typeFactories.put(typeId, factory); + if (temp.isBuiltIn()) { + builtInTypeIds.add(typeId); + } } /** diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/BuiltInAbility.java b/src/main/java/kamkeel/npcs/controllers/data/ability/BuiltInAbility.java index 904fad0b9..36344e9e5 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/BuiltInAbility.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/BuiltInAbility.java @@ -21,7 +21,13 @@ public abstract class BuiltInAbility extends Ability { this.registryKey = registryKey; this.id = registryKey; this.name = registryKey; - this.typeId = registryKey; + // Convert "namespace:name" to "ability.namespace.name" for GUI filter compatibility + this.typeId = "ability." + registryKey.replace(':', '.'); + } + + @Override + public boolean isBuiltIn() { + return true; } /** diff --git a/src/main/java/kamkeel/npcs/network/PacketHandler.java b/src/main/java/kamkeel/npcs/network/PacketHandler.java index bcfd18abd..6db07c841 100644 --- a/src/main/java/kamkeel/npcs/network/PacketHandler.java +++ b/src/main/java/kamkeel/npcs/network/PacketHandler.java @@ -96,6 +96,7 @@ import kamkeel.npcs.network.packets.request.ability.AbilitiesNpcSavePacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilitiesGetPacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilityGetPacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilityRemovePacket; +import kamkeel.npcs.network.packets.request.ability.BuiltInAbilityGetPacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilitySavePacket; import kamkeel.npcs.network.packets.request.animation.AnimationGetPacket; import kamkeel.npcs.network.packets.request.animation.AnimationRemovePacket; @@ -516,6 +517,7 @@ public class PacketHandler { REQUEST_PACKET.registerPacket(new CustomAbilityGetPacket()); REQUEST_PACKET.registerPacket(new CustomAbilityRemovePacket()); REQUEST_PACKET.registerPacket(new CustomAbilitySavePacket()); + REQUEST_PACKET.registerPacket(new BuiltInAbilityGetPacket()); // Other Packets REQUEST_PACKET.registerPacket(new IsGuiOpenInform()); diff --git a/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java b/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java index 2f3e6ea94..25ba2bba6 100644 --- a/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java +++ b/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java @@ -205,6 +205,7 @@ public enum EnumRequestPacket { CustomAbilityGet, CustomAbilityRemove, CustomAbilitySave, + BuiltInAbilityGet, Hammer, RecipeScript, diff --git a/src/main/java/kamkeel/npcs/network/packets/request/ability/BuiltInAbilityGetPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/ability/BuiltInAbilityGetPacket.java new file mode 100644 index 000000000..6e565f3de --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/ability/BuiltInAbilityGetPacket.java @@ -0,0 +1,70 @@ +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.data.ability.Ability; +import kamkeel.npcs.controllers.data.ability.AbilityController; +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 net.minecraft.nbt.NBTTagCompound; + +import java.io.IOException; + +/** + * Request packet to get a specific built-in ability by name. + * Built-in abilities are registered presets with fixed configurations. + */ +public final class BuiltInAbilityGetPacket extends AbstractPacket { + public static String packetName = "Request|BuiltInAbilityGet"; + + private String abilityName; + + public BuiltInAbilityGetPacket() { + } + + public BuiltInAbilityGetPacket(String abilityName) { + this.abilityName = abilityName; + } + + @Override + public Enum getType() { + return EnumRequestPacket.BuiltInAbilityGet; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.REQUEST_PACKET; + } + + @SideOnly(Side.CLIENT) + @Override + public void sendData(ByteBuf out) throws IOException { + ByteBufUtils.writeString(out, abilityName); + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + if (!(player instanceof EntityPlayerMP)) + return; + + if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player)) + return; + + String name = ByteBufUtils.readString(in); + Ability ability = AbilityController.Instance.getAbilityByDisplayName(name); + if (ability != null) { + NBTTagCompound compound = ability.writeNBT(); + compound.setBoolean("BuiltIn", true); + GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); + } + } +} diff --git a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilitiesGetPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilitiesGetPacket.java index 8febd9fc0..6097c7483 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilitiesGetPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilitiesGetPacket.java @@ -47,5 +47,6 @@ public final class CustomAbilitiesGetPacket extends AbstractPacket { return; NoppesUtilServer.sendCustomAbilitiesData((EntityPlayerMP) player); + NoppesUtilServer.sendBuiltInAbilitiesData((EntityPlayerMP) player); } } diff --git a/src/main/java/kamkeel/npcs/util/Register.java b/src/main/java/kamkeel/npcs/util/Register.java index db6802f34..5a64fca6a 100644 --- a/src/main/java/kamkeel/npcs/util/Register.java +++ b/src/main/java/kamkeel/npcs/util/Register.java @@ -37,6 +37,7 @@ public class Register { public static class Abilities extends Register { protected final Map uniqueNames = new LinkedHashMap<>(); + protected final Set typeOnly = new HashSet<>(); private Abilities(String namespace) { super("ability", namespace); @@ -50,9 +51,25 @@ public class Register { return factory.get(); } + /** + * Register a type only (shell template, no built-in preset). + * The type will appear in the ability type picker but won't be + * available as a pre-configured ability by name. + */ + public Ability registerType(String factoryName, Supplier factory) { + String name = registryKey + "." + namespace + "." + factoryName.trim().toLowerCase().replaceAll(" ", "_"); + entries.put(name, factory); + uniqueNames.put(name, factoryName); + typeOnly.add(name); + return factory.get(); + } + public void register() { for (Map.Entry> entry : entries.entrySet()) { - AbilityController.Instance.registerAbility(uniqueNames.get(entry.getKey()), entry.getValue().get()); + AbilityController.Instance.registerType(entry.getKey(), entry.getValue()); + if (!typeOnly.contains(entry.getKey())) { + AbilityController.Instance.registerAbility(uniqueNames.get(entry.getKey()), entry.getValue().get()); + } } } diff --git a/src/main/java/noppes/npcs/NoppesUtilServer.java b/src/main/java/noppes/npcs/NoppesUtilServer.java index 7faa7b2b5..b54b4b245 100644 --- a/src/main/java/noppes/npcs/NoppesUtilServer.java +++ b/src/main/java/noppes/npcs/NoppesUtilServer.java @@ -781,12 +781,29 @@ public class NoppesUtilServer { if (controller != null) { int index = 0; for (String typeId : controller.getTypes()) { + // Exclude built-in types from the NPC type list + if (controller.isBuiltInType(typeId)) continue; map.put(typeId, index++); } } sendScrollData(player, map, EnumScrollData.ABILITY_TYPES); } + public static void sendBuiltInAbilitiesData(EntityPlayerMP player) { + HashMap map = new HashMap(); + AbilityController controller = AbilityController.Instance; + if (controller != null) { + int index = 0; + for (String name : controller.getAbilityNames()) { + Ability ability = controller.getAbility(name); + if (ability != null) { + map.put(ability.getName(), index++); + } + } + } + sendScrollData(player, map, EnumScrollData.BUILTIN_ABILITIES); + } + public static void sendCustomAbilitiesData(EntityPlayerMP player) { HashMap map = new HashMap(); kamkeel.npcs.controllers.data.ability.AbilityController controller = kamkeel.npcs.controllers.data.ability.AbilityController.Instance; diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiNpcDialog.java b/src/main/java/noppes/npcs/client/gui/SubGuiNpcDialog.java index 3e194f85f..38777d9ab 100644 --- a/src/main/java/noppes/npcs/client/gui/SubGuiNpcDialog.java +++ b/src/main/java/noppes/npcs/client/gui/SubGuiNpcDialog.java @@ -198,6 +198,5 @@ public class SubGuiNpcDialog extends SubGuiInterface implements ISubGuiListener, } public void save() { - PacketClient.sendClient(new DialogSavePacket(this.dialogCategoryID, dialog.writeToNBT(new NBTTagCompound()), false)); } } diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiNpcDialogVisual.java b/src/main/java/noppes/npcs/client/gui/SubGuiNpcDialogVisual.java index 807a3e719..86f174bc4 100644 --- a/src/main/java/noppes/npcs/client/gui/SubGuiNpcDialogVisual.java +++ b/src/main/java/noppes/npcs/client/gui/SubGuiNpcDialogVisual.java @@ -521,8 +521,6 @@ public class SubGuiNpcDialogVisual extends SubGuiInterface implements ISubGuiLis public void save() { GuiNpcTextField.unfocus(); - if (dialog.id >= 0) - PacketClient.sendClient(new DialogSavePacket(((SubGuiNpcDialog) parent).dialogCategoryID, dialog.writeToNBT(new NBTTagCompound()), false)); } private DialogImage getSelectedImage() { diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiNpcQuest.java b/src/main/java/noppes/npcs/client/gui/SubGuiNpcQuest.java index 6406733be..9d693db81 100644 --- a/src/main/java/noppes/npcs/client/gui/SubGuiNpcQuest.java +++ b/src/main/java/noppes/npcs/client/gui/SubGuiNpcQuest.java @@ -239,6 +239,5 @@ public class SubGuiNpcQuest extends SubGuiInterface implements ISubGuiListener, } public void save() { - PacketClient.sendClient(new QuestSavePacket(this.questCategoryID, quest.writeToNBT(new NBTTagCompound()), false)); } } 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 7bc5c3846..4b9e99a13 100644 --- a/src/main/java/noppes/npcs/client/gui/advanced/GuiNPCAbilities.java +++ b/src/main/java/noppes/npcs/client/gui/advanced/GuiNPCAbilities.java @@ -168,9 +168,16 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC addButton(toggleBtn); } + // Check if selected ability is built-in (non-editable) + boolean selectedIsBuiltIn = false; + if (selectedAbilityIndex >= 0 && selectedAbilityIndex < npcSlots.size()) { + Ability sel = npcSlots.get(selectedAbilityIndex).getAbility(); + if (sel != null && sel.isBuiltIn()) selectedIsBuiltIn = true; + } + // Right side buttons: Edit and Up/Down carrots with Save button addButton(new GuiNpcButton(72, guiLeft + 210, y + 145, 55, 20, "gui.edit")); - getButton(72).setEnabled(selectedAbilityIndex >= 0); + getButton(72).setEnabled(selectedAbilityIndex >= 0 && !selectedIsBuiltIn); // Up/Down carrot buttons addButton(new GuiNpcButton(73, guiLeft + 270, y + 145, 20, 20, "<")); @@ -180,7 +187,7 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC // Save button (to the right of carrots, only enabled when ability is selected) addButton(new GuiNpcButton(76, centerX, y + 82, 40, 20, "gui.save")); - getButton(76).setEnabled(selectedAbilityIndex >= 0); + getButton(76).setEnabled(selectedAbilityIndex >= 0 && !selectedIsBuiltIn); } @Override @@ -464,6 +471,10 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC String displayName = I18n.format(typeId); // Search matches either the display name or the typeId if (search.isEmpty() || displayName.toLowerCase().contains(search) || typeId.toLowerCase().contains(search)) { + // Gray out built-in types to distinguish them from configurable types + if (AbilityController.Instance.isBuiltInType(typeId)) { + displayName = "\u00A77" + displayName; + } list.add(displayName); displayNameToTypeId.put(displayName, typeId); } @@ -512,6 +523,9 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC if (slot.isReference()) { return colorPrefix + (index + 1) + ". \u00A7e> " + nameDisplay + "\u00A7r"; } + if (ability.isBuiltIn()) { + return colorPrefix + (index + 1) + ". \u00A77" + nameDisplay + "\u00A7r"; + } return colorPrefix + (index + 1) + ". " + nameDisplay; } @@ -612,10 +626,11 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC public void customScrollDoubleClicked(String selection, GuiCustomScroll scroll) { if (scroll == npcAbilitiesScroll && selectedAbilityIndex >= 0 && selectedAbilityIndex < npcSlots.size()) { AbilitySlot slot = npcSlots.get(selectedAbilityIndex); + Ability ability = slot.getAbility(); + if (ability != null && ability.isBuiltIn()) return; if (slot.isReference()) { setSubGui(new SubGuiAbilityEditMode()); } else { - Ability ability = slot.getAbility(); if (ability != null) { setSubGui(ability.createConfigGui(this)); } diff --git a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityTypeSelect.java b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityTypeSelect.java index 5d810862f..cce48bd32 100644 --- a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityTypeSelect.java +++ b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityTypeSelect.java @@ -58,6 +58,8 @@ public class SubGuiAbilityTypeSelect extends SubGuiInterface implements ICustomS allDisplayNameToTypeId.clear(); String[] types = AbilityController.Instance.getTypes(); for (String typeId : types) { + // Exclude built-in types — they cannot be created as custom presets + if (AbilityController.Instance.isBuiltInType(typeId)) continue; String displayName = I18n.format(typeId); allDisplayNameToTypeId.put(displayName, typeId); } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiNpcManageAbilities.java b/src/main/java/noppes/npcs/client/gui/global/GuiNpcManageAbilities.java index b06f9d097..66e8ed46c 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiNpcManageAbilities.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiNpcManageAbilities.java @@ -6,6 +6,7 @@ import kamkeel.npcs.controllers.data.ability.AbilityController; import java.util.UUID; 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.CustomAbilitiesGetPacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilityGetPacket; import kamkeel.npcs.network.packets.request.ability.CustomAbilityRemovePacket; @@ -51,6 +52,7 @@ import java.util.Vector; * - Left side: 3D preview of NPC with ability effects * - Right side: Ability list with search * - Play/Pause/Stop buttons to preview abilities + * - Toggle between Custom and Built-in ability views */ public class GuiNpcManageAbilities extends GuiAbilityInterface implements ICustomScrollListener, ISubGuiListener, IAbilityConfigCallback, @@ -58,7 +60,8 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface // ==================== DATA ==================== private GuiCustomScroll scroll; - private HashMap abilityData = new HashMap<>(); + private HashMap customData = new HashMap<>(); + private HashMap builtInData = new HashMap<>(); /** Maps display name -> UUID for custom abilities */ private final HashMap displayToUuid = new HashMap<>(); private String selected = null; @@ -66,6 +69,10 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface private String search = ""; private Ability selectedAbility = null; + // ==================== BUILT-IN TOGGLE ==================== + private boolean showingBuiltIn = false; + private boolean currentIsBuiltIn = false; + // ==================== PENDING SAVE ==================== private Ability pendingSaveAbility = null; private boolean pendingNewCreation = false; @@ -88,7 +95,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface previewExecutor = new AbilityPreviewExecutor(); previewExecutor.setParentGui(this); - // Request ability data from server + // Request ability data from server (sends both custom and built-in) PacketClient.sendClient(new CustomAbilitiesGetPacket()); // Enable animation data @@ -100,18 +107,26 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface public void initGui() { super.initGui(); - // Add button - addButton(new GuiNpcButton(2, guiLeft + 368, guiTop + 8, 45, 20, "gui.add")); + // Toggle button — added FIRST to maintain stable buttonList index + String toggleLabel = showingBuiltIn ? "gui.builtin" : "gui.custom"; + addButton(new GuiNpcButton(10, guiLeft + 368, guiTop + 52, 45, 20, toggleLabel)); - // Remove button - addButton(new GuiNpcButton(1, guiLeft + 368, guiTop + 30, 45, 20, "gui.remove")); - getButton(1).setEnabled(selected != null && !selected.isEmpty() && displayToUuid.containsKey(selected)); + if (!showingBuiltIn) { + // Add button — only for custom view + addButton(new GuiNpcButton(2, guiLeft + 368, guiTop + 8, 45, 20, "gui.add")); - // Edit button - addButton(new GuiNpcButton(100, guiLeft + 368, guiTop + 52, 45, 20, "gui.edit")); - getButton(100).setEnabled(selected != null && !selected.isEmpty() && selectedAbility != null); + // Remove button — only for custom view + addButton(new GuiNpcButton(1, guiLeft + 368, guiTop + 30, 45, 20, "gui.remove")); + getButton(1).setEnabled(selected != null && !selected.isEmpty() && displayToUuid.containsKey(selected)); + } - // Scroll list of saved abilities (right side) + // Edit button — hidden when viewing built-in + if (!showingBuiltIn && !currentIsBuiltIn) { + addButton(new GuiNpcButton(100, guiLeft + 368, guiTop + 74, 45, 20, "gui.edit")); + getButton(100).setEnabled(selected != null && !selected.isEmpty() && selectedAbility != null); + } + + // Scroll list of abilities (right side) if (scroll == null) { scroll = new GuiCustomScroll(this, 0); scroll.setSize(143, 185); @@ -122,7 +137,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface // Update scroll list scroll.setList(getSearchList()); - if (selected != null && displayToUuid.containsKey(selected)) { + if (selected != null) { scroll.setSelected(selected); } @@ -134,9 +149,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface return; } - // ID/Name label (below search bar) - addLabel(new GuiNpcLabel(10, "ID: " + selectedAbility.getId(), guiLeft + 220, guiTop + 215)); - // Play/Pause/Stop buttons (below model preview area) String animTexture = "customnpcs:textures/gui/animation.png"; int playButtonOffsetX = 60; @@ -161,17 +173,34 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface } } + private HashMap getCurrentData() { + return showingBuiltIn ? builtInData : customData; + } + private List getSearchList() { - if (search.isEmpty()) { - return new ArrayList<>(displayToUuid.keySet()); - } - List list = new ArrayList<>(); - for (String name : displayToUuid.keySet()) { - if (name.toLowerCase().contains(search.toLowerCase())) { - list.add(name); + if (showingBuiltIn) { + if (search.isEmpty()) { + return new ArrayList<>(builtInData.keySet()); } + List list = new ArrayList<>(); + for (String name : builtInData.keySet()) { + if (name.toLowerCase().contains(search.toLowerCase())) { + list.add(name); + } + } + return list; + } else { + if (search.isEmpty()) { + return new ArrayList<>(displayToUuid.keySet()); + } + List list = new ArrayList<>(); + for (String name : displayToUuid.keySet()) { + if (name.toLowerCase().contains(search.toLowerCase())) { + list.add(name); + } + } + return list; } - return list; } @Override @@ -227,13 +256,30 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface int id = guibutton.id; - if (id == 2) { + // Toggle between custom and built-in views + if (id == 10) { + showingBuiltIn = !showingBuiltIn; + selected = null; + selectedUuid = null; + selectedAbility = null; + currentIsBuiltIn = false; + search = ""; + previewExecutor.stop(); + if (scroll != null) { + scroll.clear(); + } + PacketClient.sendClient(new CustomAbilitiesGetPacket()); + initGui(); + return; + } + + if (id == 2 && !showingBuiltIn) { // Add — open type selection setSubGui(new SubGuiAbilityTypeSelect()); - } else if (id == 1 && selected != null && selectedUuid != null) { + } else if (id == 1 && !showingBuiltIn && selected != null && selectedUuid != null) { GuiYesNo guiyesno = new GuiYesNo(this, selected, StatCollector.translateToLocal("gui.delete"), 1); displayGuiScreen(guiyesno); - } else if (id == 100 && selectedAbility != null) { + } else if (id == 100 && !showingBuiltIn && !currentIsBuiltIn && selectedAbility != null) { // Edit previewExecutor.stop(); setSubGui(selectedAbility.createConfigGui(this)); @@ -276,11 +322,20 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface String newSelection = scroll.getSelected(); if (newSelection != null && !newSelection.equals(selected)) { previewExecutor.stop(); - selected = newSelection; - selectedUuid = displayToUuid.get(selected); - if (selectedUuid != null) { - PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid)); + + if (showingBuiltIn) { + // Fetch built-in ability by display name + selectedUuid = null; + currentIsBuiltIn = true; + PacketClient.sendClient(new BuiltInAbilityGetPacket(selected)); + } else { + // Fetch custom ability by UUID + selectedUuid = displayToUuid.get(selected); + currentIsBuiltIn = false; + if (selectedUuid != null) { + PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid)); + } } } } @@ -289,8 +344,8 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface @Override public void customScrollDoubleClicked(String selection, GuiCustomScroll scroll) { if (scroll.id == 0 && selection != null && !selection.isEmpty()) { - // Double-click to edit - if (selectedAbility != null) { + // Double-click to edit — only for custom, non-built-in abilities + if (!showingBuiltIn && !currentIsBuiltIn && selectedAbility != null) { previewExecutor.stop(); setSubGui(selectedAbility.createConfigGui(this)); } @@ -301,7 +356,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface public void setData(Vector list, HashMap data, EnumScrollData type) { if (type == EnumScrollData.CUSTOM_ABILITIES) { String prevSelected = scroll != null ? scroll.getSelected() : null; - this.abilityData = data; + this.customData = data; displayToUuid.clear(); for (String key : data.keySet()) { // key format: "displayName\tUUID" @@ -314,18 +369,30 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface displayToUuid.put(key, key); } } - if (scroll != null) { + if (!showingBuiltIn && scroll != null) { scroll.setList(getSearchList()); if (prevSelected != null && displayToUuid.containsKey(prevSelected)) { scroll.setSelected(prevSelected); } } initGui(); + } else if (type == EnumScrollData.BUILTIN_ABILITIES) { + this.builtInData = data; + if (showingBuiltIn && scroll != null) { + scroll.setList(getSearchList()); + if (selected != null && builtInData.containsKey(selected)) { + scroll.setSelected(selected); + } + } + initGui(); } } @Override public void setGuiData(NBTTagCompound compound) { + // Check if this is a built-in ability response + currentIsBuiltIn = compound.hasKey("BuiltIn") && compound.getBoolean("BuiltIn"); + // Received ability data from server selectedAbility = AbilityController.Instance.fromNBT(compound); if (selectedAbility != null) { @@ -364,7 +431,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface return; } pendingTypeId = null; - saveAndOpenConfig(newAbility); + openConfig(newAbility); return; } } @@ -390,7 +457,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface setSubGui(new SubGuiDuplicateNameConfirm()); return; } - saveAndOpenConfig(newAbility); + openConfig(newAbility); return; } } @@ -398,7 +465,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface SubGuiDuplicateNameConfirm confirm = (SubGuiDuplicateNameConfirm) subgui; if (confirm.isConfirmed() && pendingSaveAbility != null) { if (pendingNewCreation) { - saveAndOpenConfig(pendingSaveAbility); + openConfig(pendingSaveAbility); pendingSaveAbility = null; pendingNewCreation = false; return; @@ -434,8 +501,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface initGui(); } - private void saveAndOpenConfig(Ability ability) { - PacketClient.sendClient(new CustomAbilitySavePacket(ability.writeNBT())); + private void openConfig(Ability ability) { selectedAbility = ability; selected = ability.getName(); selectedUuid = ability.getId(); diff --git a/src/main/java/noppes/npcs/constants/EnumScrollData.java b/src/main/java/noppes/npcs/constants/EnumScrollData.java index 06d5a6ec3..cbec6ad34 100644 --- a/src/main/java/noppes/npcs/constants/EnumScrollData.java +++ b/src/main/java/noppes/npcs/constants/EnumScrollData.java @@ -8,6 +8,7 @@ public enum EnumScrollData { QUEST_GROUP, ABILITY_TYPES, CUSTOM_ABILITIES, + BUILTIN_ABILITIES, ANIMATIONS, BUILTIN_ANIMATIONS } diff --git a/src/main/java/noppes/npcs/controllers/data/action/Action.java b/src/main/java/noppes/npcs/controllers/data/action/Action.java index b7bce1102..cb2af9454 100644 --- a/src/main/java/noppes/npcs/controllers/data/action/Action.java +++ b/src/main/java/noppes/npcs/controllers/data/action/Action.java @@ -6,6 +6,7 @@ import noppes.npcs.api.handler.data.IActionQueue; import noppes.npcs.api.handler.data.actions.IConditionalAction; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.scripted.CustomNPCsException; +import org.apache.commons.lang3.exception.ExceptionUtils; import java.util.HashMap; import java.util.Map; @@ -344,6 +345,10 @@ public class Action implements IAction { try { onStart.accept(this); } catch (Throwable t) { + String err = "IAction" + getName() + " onStart threw an exception:"; + if (reportTo != null) + reportTo.appendConsole(err + "\n" + ExceptionUtils.getStackTrace(t)); + manager.LOGGER.error("onStart Task of " + this + " threw an exception:", t); } @@ -359,6 +364,11 @@ public class Action implements IAction { task.accept(this); count++; } catch (Throwable t) { + String err = "IAction '" + name + "' threw an exception:"; + + if (reportTo != null) + reportTo.appendConsole(err + "\n" + ExceptionUtils.getStackTrace(t)); + manager.LOGGER.error("Task of " + this + " threw an exception:", t); markDone(); } @@ -374,6 +384,10 @@ public class Action implements IAction { try { onDone.accept(this); } catch (Throwable t) { + String err = "IAction" + getName() + " onDone threw an exception:"; + if (reportTo != null) + reportTo.appendConsole(err + "\n" + ExceptionUtils.getStackTrace(t)); + manager.LOGGER.error("onDone Task of " + this + " threw an exception:", t); } diff --git a/src/main/java/noppes/npcs/controllers/data/action/action/ConditionalAction.java b/src/main/java/noppes/npcs/controllers/data/action/action/ConditionalAction.java index 9e608df37..427749e33 100644 --- a/src/main/java/noppes/npcs/controllers/data/action/action/ConditionalAction.java +++ b/src/main/java/noppes/npcs/controllers/data/action/action/ConditionalAction.java @@ -4,6 +4,7 @@ import noppes.npcs.api.handler.data.IAction; import noppes.npcs.api.handler.data.actions.IConditionalAction; import noppes.npcs.controllers.data.action.Action; import noppes.npcs.controllers.data.action.ActionManager; +import org.apache.commons.lang3.exception.ExceptionUtils; import java.util.function.Consumer; import java.util.function.Function; @@ -130,6 +131,10 @@ public class ConditionalAction extends Action implements IConditionalAction { count++; taskExecuted = true; } catch (Throwable t) { + String err = "IConditionalAction '" + getName() + "' threw an exception:"; + if (reportTo != null) + reportTo.appendConsole(err + "\n" + ExceptionUtils.getStackTrace(t)); + manager.LOGGER.error("Task of " + this + " threw an exception:", t); markDone(); } @@ -146,6 +151,10 @@ public class ConditionalAction extends Action implements IConditionalAction { try { onTermination.accept(this); } catch (Throwable t) { + String err = "IConditionalAction" + getName() + " onTermination threw an exception:"; + if (reportTo != null) + reportTo.appendConsole(err + "\n" + ExceptionUtils.getStackTrace(t)); + manager.LOGGER.error("Termination Task of " + this + " threw an exception:", t); }