Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vidal committed 2026-02-17 23:02:39 -03:00
commit 90e146e7b0
12 files changed
+171 -185

No files matched your search

@@ -33,20 +33,19 @@ public class AbilityCommand extends CommandKamkeelBase {
desc = "List all custom abilities"
)
public void list(ICommandSender sender, String[] args) {
Set<String> uuids = AbilityController.Instance.getCustomAbilityIds();
if (uuids.isEmpty()) {
Set<String> names = AbilityController.Instance.getCustomAbilityNames();
if (names.isEmpty()) {
sendResult(sender, "No custom abilities found.");
return;
}
sendResult(sender, "Custom Abilities (" + uuids.size() + "):");
for (String uuid : uuids) {
Ability ability = AbilityController.Instance.getCustomAbility(uuid);
sendResult(sender, "Custom Abilities (" + names.size() + "):");
for (String name : names) {
Ability ability = AbilityController.Instance.getCustomAbility(name);
if (ability != null) {
String name = ability.getName() != null ? ability.getName() : uuid;
sendResult(sender, " - \u00A7b" + name + "\u00A77 (" + ability.getTypeId() + ") [" + uuid + "]");
sendResult(sender, " - \u00A7b" + name + "\u00A77 (" + ability.getTypeId() + ")");
} else {
sendResult(sender, " - \u00A7b" + uuid);
sendResult(sender, " - \u00A7b" + name);
}
}
}
@@ -82,23 +81,23 @@ public class AbilityCommand extends CommandKamkeelBase {
}
@SubCommand(
desc = "Delete a custom ability by UUID",
usage = "<uuid>"
desc = "Delete a custom ability by name",
usage = "<name>"
)
public void delete(ICommandSender sender, String[] args) {
if (args.length < 1) {
sendError(sender, "Usage: /kam ability delete <uuid>");
sendError(sender, "Usage: /kam ability delete <name>");
return;
}
String uuid = args[0];
if (!AbilityController.Instance.hasCustomAbility(uuid)) {
sendError(sender, "No custom ability with UUID: " + uuid);
String name = joinArgs(args, 0);
if (!AbilityController.Instance.hasCustomAbilityName(name)) {
sendError(sender, "No custom ability with name: " + name);
return;
}
AbilityController.Instance.deleteCustomAbility(uuid);
sendResult(sender, "Deleted ability: \u00A7b" + uuid);
AbilityController.Instance.deleteCustomAbility(name);
sendResult(sender, "Deleted ability: \u00A7b" + name);
}
@SubCommand(
@@ -189,7 +188,7 @@ public class AbilityCommand extends CommandKamkeelBase {
return;
}
// Use the ability's canonical ID for storage (registry key for built-in, UUID for custom)
// Use the ability's canonical ID for storage (registry key for built-in, name for custom)
String canonicalKey = ability.getId();
if (canonicalKey == null || canonicalKey.isEmpty()) {
canonicalKey = abilityKey; // Fallback to user-provided key
@@ -24,6 +24,7 @@ import java.io.File;
import java.util.*;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;
public class AbilityController implements IAbilityHandler {
@@ -40,6 +41,11 @@ public class AbilityController implements IAbilityHandler {
private final List<IAbilityExtender> extenders = new ArrayList<>();
private final List<Predicate<EntityPlayer>> flightCheckers = new ArrayList<>();
// ── Legacy Migration ─────────────────────────────────────────────────────
private static final Pattern UUID_PATTERN =
Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
Pattern.CASE_INSENSITIVE);
// ── Derived State ────────────────────────────────────────────────────────
private final Set<String> builtInTypeIds = new HashSet<>();
@@ -156,6 +162,7 @@ public class AbilityController implements IAbilityHandler {
public void load() {
customAbilities.clear();
int migrated = 0;
File dir = getDir();
File[] files = dir.exists() ? dir.listFiles() : null;
@@ -164,14 +171,39 @@ public class AbilityController implements IAbilityHandler {
if (!file.isFile() || !file.getName().endsWith(".json")) continue;
try {
String filename = file.getName();
String uuid = filename.substring(0, filename.length() - 5);
String key = filename.substring(0, filename.length() - 5);
NBTTagCompound nbt = NBTJsonUtil.LoadFile(file);
Ability ability = fromNBT(nbt);
if (ability != null) {
ability.setId(uuid);
customAbilities.put(uuid, ability);
if (ability == null) continue;
// Legacy migration: UUID-named files get renamed to name-based
if (UUID_PATTERN.matcher(key).matches()) {
String name = ability.getName();
if (name == null || name.isEmpty()) {
name = key; // Fallback to UUID if no name stored
}
// Ensure unique name
while (hasCustomAbilityName(name) || nameFileExists(dir, name)) {
name = name + "_";
}
ability.setName(name);
// Rename file from UUID.json to name.json
File newFile = new File(dir, name + ".json");
if (file.renameTo(newFile)) {
LogWriter.info("Migrated ability file: " + key + ".json -> " + name + ".json");
} else {
LogWriter.error("Failed to rename ability file: " + key + ".json -> " + name + ".json");
}
key = name;
migrated++;
}
ability.setId(key);
customAbilities.put(key, ability);
} catch (Exception e) {
LogWriter.error("Error loading custom ability: " + file.getAbsolutePath(), e);
}
@@ -179,21 +211,57 @@ public class AbilityController implements IAbilityHandler {
}
customAbilityRevision++;
if (migrated > 0) {
LogWriter.info("Migrated " + migrated + " UUID-named abilities to name-based files");
}
LogWriter.info("Loaded " + customAbilities.size() + " custom abilities");
}
private boolean nameFileExists(File dir, String name) {
return new File(dir, name + ".json").exists();
}
public boolean saveCustomAbility(Ability ability) {
if (ability == null) return false;
String uuid = ability.getId();
if (uuid == null || uuid.isEmpty()) {
uuid = UUID.randomUUID().toString();
ability.setId(uuid);
String name = ability.getName();
if (name == null || name.isEmpty()) return false;
// Ensure unique name for new abilities (no existing id)
String oldName = ability.getId();
boolean isNew = oldName == null || oldName.isEmpty();
if (isNew) {
while (hasCustomAbilityName(name)) {
name = name + "_";
}
ability.setName(name);
}
// Handle rename: old id (previous name) differs from current name
if (!isNew && !oldName.equals(name)) {
// Check uniqueness of new name (excluding the entry being renamed)
String testName = name;
while (customAbilities.containsKey(testName) && !testName.equals(oldName)) {
testName = testName + "_";
}
if (!testName.equals(name)) {
name = testName;
ability.setName(name);
}
// Delete old file and remove old map entry
File oldFile = new File(getDir(), oldName + ".json");
if (oldFile.exists()) {
oldFile.delete();
}
customAbilities.remove(oldName);
}
ability.setId(name);
File dir = getDir();
File fileNew = new File(dir, uuid + ".json_new");
File fileCurrent = new File(dir, uuid + ".json");
File fileNew = new File(dir, name + ".json_new");
File fileCurrent = new File(dir, name + ".json");
try {
NBTTagCompound nbt = ability.writeNBT();
@@ -207,52 +275,43 @@ public class AbilityController implements IAbilityHandler {
fileNew.delete();
}
customAbilities.put(uuid, ability);
customAbilities.put(name, ability);
customAbilityRevision++;
LogWriter.info("Saved custom ability: " + ability.getName() + " [" + uuid + "]");
LogWriter.info("Saved custom ability: " + name);
SyncController.syncAllCustomAbilities();
return true;
} catch (Exception e) {
LogWriter.error("Error saving custom ability: " + uuid, e);
LogWriter.error("Error saving custom ability: " + name, e);
return false;
}
}
public boolean deleteCustomAbility(String uuid) {
if (uuid == null || uuid.isEmpty()) return false;
public boolean deleteCustomAbility(String name) {
if (name == null || name.isEmpty()) return false;
Ability removed = customAbilities.remove(uuid);
Ability removed = customAbilities.remove(name);
if (removed == null) return false;
File dir = getDir();
File file = new File(dir, uuid + ".json");
File file = new File(dir, name + ".json");
if (file.exists()) {
file.delete();
}
customAbilityRevision++;
LogWriter.info("Deleted custom ability: " + uuid);
LogWriter.info("Deleted custom ability: " + name);
SyncController.syncAllCustomAbilities();
return true;
}
public Ability getCustomAbility(String uuid) {
return customAbilities.get(uuid);
public Ability getCustomAbility(String name) {
return customAbilities.get(name);
}
public boolean hasCustomAbility(String uuid) {
return customAbilities.containsKey(uuid);
}
public Set<String> getCustomAbilityIds() {
public Set<String> getCustomAbilityNames() {
return new LinkedHashSet<>(customAbilities.keySet());
}
public String getCustomAbilityName(String uuid) {
Ability a = customAbilities.get(uuid);
return a != null ? a.getName() : null;
}
public Map<String, Ability> getCustomAbilities() {
return customAbilities;
}
@@ -300,7 +359,7 @@ public class AbilityController implements IAbilityHandler {
}
}
// Custom: exact UUID
// Custom: exact name
Ability custom = customAbilities.get(key);
if (custom != null) return custom.deepCopy();
@@ -322,7 +381,7 @@ public class AbilityController implements IAbilityHandler {
keys.add(id);
}
}
// Use ability names for custom abilities (instead of UUIDs) for readable tab completion
// Custom abilities keyed by name
for (Ability ability : customAbilities.values()) {
String name = ability.getName();
if (name != null && !name.isEmpty()) {
@@ -342,7 +401,7 @@ public class AbilityController implements IAbilityHandler {
}
}
}
// Use ability names for custom abilities (instead of UUIDs) for readable tab completion
// Custom abilities keyed by name
for (Ability ability : customAbilities.values()) {
if (ability.getAllowedBy().allowsPlayer()) {
String name = ability.getName();
@@ -378,7 +437,7 @@ public class AbilityController implements IAbilityHandler {
if (ability.getId() != null && ability.getId().equalsIgnoreCase(key)) return true;
}
// Custom: exact UUID
// Custom: exact name
if (customAbilities.containsKey(key)) return true;
// Custom: case-insensitive name
@@ -549,17 +608,17 @@ public class AbilityController implements IAbilityHandler {
}
@Override
public String[] getCustomAbilityIdArray() {
public String[] getCustomAbilityNameArray() {
return customAbilities.keySet().toArray(new String[0]);
}
@Override
public boolean hasCustomAbilityId(String uuid) {
return customAbilities.containsKey(uuid);
public boolean hasCustomAbilityName(String name) {
return customAbilities.containsKey(name);
}
@Override
public boolean deleteCustomAbilityById(String uuid) {
return deleteCustomAbility(uuid);
public boolean deleteCustomAbilityByName(String name) {
return deleteCustomAbility(name);
}
}
@@ -33,7 +33,7 @@ public class AbilitySlot {
}
/**
* Create a slot that references a built-in ability (by name) or custom ability (by UUID).
* Create a slot that references a built-in ability (by name) or custom ability (by name).
*/
public static AbilitySlot reference(String referenceId) {
AbilitySlot slot = new AbilitySlot();
@@ -20,18 +20,18 @@ import net.minecraft.nbt.NBTTagCompound;
import java.io.IOException;
/**
* Request packet to get a specific custom ability by UUID.
* Request packet to get a specific custom ability by name.
*/
public final class CustomAbilityGetPacket extends AbstractPacket {
public static String packetName = "Request|CustomAbilityGet";
private String uuid;
private String name;
public CustomAbilityGetPacket() {
}
public CustomAbilityGetPacket(String uuid) {
this.uuid = uuid;
public CustomAbilityGetPacket(String name) {
this.name = name;
}
@Override
@@ -47,7 +47,7 @@ public final class CustomAbilityGetPacket extends AbstractPacket {
@SideOnly(Side.CLIENT)
@Override
public void sendData(ByteBuf out) throws IOException {
ByteBufUtils.writeString(out, uuid);
ByteBufUtils.writeString(out, name);
}
@Override
@@ -58,12 +58,10 @@ public final class CustomAbilityGetPacket extends AbstractPacket {
if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player))
return;
String id = ByteBufUtils.readString(in);
Ability ability = AbilityController.Instance.getCustomAbility(id);
String abilityName = ByteBufUtils.readString(in);
Ability ability = AbilityController.Instance.getCustomAbility(abilityName);
if (ability != null) {
NBTTagCompound compound = ability.writeNBT();
// Include the UUID so the client can track it
compound.setString("_uuid", id);
GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound);
}
}
@@ -19,18 +19,18 @@ import noppes.npcs.NoppesUtilServer;
import java.io.IOException;
/**
* Request packet to remove a custom ability by UUID.
* Request packet to remove a custom ability by name.
*/
public final class CustomAbilityRemovePacket extends AbstractPacket {
public static String packetName = "Request|CustomAbilityRemove";
private String uuid;
private String name;
public CustomAbilityRemovePacket() {
}
public CustomAbilityRemovePacket(String uuid) {
this.uuid = uuid;
public CustomAbilityRemovePacket(String name) {
this.name = name;
}
@Override
@@ -51,7 +51,7 @@ public final class CustomAbilityRemovePacket extends AbstractPacket {
@SideOnly(Side.CLIENT)
@Override
public void sendData(ByteBuf out) throws IOException {
ByteBufUtils.writeString(out, uuid);
ByteBufUtils.writeString(out, name);
}
@Override
@@ -71,7 +71,6 @@ public final class CustomAbilitySavePacket extends AbstractPacket {
AbilityController.Instance.saveCustomAbility(ability);
NoppesUtilServer.sendCustomAbilitiesData((EntityPlayerMP) player);
NBTTagCompound responseNbt = ability.writeNBT();
responseNbt.setString("_uuid", ability.getId());
GuiDataPacket.sendGuiData((EntityPlayerMP) player, responseNbt);
}
}
+1 -1
View File
@@ -950,7 +950,7 @@ public class DataAbilities {
abilitySlots.add(AbilitySlot.inline(ability));
}
/** Add a reference ability by key (built-in name or custom UUID). */
/** Add a reference ability by key (built-in name or custom ability name). */
public void addAbilityReference(String key) {
abilitySlots.add(AbilitySlot.reference(key));
}
@@ -810,13 +810,10 @@ public class NoppesUtilServer {
kamkeel.npcs.controllers.data.ability.AbilityController controller = kamkeel.npcs.controllers.data.ability.AbilityController.Instance;
if (controller != null) {
int index = 0;
for (String uuid : controller.getCustomAbilityIds()) {
Ability ability = controller.getCustomAbility(uuid);
for (String name : controller.getCustomAbilityNames()) {
Ability ability = controller.getCustomAbility(name);
if (ability == null || !ability.getAllowedBy().allowsNpc()) continue;
String name = controller.getCustomAbilityName(uuid);
if (name == null || name.isEmpty()) name = uuid;
map.put(name + "\t" + uuid, index++);
map.put(name, index++);
}
}
sendScrollData(player, map, EnumScrollData.CUSTOM_ABILITIES);
@@ -21,7 +21,6 @@ import noppes.npcs.constants.EnumScrollData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
/**
@@ -33,10 +32,8 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
private final SubGuiAbilityConfig parentConfig;
private final GuiNPCAbilities parentAbilities;
private String selectedDisplayName = null;
private String selectedUuid = null;
private String selectedName = null;
/** Maps display name -> UUID for custom abilities. */
private final HashMap<String, String> displayToUuid = new HashMap<>();
private HashMap<String, Integer> rawData = new HashMap<>();
private GuiCustomScroll scroll;
@@ -97,15 +94,15 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
addButton(new GuiNpcButton(0, guiLeft + 10, y, 95, 20, "gui.load"));
addButton(new GuiNpcButton(2, guiLeft + 115, y, 95, 20, "gui.cancel"));
getButton(0).setEnabled(selectedUuid != null);
getButton(0).setEnabled(selectedName != null);
}
private List<String> getFilteredList() {
if (search.isEmpty()) {
return new ArrayList<>(displayToUuid.keySet());
return new ArrayList<>(rawData.keySet());
}
List<String> list = new ArrayList<>();
for (String name : displayToUuid.keySet()) {
for (String name : rawData.keySet()) {
if (name.toLowerCase().contains(search.toLowerCase())) {
list.add(name);
}
@@ -117,7 +114,7 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
public void buttonEvent(GuiButton guibutton) {
int id = guibutton.id;
if (id == 0 && selectedUuid != null) {
if (id == 0 && selectedName != null) {
if (parentAbilities != null) {
// From NPC abilities GUI - ask clone/reference
setSubGui(new SubGuiAbilityLoadMode());
@@ -125,7 +122,7 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
// From config GUI - always clone
waitingForLoad = true;
pendingLoadMode = SubGuiAbilityLoadMode.MODE_CLONE;
PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid));
PacketClient.sendClient(new CustomAbilityGetPacket(selectedName));
}
} else if (id == 2) {
close();
@@ -152,16 +149,16 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
if (mode < 0) return; // cancelled
if (mode == SubGuiAbilityLoadMode.MODE_REFERENCE) {
// Reference mode - just pass the reference ID
if (parentAbilities != null && selectedUuid != null) {
parentAbilities.loadAbilityReference(selectedUuid);
// Reference mode - just pass the reference name
if (parentAbilities != null && selectedName != null) {
parentAbilities.loadAbilityReference(selectedName);
}
close();
} else {
// Clone mode - request full data
waitingForLoad = true;
pendingLoadMode = SubGuiAbilityLoadMode.MODE_CLONE;
PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid));
PacketClient.sendClient(new CustomAbilityGetPacket(selectedName));
}
}
}
@@ -170,9 +167,7 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
public void customScrollClicked(int i, int j, int k, GuiCustomScroll scroll) {
if (scroll.id == 0) {
selectedDisplayName = scroll.getSelected();
if (selectedDisplayName != null) {
selectedUuid = displayToUuid.get(selectedDisplayName);
}
selectedName = selectedDisplayName;
initGui();
}
}
@@ -181,15 +176,13 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
public void customScrollDoubleClicked(String selection, GuiCustomScroll scroll) {
if (scroll.id == 0 && selection != null && !selection.isEmpty()) {
selectedDisplayName = selection;
selectedUuid = displayToUuid.get(selectedDisplayName);
if (selectedUuid != null) {
if (parentAbilities != null) {
setSubGui(new SubGuiAbilityLoadMode());
} else {
waitingForLoad = true;
pendingLoadMode = SubGuiAbilityLoadMode.MODE_CLONE;
PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid));
}
selectedName = selection;
if (parentAbilities != null) {
setSubGui(new SubGuiAbilityLoadMode());
} else {
waitingForLoad = true;
pendingLoadMode = SubGuiAbilityLoadMode.MODE_CLONE;
PacketClient.sendClient(new CustomAbilityGetPacket(selectedName));
}
}
}
@@ -197,22 +190,10 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
@Override
public void setData(Vector<String> list, HashMap<String, Integer> data, EnumScrollData type) {
if (type == EnumScrollData.CUSTOM_ABILITIES) {
displayToUuid.clear();
rawData = data;
for (String key : data.keySet()) {
// key format: "displayName\tUUID"
int tabIndex = key.indexOf('\t');
if (tabIndex > 0) {
String displayName = key.substring(0, tabIndex);
String uuid = key.substring(tabIndex + 1);
displayToUuid.put(displayName, uuid);
} else {
displayToUuid.put(key, key);
}
}
if (scroll != null) {
scroll.setList(getFilteredList());
if (selectedDisplayName != null && displayToUuid.containsKey(selectedDisplayName)) {
if (selectedDisplayName != null && rawData.containsKey(selectedDisplayName)) {
scroll.setSelected(selectedDisplayName);
}
}
@@ -226,11 +207,6 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL
waitingForLoad = false;
Ability loaded = AbilityController.Instance.fromNBT(compound);
if (loaded != null) {
if (pendingLoadMode == SubGuiAbilityLoadMode.MODE_CLONE) {
// Give it a new UUID so it's a unique inline copy
loaded.setId(UUID.randomUUID().toString());
}
if (parentConfig != null) {
parentConfig.loadAbility(loaded);
} else if (parentAbilities != null) {
@@ -3,7 +3,6 @@ package noppes.npcs.client.gui.global;
import kamkeel.npcs.client.renderer.TelegraphRenderer;
import kamkeel.npcs.controllers.data.ability.Ability;
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;
@@ -62,10 +61,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
private GuiCustomScroll scroll;
private HashMap<String, Integer> customData = new HashMap<>();
private HashMap<String, Integer> builtInData = new HashMap<>();
/** Maps display name -> UUID for custom abilities */
private final HashMap<String, String> displayToUuid = new HashMap<>();
private String selected = null;
private String selectedUuid = null;
private String search = "";
private Ability selectedAbility = null;
@@ -117,7 +113,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
// 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));
getButton(1).setEnabled(selected != null && !selected.isEmpty() && customData.containsKey(selected));
}
// Edit button — hidden when viewing built-in
@@ -178,29 +174,17 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
}
private List<String> getSearchList() {
if (showingBuiltIn) {
if (search.isEmpty()) {
return new ArrayList<>(builtInData.keySet());
}
List<String> 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<String> list = new ArrayList<>();
for (String name : displayToUuid.keySet()) {
if (name.toLowerCase().contains(search.toLowerCase())) {
list.add(name);
}
}
return list;
HashMap<String, Integer> data = getCurrentData();
if (search.isEmpty()) {
return new ArrayList<>(data.keySet());
}
List<String> list = new ArrayList<>();
for (String name : data.keySet()) {
if (name.toLowerCase().contains(search.toLowerCase())) {
list.add(name);
}
}
return list;
}
@Override
@@ -260,7 +244,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
if (id == 10) {
showingBuiltIn = !showingBuiltIn;
selected = null;
selectedUuid = null;
selectedAbility = null;
currentIsBuiltIn = false;
search = "";
@@ -276,7 +259,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
if (id == 2 && !showingBuiltIn) {
// Add — open type selection
setSubGui(new SubGuiAbilityTypeSelect());
} else if (id == 1 && !showingBuiltIn && selected != null && selectedUuid != null) {
} else if (id == 1 && !showingBuiltIn && selected != null) {
GuiYesNo guiyesno = new GuiYesNo(this, selected, StatCollector.translateToLocal("gui.delete"), 1);
displayGuiScreen(guiyesno);
} else if (id == 100 && !showingBuiltIn && !currentIsBuiltIn && selectedAbility != null) {
@@ -325,17 +308,11 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
selected = newSelection;
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));
}
PacketClient.sendClient(new CustomAbilityGetPacket(selected));
}
}
}
@@ -357,21 +334,9 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
if (type == EnumScrollData.CUSTOM_ABILITIES) {
String prevSelected = scroll != null ? scroll.getSelected() : null;
this.customData = data;
displayToUuid.clear();
for (String key : data.keySet()) {
// key format: "displayName\tUUID"
int tabIndex = key.indexOf('\t');
if (tabIndex > 0) {
String displayName = key.substring(0, tabIndex);
String uuid = key.substring(tabIndex + 1);
displayToUuid.put(displayName, uuid);
} else {
displayToUuid.put(key, key);
}
}
if (!showingBuiltIn && scroll != null) {
scroll.setList(getSearchList());
if (prevSelected != null && displayToUuid.containsKey(prevSelected)) {
if (prevSelected != null && customData.containsKey(prevSelected)) {
scroll.setSelected(prevSelected);
}
}
@@ -422,7 +387,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
Ability newAbility = AbilityController.Instance.create(pendingTypeId);
if (newAbility != null) {
variantGui.getVariants().get(idx).apply(newAbility);
newAbility.setId(UUID.randomUUID().toString());
if (hasDuplicateName(newAbility)) {
pendingSaveAbility = newAbility;
pendingNewCreation = true;
@@ -450,7 +414,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
if (variants.size() == 1) {
variants.get(0).apply(newAbility);
}
newAbility.setId(UUID.randomUUID().toString());
if (hasDuplicateName(newAbility)) {
pendingSaveAbility = newAbility;
pendingNewCreation = true;
@@ -504,7 +467,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
private void openConfig(Ability ability) {
selectedAbility = ability;
selected = ability.getName();
selectedUuid = ability.getId();
previewExecutor.stop();
setSubGui(ability.createConfigGui(this));
}
@@ -512,12 +474,9 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
private boolean hasDuplicateName(Ability ability) {
String name = ability.getName();
if (name == null || name.isEmpty()) return false;
for (java.util.Map.Entry<String, String> entry : displayToUuid.entrySet()) {
if (entry.getKey().equals(name) && !entry.getValue().equals(ability.getId())) {
return true;
}
}
return false;
String oldName = ability.getId();
// A duplicate exists if the name is already in use and it's not the same ability being edited
return customData.containsKey(name) && !name.equals(oldName);
}
@Override
@@ -525,11 +484,10 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface
NoppesUtil.openGUI(player, this);
if (!result) return;
if (id == 1 && selected != null && selectedUuid != null) {
PacketClient.sendClient(new CustomAbilityRemovePacket(selectedUuid));
if (id == 1 && selected != null) {
PacketClient.sendClient(new CustomAbilityRemovePacket(selected));
scroll.clear();
selected = null;
selectedUuid = null;
selectedAbility = null;
initGui();
}
@@ -36,7 +36,7 @@ public class ScriptPlayerAbilityData implements IPlayerAbilityData {
if (!ability.getAllowedBy().allowsPlayer()) {
return; // Ability is NPC-only
}
// Use canonical key (registry key for built-in, UUID for custom)
// Use canonical key (registry key for built-in, name for custom)
String canonicalKey = ability.getId() != null ? ability.getId() : key;
data.unlockAbility(canonicalKey);
}