diff --git a/CONCEPT_FOR_TABS.png b/CONCEPT_FOR_TABS.png new file mode 100644 index 000000000..17a02be71 Binary files /dev/null and b/CONCEPT_FOR_TABS.png differ diff --git a/src/api b/src/api index 29ad8637d..3f8ccc087 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit 29ad8637d68f12b3138c5b0c266088147432733b +Subproject commit 3f8ccc087f781a43c2e5a2044f5d859be53d88c8 diff --git a/src/main/java/kamkeel/npcs/command/CloneCommand.java b/src/main/java/kamkeel/npcs/command/CloneCommand.java index 027ff228e..312293ca5 100644 --- a/src/main/java/kamkeel/npcs/command/CloneCommand.java +++ b/src/main/java/kamkeel/npcs/command/CloneCommand.java @@ -13,6 +13,7 @@ import net.minecraft.util.MathHelper; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import noppes.npcs.controllers.ServerCloneController; +import noppes.npcs.controllers.data.CloneFolder; import noppes.npcs.entity.EntityNPCInterface; import java.util.List; @@ -35,20 +36,24 @@ public class CloneCommand extends CommandKamkeelBase { @SubCommand( desc = "Add NPC(s) to clone storage", - usage = " [clonedname]", + usage = " [clonedname]", permission = 4 ) public void add(ICommandSender sender, String[] args) { - int tab = 0; + int tab = -1; + String folder = null; try { tab = Integer.parseInt(args[1]); - if (tab < 0 || tab > 15) { + if (tab < 1 || tab > 15) { sendError(sender, "Tab must be within 1-15"); return; } } catch (NumberFormatException ex) { - sendError(sender, String.format("Tab is not a number: %s", args[1])); - return; + folder = args[1]; + if (ServerCloneController.Instance == null || !ServerCloneController.Instance.hasFolder(folder)) { + sendError(sender, String.format("Unknown folder: %s", folder)); + return; + } } int x = sender.getPlayerCoordinates().posX; @@ -64,32 +69,47 @@ public class CloneCommand extends CommandKamkeelBase { NBTTagCompound compound = new NBTTagCompound(); if (!npc.writeToNBTOptional(compound)) return; - ServerCloneController.Instance.addClone(compound, name, tab); - sendResult(sender, String.format("Added NPC \u00A7e%s\u00A77 to Tab \u00A7b%d\u00A77", name, tab)); + if (folder != null) { + ServerCloneController.Instance.addClone(compound, name, folder); + sendResult(sender, String.format("Added NPC \u00A7e%s\u00A77 to Folder \u00A7b%s\u00A77", name, folder)); + } else { + ServerCloneController.Instance.addClone(compound, name, tab); + sendResult(sender, String.format("Added NPC \u00A7e%s\u00A77 to Tab \u00A7b%d\u00A77", name, tab)); + } } } } @SubCommand( desc = "List NPC from clone storage", - usage = "", + usage = "", permission = 2 ) public void list(ICommandSender sender, String[] args) { sendMessage(sender, "--- Stored NPCs --- (server side)"); - int tab = 0; + int tab = -1; + String folder = null; try { tab = Integer.parseInt(args[1]); - if (tab < 0 || tab > 15) { + if (tab < 1 || tab > 15) { sendError(sender, "Tab must be within 1-15"); return; } } catch (NumberFormatException ex) { - sendError(sender, String.format("Tab is not a number: %s", args[1])); - return; + folder = args[1]; + if (ServerCloneController.Instance == null || !ServerCloneController.Instance.hasFolder(folder)) { + sendError(sender, String.format("Unknown folder: %s", folder)); + return; + } } - for (String name : ServerCloneController.Instance.getClones(tab)) { + List clones; + if (folder != null) { + clones = ServerCloneController.Instance.getClones(folder); + } else { + clones = ServerCloneController.Instance.getClones(tab); + } + for (String name : clones) { sendMessage(sender, name); } sendMessage(sender, "------------------------------------"); @@ -97,59 +117,75 @@ public class CloneCommand extends CommandKamkeelBase { @SubCommand( desc = "Remove NPC from clone storage", - usage = " ", + usage = " ", permission = 4 ) public void del(ICommandSender sender, String[] args) throws CommandException { String nametodel = args[0]; - int tab = 0; + int tab = -1; + String folder = null; try { tab = Integer.parseInt(args[1]); - if (tab < 0 || tab > 15) { - sendError(sender, String.format("Tab must be within 1-15")); + if (tab < 1 || tab > 15) { + sendError(sender, "Tab must be within 1-15"); return; } } catch (NumberFormatException ex) { - sendError(sender, String.format("Tab is not a number: %s", args[1])); - return; - } - - boolean deleted = false; - for (String name : ServerCloneController.Instance.getClones(tab)) { - if (nametodel.equalsIgnoreCase(name)) { - ServerCloneController.Instance.removeClone(name, tab); - deleted = true; - break; + folder = args[1]; + if (ServerCloneController.Instance == null || !ServerCloneController.Instance.hasFolder(folder)) { + sendError(sender, String.format("Unknown folder: %s", folder)); + return; } } - if (!ServerCloneController.Instance.removeClone(nametodel, tab)) { + + boolean success; + if (folder != null) { + success = ServerCloneController.Instance.removeClone(nametodel, folder); + } else { + success = ServerCloneController.Instance.removeClone(nametodel, tab); + } + + if (!success) { sendError(sender, String.format("NPC '%s' was not found", nametodel)); } else { - sendResult(sender, String.format("Removed NPC \u00A7e%s\u00A77 from Tab \u00A7b%d\u00A77", nametodel, tab)); + if (folder != null) { + sendResult(sender, String.format("Removed NPC \u00A7e%s\u00A77 from Folder \u00A7b%s\u00A77", nametodel, folder)); + } else { + sendResult(sender, String.format("Removed NPC \u00A7e%s\u00A77 from Tab \u00A7b%d\u00A77", nametodel, tab)); + } } } @SubCommand( desc = "Spawn cloned NPC", - usage = " [[world:]x,y,z]] [newname]", + usage = " [[world:]x,y,z]] [newname]", permission = 2 ) public void spawn(ICommandSender sender, String[] args) throws CommandException { String name = args[0].replaceAll("%", " "); // if name of npc separed by space, user must use % in place of space - int tab = 0; + int tab = -1; + String folder = null; try { tab = Integer.parseInt(args[1]); - if (tab < 0 || tab > 15) { - sendError(sender, String.format("Tab must be within 1-15")); + if (tab < 1 || tab > 15) { + sendError(sender, "Tab must be within 1-15"); return; } } catch (NumberFormatException ex) { - sendError(sender, String.format("Tab is not a number: %s", args[1])); - return; + folder = args[1]; + if (ServerCloneController.Instance == null || !ServerCloneController.Instance.hasFolder(folder)) { + sendError(sender, String.format("Unknown folder: %s", folder)); + return; + } } String newname = null; - NBTTagCompound compound = ServerCloneController.Instance.getCloneData(sender, name, tab); + NBTTagCompound compound; + if (folder != null) { + compound = ServerCloneController.Instance.getCloneData(sender, name, folder); + } else { + compound = ServerCloneController.Instance.getCloneData(sender, name, tab); + } if (compound == null) { sendError(sender, "Unknown npc"); return; @@ -211,22 +247,26 @@ public class CloneCommand extends CommandKamkeelBase { } @SubCommand( - desc = "Spawn cloned NPC", - usage = " [[world:]x,y,z]] [newname]", + desc = "Spawn cloned NPC in a grid", + usage = " [[world:]x,y,z]] [newname]", permission = 2 ) public void grid(ICommandSender sender, String[] args) throws CommandException { String name = args[0].replaceAll("%", " "); // if name of npc separed by space, user must use % in place of space - int tab = 0; + int tab = -1; + String folder = null; try { tab = Integer.parseInt(args[1]); - if (tab < 0 || tab > 15) { - sendError(sender, String.format("Tab must be within 1-15")); + if (tab < 1 || tab > 15) { + sendError(sender, "Tab must be within 1-15"); return; } } catch (NumberFormatException ex) { - sendError(sender, String.format("Tab is not a number: %s", args[1])); - return; + folder = args[1]; + if (ServerCloneController.Instance == null || !ServerCloneController.Instance.hasFolder(folder)) { + sendError(sender, String.format("Unknown folder: %s", folder)); + return; + } } int width, height; @@ -238,9 +278,13 @@ public class CloneCommand extends CommandKamkeelBase { return; } - String newname = null; - NBTTagCompound compound = ServerCloneController.Instance.getCloneData(sender, name, tab); + NBTTagCompound compound; + if (folder != null) { + compound = ServerCloneController.Instance.getCloneData(sender, name, folder); + } else { + compound = ServerCloneController.Instance.getCloneData(sender, name, tab); + } if (compound == null) { sendError(sender, "Unknown npc"); return; @@ -317,6 +361,163 @@ public class CloneCommand extends CommandKamkeelBase { return; } + @SubCommand( + desc = "List all custom clone folders", + usage = "", + permission = 2 + ) + public void listfolders(ICommandSender sender, String[] args) { + if (ServerCloneController.Instance == null) { + sendError(sender, "Folder system not initialized"); + return; + } + sendMessage(sender, "--- Clone Folders ---"); + for (CloneFolder f : ServerCloneController.Instance.getFolderList()) { + sendMessage(sender, f.name); + } + sendMessage(sender, "------------------------------------"); + } + + @SubCommand( + desc = "Create a custom clone folder", + usage = "", + permission = 4 + ) + public void createfolder(ICommandSender sender, String[] args) { + if (ServerCloneController.Instance == null) { + sendError(sender, "Folder system not initialized"); + return; + } + String name = args[0]; + if (!CloneFolder.isValidName(name)) { + sendError(sender, String.format("Invalid folder name: %s", name)); + return; + } + if (ServerCloneController.Instance.hasFolder(name)) { + sendError(sender, String.format("Folder '%s' already exists", name)); + return; + } + CloneFolder created = ServerCloneController.Instance.createFolder(name); + if (created != null) { + sendResult(sender, String.format("Created folder \u00A7b%s", name)); + } else { + sendError(sender, String.format("Failed to create folder '%s'", name)); + } + } + + @SubCommand( + desc = "Rename a custom clone folder", + usage = " ", + permission = 4 + ) + public void renamefolder(ICommandSender sender, String[] args) { + if (ServerCloneController.Instance == null) { + sendError(sender, "Folder system not initialized"); + return; + } + String oldName = args[0]; + String newName = args[1]; + if (!ServerCloneController.Instance.hasFolder(oldName)) { + sendError(sender, String.format("Unknown folder: %s", oldName)); + return; + } + if (!CloneFolder.isValidName(newName)) { + sendError(sender, String.format("Invalid folder name: %s", newName)); + return; + } + if (ServerCloneController.Instance.renameFolder(oldName, newName)) { + sendResult(sender, String.format("Renamed folder \u00A7b%s\u00A77 to \u00A7b%s", oldName, newName)); + } else { + sendError(sender, String.format("Failed to rename folder '%s'", oldName)); + } + } + + @SubCommand( + desc = "Delete an empty custom clone folder", + usage = "", + permission = 4 + ) + public void deletefolder(ICommandSender sender, String[] args) { + if (ServerCloneController.Instance == null) { + sendError(sender, "Folder system not initialized"); + return; + } + String name = args[0]; + if (!ServerCloneController.Instance.hasFolder(name)) { + sendError(sender, String.format("Unknown folder: %s", name)); + return; + } + if (ServerCloneController.Instance.deleteFolder(name)) { + sendResult(sender, String.format("Deleted folder \u00A7b%s", name)); + } else { + sendError(sender, String.format("Failed to delete folder '%s' (folder must be empty)", name)); + } + } + + @SubCommand( + desc = "Move a clone between tabs/folders", + usage = " ", + permission = 4 + ) + public void move(ICommandSender sender, String[] args) { + if (ServerCloneController.Instance == null) { + sendError(sender, "Folder system not initialized"); + return; + } + String cloneName = args[0]; + + int fromTab = -1; + String fromFolder = null; + try { + fromTab = Integer.parseInt(args[1]); + if (fromTab < 1 || fromTab > 15) { + sendError(sender, "Source tab must be within 1-15"); + return; + } + } catch (NumberFormatException ex) { + fromFolder = args[1]; + if (!ServerCloneController.Instance.hasFolder(fromFolder)) { + sendError(sender, String.format("Unknown source folder: %s", fromFolder)); + return; + } + } + + int toTab = -1; + String toFolder = null; + try { + toTab = Integer.parseInt(args[2]); + if (toTab < 1 || toTab > 15) { + sendError(sender, "Destination tab must be within 1-15"); + return; + } + } catch (NumberFormatException ex) { + toFolder = args[2]; + if (!ServerCloneController.Instance.hasFolder(toFolder)) { + sendError(sender, String.format("Unknown destination folder: %s", toFolder)); + return; + } + } + + boolean success; + if (fromFolder != null && toFolder != null) { + success = ServerCloneController.Instance.moveClone(cloneName, fromFolder, toFolder); + } else if (fromFolder != null) { + success = ServerCloneController.Instance.moveClone(cloneName, fromFolder, toTab); + } else if (toFolder != null) { + success = ServerCloneController.Instance.moveClone(cloneName, fromTab, toFolder); + } else { + success = ServerCloneController.Instance.moveClone(cloneName, fromTab, toTab); + } + + if (success) { + String from = fromFolder != null ? fromFolder : String.valueOf(fromTab); + String to = toFolder != null ? toFolder : String.valueOf(toTab); + sendResult(sender, String.format("Moved \u00A7e%s\u00A77 from \u00A7b%s\u00A77 to \u00A7b%s", cloneName, from, to)); + } else { + sendError(sender, String.format("Failed to move '%s'", cloneName)); + } + } + public World getWorld(String t) { WorldServer[] ws = MinecraftServer.getServer().worldServers; for (WorldServer w : ws) { diff --git a/src/main/java/kamkeel/npcs/network/PacketHandler.java b/src/main/java/kamkeel/npcs/network/PacketHandler.java index 63ab9b192..bcfd18abd 100644 --- a/src/main/java/kamkeel/npcs/network/PacketHandler.java +++ b/src/main/java/kamkeel/npcs/network/PacketHandler.java @@ -108,6 +108,9 @@ import kamkeel.npcs.network.packets.request.bank.BankSavePacket; import kamkeel.npcs.network.packets.request.bank.BanksGetPacket; import kamkeel.npcs.network.packets.request.clone.CloneAllTagsPacket; import kamkeel.npcs.network.packets.request.clone.CloneAllTagsShortPacket; +import kamkeel.npcs.network.packets.request.clone.CloneFolderCrudPacket; +import kamkeel.npcs.network.packets.request.clone.CloneFolderListPacket; +import kamkeel.npcs.network.packets.request.clone.CloneMovePacket; import kamkeel.npcs.network.packets.request.clone.CloneListPacket; import kamkeel.npcs.network.packets.request.clone.ClonePreSavePacket; import kamkeel.npcs.network.packets.request.clone.CloneRemovePacket; @@ -325,6 +328,9 @@ public class PacketHandler { REQUEST_PACKET.registerPacket(new CloneTagListPacket()); REQUEST_PACKET.registerPacket(new CloneAllTagsPacket()); REQUEST_PACKET.registerPacket(new CloneAllTagsShortPacket()); + REQUEST_PACKET.registerPacket(new CloneFolderListPacket()); + REQUEST_PACKET.registerPacket(new CloneFolderCrudPacket()); + REQUEST_PACKET.registerPacket(new CloneMovePacket()); // Linked Packets diff --git a/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java b/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java index 8183941d0..2f3e6ea94 100644 --- a/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java +++ b/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java @@ -163,6 +163,9 @@ public enum EnumRequestPacket { CloneAllTags, CloneAllTagsShort, ClonePreSave, + CloneFolderList, + CloneFolderCrud, + CloneMove, EventScript, PlayerScript, diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneFolderCrudPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneFolderCrudPacket.java new file mode 100644 index 000000000..afaa5e4ed --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneFolderCrudPacket.java @@ -0,0 +1,109 @@ +package kamkeel.npcs.network.packets.request.clone; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +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 net.minecraft.nbt.NBTTagList; +import noppes.npcs.CustomNpcsPermissions; +import noppes.npcs.controllers.ServerCloneController; +import noppes.npcs.controllers.data.CloneFolder; + +import java.io.IOException; + +public final class CloneFolderCrudPacket extends AbstractPacket { + public static String packetName = "Request|CloneFolderCrud"; + + public static final byte ACTION_CREATE = 0; + public static final byte ACTION_RENAME = 1; + public static final byte ACTION_DELETE = 2; + + private byte action; + private String name; + private String newName; + + public CloneFolderCrudPacket() { + } + + public CloneFolderCrudPacket(byte action, String name) { + this.action = action; + this.name = name; + this.newName = ""; + } + + public CloneFolderCrudPacket(byte action, String name, String newName) { + this.action = action; + this.name = name; + this.newName = newName; + } + + @Override + public Enum getType() { + return EnumRequestPacket.CloneFolderCrud; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.REQUEST_PACKET; + } + + @Override + public CustomNpcsPermissions.Permission getPermission() { + return CustomNpcsPermissions.NPC_CLONE; + } + + @SideOnly(Side.CLIENT) + @Override + public void sendData(ByteBuf out) throws IOException { + out.writeByte(action); + ByteBufUtils.writeString(out, name); + if (action == ACTION_RENAME) { + ByteBufUtils.writeString(out, newName); + } + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + if (!(player instanceof EntityPlayerMP)) + return; + if (!PacketUtil.verifyItemPacket(packetName, player, EnumItemPacketType.CLONER)) + return; + + byte action = in.readByte(); + String name = ByteBufUtils.readString(in); + + boolean success = false; + switch (action) { + case ACTION_CREATE: + success = ServerCloneController.Instance.createFolder(name) != null; + break; + case ACTION_RENAME: + String newName = ByteBufUtils.readString(in); + success = ServerCloneController.Instance.renameFolder(name, newName); + break; + case ACTION_DELETE: + success = ServerCloneController.Instance.deleteFolder(name); + break; + } + + NBTTagCompound compound = new NBTTagCompound(); + compound.setBoolean("Success", success); + NBTTagList folderList = new NBTTagList(); + for (CloneFolder folder : ServerCloneController.Instance.getFolderList()) { + folderList.appendTag(folder.writeNBT(new NBTTagCompound())); + } + compound.setTag("CloneFolders", folderList); + + GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); + } +} diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneFolderListPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneFolderListPacket.java new file mode 100644 index 000000000..b7e7fe9f8 --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneFolderListPacket.java @@ -0,0 +1,59 @@ +package kamkeel.npcs.network.packets.request.clone; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +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 net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import noppes.npcs.controllers.ServerCloneController; +import noppes.npcs.controllers.data.CloneFolder; + +import java.io.IOException; + +public final class CloneFolderListPacket extends AbstractPacket { + public static String packetName = "Request|CloneFolderList"; + + public CloneFolderListPacket() { + } + + @Override + public Enum getType() { + return EnumRequestPacket.CloneFolderList; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.REQUEST_PACKET; + } + + @SideOnly(Side.CLIENT) + @Override + public void sendData(ByteBuf out) throws IOException { + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + if (!(player instanceof EntityPlayerMP)) + return; + if (!PacketUtil.verifyItemPacket(packetName, player, EnumItemPacketType.CLONER)) + return; + + NBTTagCompound compound = new NBTTagCompound(); + NBTTagList folderList = new NBTTagList(); + for (CloneFolder folder : ServerCloneController.Instance.getFolderList()) { + folderList.appendTag(folder.writeNBT(new NBTTagCompound())); + } + compound.setTag("CloneFolders", folderList); + + GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); + } +} diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneListPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneListPacket.java index 87bac4a29..e6ce35f40 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneListPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneListPacket.java @@ -23,9 +23,16 @@ public final class CloneListPacket extends AbstractPacket { public static String packetName = "Request|CloneList"; private int tab; + private String folderName; public CloneListPacket(int tab) { this.tab = tab; + this.folderName = null; + } + + public CloneListPacket(String folderName) { + this.tab = -1; + this.folderName = folderName; } public CloneListPacket() { @@ -45,6 +52,9 @@ public final class CloneListPacket extends AbstractPacket { @Override public void sendData(ByteBuf out) throws IOException { out.writeInt(tab); + if (tab == -1) { + kamkeel.npcs.util.ByteBufUtils.writeString(out, folderName); + } } @Override @@ -54,14 +64,26 @@ public final class CloneListPacket extends AbstractPacket { if (!PacketUtil.verifyItemPacket(packetName, player, EnumItemPacketType.MOUNTER, EnumItemPacketType.CLONER)) return; - NBTTagList list = new NBTTagList(); int tab = in.readInt(); - for (String name : ServerCloneController.Instance.getClones(tab)) - list.appendTag(new NBTTagString(name)); + String folder = null; + if (tab == -1) { + folder = kamkeel.npcs.util.ByteBufUtils.readString(in); + } + NBTTagList list = new NBTTagList(); NBTTagList listDate = new NBTTagList(); - for (String name : ServerCloneController.Instance.getClonesDate(tab)) - listDate.appendTag(new NBTTagString(name)); + + if (folder != null) { + for (String name : ServerCloneController.Instance.getClones(folder)) + list.appendTag(new NBTTagString(name)); + for (String name : ServerCloneController.Instance.getClonesDate(folder)) + listDate.appendTag(new NBTTagString(name)); + } else { + for (String name : ServerCloneController.Instance.getClones(tab)) + list.appendTag(new NBTTagString(name)); + for (String name : ServerCloneController.Instance.getClonesDate(tab)) + listDate.appendTag(new NBTTagString(name)); + } NBTTagCompound compound = new NBTTagCompound(); compound.setTag("List", list); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneMovePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneMovePacket.java new file mode 100644 index 000000000..124bd5204 --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneMovePacket.java @@ -0,0 +1,97 @@ +package kamkeel.npcs.network.packets.request.clone; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +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 noppes.npcs.CustomNpcsPermissions; +import noppes.npcs.controllers.ServerCloneController; + +import java.io.IOException; + +public final class CloneMovePacket extends AbstractPacket { + public static String packetName = "Request|CloneMove"; + + private String cloneName; + private int fromTab; + private String fromFolder; + private int toTab; + private String toFolder; + + public CloneMovePacket() { + } + + public CloneMovePacket(String cloneName, int fromTab, String fromFolder, int toTab, String toFolder) { + this.cloneName = cloneName; + this.fromTab = fromTab; + this.fromFolder = fromFolder; + this.toTab = toTab; + this.toFolder = toFolder; + } + + @Override + public Enum getType() { + return EnumRequestPacket.CloneMove; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.REQUEST_PACKET; + } + + @Override + public CustomNpcsPermissions.Permission getPermission() { + return CustomNpcsPermissions.NPC_CLONE; + } + + @SideOnly(Side.CLIENT) + @Override + public void sendData(ByteBuf out) throws IOException { + ByteBufUtils.writeString(out, cloneName); + out.writeInt(fromTab); + ByteBufUtils.writeString(out, fromFolder != null ? fromFolder : ""); + out.writeInt(toTab); + ByteBufUtils.writeString(out, toFolder != null ? toFolder : ""); + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + if (!(player instanceof EntityPlayerMP)) + return; + if (!PacketUtil.verifyItemPacket(packetName, player, EnumItemPacketType.CLONER)) + return; + + String cloneName = ByteBufUtils.readString(in); + int fromTab = in.readInt(); + String fromFolder = ByteBufUtils.readString(in); + if (fromFolder.isEmpty()) fromFolder = null; + int toTab = in.readInt(); + String toFolder = ByteBufUtils.readString(in); + if (toFolder.isEmpty()) toFolder = null; + + boolean success = false; + if (fromFolder != null && toFolder != null) { + success = ServerCloneController.Instance.moveClone(cloneName, fromFolder, toFolder); + } else if (fromFolder != null) { + success = ServerCloneController.Instance.moveClone(cloneName, fromFolder, toTab); + } else if (toFolder != null) { + success = ServerCloneController.Instance.moveClone(cloneName, fromTab, toFolder); + } else { + success = ServerCloneController.Instance.moveClone(cloneName, fromTab, toTab); + } + + NBTTagCompound compound = new NBTTagCompound(); + compound.setBoolean("MoveSuccess", success); + GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); + } +} diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/ClonePreSavePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/ClonePreSavePacket.java index 378145ddb..4d36eb531 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/clone/ClonePreSavePacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/ClonePreSavePacket.java @@ -24,6 +24,7 @@ public final class ClonePreSavePacket extends AbstractPacket { private String name; private int tab; + private String folderName; public ClonePreSavePacket() { } @@ -31,6 +32,13 @@ public final class ClonePreSavePacket extends AbstractPacket { public ClonePreSavePacket(String name, int tab) { this.name = name; this.tab = tab; + this.folderName = null; + } + + public ClonePreSavePacket(String name, String folderName) { + this.name = name; + this.tab = -1; + this.folderName = folderName; } @Override @@ -53,6 +61,9 @@ public final class ClonePreSavePacket extends AbstractPacket { public void sendData(ByteBuf out) throws IOException { ByteBufUtils.writeString(out, this.name); out.writeInt(tab); + if (tab == -1) { + ByteBufUtils.writeString(out, this.folderName); + } } @Override @@ -62,7 +73,15 @@ public final class ClonePreSavePacket extends AbstractPacket { if (!PacketUtil.verifyItemPacket(packetName, player, EnumItemPacketType.CLONER)) return; - boolean bo = ServerCloneController.Instance.getCloneData(null, ByteBufUtils.readString(in), in.readInt()) != null; + String name = ByteBufUtils.readString(in); + int tab = in.readInt(); + boolean bo; + if (tab == -1) { + String folder = ByteBufUtils.readString(in); + bo = ServerCloneController.Instance.getCloneData(null, name, folder) != null; + } else { + bo = ServerCloneController.Instance.getCloneData(null, name, tab) != null; + } NBTTagCompound compound = new NBTTagCompound(); compound.setBoolean("NameExists", bo); GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneRemovePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneRemovePacket.java index ce1fcb367..96069f414 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneRemovePacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneRemovePacket.java @@ -25,6 +25,7 @@ public final class CloneRemovePacket extends AbstractPacket { public static String packetName = "Request|CloneRemove"; private int tab; + private String folderName; private String name; public CloneRemovePacket() { @@ -32,6 +33,13 @@ public final class CloneRemovePacket extends AbstractPacket { public CloneRemovePacket(int tab, String name) { this.tab = tab; + this.folderName = null; + this.name = name; + } + + public CloneRemovePacket(String folderName, String name) { + this.tab = -1; + this.folderName = folderName; this.name = name; } @@ -54,6 +62,9 @@ public final class CloneRemovePacket extends AbstractPacket { @Override public void sendData(ByteBuf out) throws IOException { out.writeInt(tab); + if (tab == -1) { + ByteBufUtils.writeString(out, this.folderName); + } ByteBufUtils.writeString(out, this.name); } @@ -65,16 +76,28 @@ public final class CloneRemovePacket extends AbstractPacket { return; int tab = in.readInt(); - ServerCloneController.Instance.removeClone(ByteBufUtils.readString(in), tab); + String folder = null; + if (tab == -1) { + folder = ByteBufUtils.readString(in); + } + String cloneName = ByteBufUtils.readString(in); NBTTagList list = new NBTTagList(); - - for (String name : ServerCloneController.Instance.getClones(tab)) - list.appendTag(new NBTTagString(name)); - NBTTagList listDate = new NBTTagList(); - for (String name : ServerCloneController.Instance.getClonesDate(tab)) - listDate.appendTag(new NBTTagString(name)); + + if (folder != null) { + ServerCloneController.Instance.removeClone(cloneName, folder); + for (String name : ServerCloneController.Instance.getClones(folder)) + list.appendTag(new NBTTagString(name)); + for (String name : ServerCloneController.Instance.getClonesDate(folder)) + listDate.appendTag(new NBTTagString(name)); + } else { + ServerCloneController.Instance.removeClone(cloneName, tab); + for (String name : ServerCloneController.Instance.getClones(tab)) + list.appendTag(new NBTTagString(name)); + for (String name : ServerCloneController.Instance.getClonesDate(tab)) + listDate.appendTag(new NBTTagString(name)); + } NBTTagCompound compound = new NBTTagCompound(); compound.setTag("List", list); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneSavePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneSavePacket.java index 3a8abd9c0..2c66572e3 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneSavePacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneSavePacket.java @@ -25,6 +25,7 @@ public final class CloneSavePacket extends AbstractPacket { private String name; private int tab; + private String folderName; private NBTTagCompound tagExtra; private NBTTagCompound tagCompound; @@ -34,6 +35,15 @@ public final class CloneSavePacket extends AbstractPacket { public CloneSavePacket(String name, int tab, NBTTagCompound tagExtra, NBTTagCompound tagCompound) { this.name = name; this.tab = tab; + this.folderName = null; + this.tagExtra = tagExtra; + this.tagCompound = tagCompound; + } + + public CloneSavePacket(String name, String folderName, NBTTagCompound tagExtra, NBTTagCompound tagCompound) { + this.name = name; + this.tab = -1; + this.folderName = folderName; this.tagExtra = tagExtra; this.tagCompound = tagCompound; } @@ -58,6 +68,9 @@ public final class CloneSavePacket extends AbstractPacket { public void sendData(ByteBuf out) throws IOException { ByteBufUtils.writeString(out, this.name); out.writeInt(tab); + if (tab == -1) { + ByteBufUtils.writeString(out, this.folderName); + } ByteBufUtils.writeNBT(out, this.tagExtra); ByteBufUtils.writeNBT(out, this.tagCompound); } @@ -75,11 +88,20 @@ public final class CloneSavePacket extends AbstractPacket { String name = ByteBufUtils.readString(in); int tab = in.readInt(); + String folder = null; + if (tab == -1) { + folder = ByteBufUtils.readString(in); + } NBTTagCompound tagExtra = ByteBufUtils.readNBT(in); NBTTagCompound tagCompound = ByteBufUtils.readNBT(in); NBTTagList tagList = tagCompound.getTagList("TagUUIDs", 8); data.cloned.setTag("TagUUIDs", tagList); - ServerCloneController.Instance.addClone(data.cloned, name, tab, tagExtra); + + if (folder != null) { + ServerCloneController.Instance.addClone(data.cloned, name, folder, tagExtra); + } else { + ServerCloneController.Instance.addClone(data.cloned, name, tab, tagExtra); + } } } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneTagListPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneTagListPacket.java index 4d7b21aac..926c5519c 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneTagListPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/CloneTagListPacket.java @@ -13,6 +13,7 @@ import kamkeel.npcs.network.packets.data.large.GuiDataPacket; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; +import kamkeel.npcs.util.ByteBufUtils; import noppes.npcs.controllers.ServerTagMapController; import noppes.npcs.controllers.data.TagMap; @@ -22,9 +23,16 @@ public final class CloneTagListPacket extends AbstractPacket { public static String packetName = "Request|CloneTagList"; private int tab; + private String folderName; public CloneTagListPacket(int tab) { this.tab = tab; + this.folderName = null; + } + + public CloneTagListPacket(String folderName) { + this.tab = -1; + this.folderName = folderName; } public CloneTagListPacket() { @@ -44,6 +52,9 @@ public final class CloneTagListPacket extends AbstractPacket { @Override public void sendData(ByteBuf out) throws IOException { out.writeInt(tab); + if (tab == -1) { + ByteBufUtils.writeString(out, folderName); + } } @Override @@ -54,7 +65,13 @@ public final class CloneTagListPacket extends AbstractPacket { return; int tab = in.readInt(); - TagMap tagMap = ServerTagMapController.Instance.getTagMap(tab); + TagMap tagMap; + if (tab == -1) { + String folder = ByteBufUtils.readString(in); + tagMap = ServerTagMapController.Instance.getTagMap(folder); + } else { + tagMap = ServerTagMapController.Instance.getTagMap(tab); + } NBTTagCompound compound = new NBTTagCompound(); compound.setTag("CloneTags", tagMap.writeNBT()); GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/MobSpawnerPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/MobSpawnerPacket.java index cc4dfb3ed..0ad817b06 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/clone/MobSpawnerPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/MobSpawnerPacket.java @@ -31,6 +31,7 @@ public class MobSpawnerPacket extends AbstractPacket { private String selectedName; private int tab; + private String folderName; private NBTTagCompound compound; @@ -44,6 +45,17 @@ public class MobSpawnerPacket extends AbstractPacket { this.posz = posz; this.selectedName = selectedName; this.tab = tab; + this.folderName = null; + } + + public MobSpawnerPacket(Action type, int posX, int posY, int posz, String selectedName, String folderName) { + this.type = type; + this.posX = posX; + this.posY = posY; + this.posz = posz; + this.selectedName = selectedName; + this.tab = -1; + this.folderName = folderName; } public MobSpawnerPacket(Action type, int posX, int posY, int posz, NBTTagCompound compound) { @@ -58,6 +70,10 @@ public class MobSpawnerPacket extends AbstractPacket { PacketClient.sendClient(new MobSpawnerPacket(Action.Server, x, y, z, name, tab)); } + public static void ServerFolder(int x, int y, int z, String name, String folderName) { + PacketClient.sendClient(new MobSpawnerPacket(Action.Server, x, y, z, name, folderName)); + } + public static void Client(int x, int y, int z, NBTTagCompound compound) { PacketClient.sendClient(new MobSpawnerPacket(Action.Client, x, y, z, compound)); } @@ -88,6 +104,9 @@ public class MobSpawnerPacket extends AbstractPacket { if (type == Action.Server) { ByteBufUtils.writeString(out, this.selectedName); out.writeInt(this.tab); + if (this.tab == -1) { + ByteBufUtils.writeString(out, this.folderName); + } } else { ByteBufUtils.writeNBT(out, this.compound); } @@ -107,10 +126,18 @@ public class MobSpawnerPacket extends AbstractPacket { int z = in.readInt(); NBTTagCompound compound; - if (requestedAction == Action.Server) - compound = ServerCloneController.Instance.getCloneData(player, ByteBufUtils.readString(in), in.readInt()); - else + if (requestedAction == Action.Server) { + String name = ByteBufUtils.readString(in); + int tab = in.readInt(); + if (tab == -1) { + String folder = ByteBufUtils.readString(in); + compound = ServerCloneController.Instance.getCloneData(player, name, folder); + } else { + compound = ServerCloneController.Instance.getCloneData(player, name, tab); + } + } else { compound = ByteBufUtils.readNBT(in); + } if (!ConfigScript.canScript(player, CustomNpcsPermissions.SCRIPT)) { return; diff --git a/src/main/java/kamkeel/npcs/network/packets/request/clone/SpawnMobPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/clone/SpawnMobPacket.java index b83fb9286..8afde02cf 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/clone/SpawnMobPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/clone/SpawnMobPacket.java @@ -37,6 +37,7 @@ public class SpawnMobPacket extends AbstractPacket { private String selectedName; private int tab; + private String folderName; private NBTTagCompound compound; @@ -50,6 +51,17 @@ public class SpawnMobPacket extends AbstractPacket { this.posz = posz; this.selectedName = selectedName; this.tab = tab; + this.folderName = null; + } + + public SpawnMobPacket(Action type, int posX, int posY, int posz, String selectedName, String folderName) { + this.type = type; + this.posX = posX; + this.posY = posY; + this.posz = posz; + this.selectedName = selectedName; + this.tab = -1; + this.folderName = folderName; } public SpawnMobPacket(Action type, int posX, int posY, int posz, NBTTagCompound compound) { @@ -64,6 +76,10 @@ public class SpawnMobPacket extends AbstractPacket { PacketClient.sendClient(new SpawnMobPacket(Action.Server, x, y, z, name, tab)); } + public static void ServerFolder(int x, int y, int z, String name, String folderName) { + PacketClient.sendClient(new SpawnMobPacket(Action.Server, x, y, z, name, folderName)); + } + public static void Client(int x, int y, int z, NBTTagCompound compound) { PacketClient.sendClient(new SpawnMobPacket(Action.Client, x, y, z, compound)); } @@ -94,6 +110,9 @@ public class SpawnMobPacket extends AbstractPacket { if (type == Action.Server) { ByteBufUtils.writeString(out, this.selectedName); out.writeInt(this.tab); + if (this.tab == -1) { + ByteBufUtils.writeString(out, this.folderName); + } } else { ByteBufUtils.writeNBT(out, this.compound); } @@ -113,10 +132,18 @@ public class SpawnMobPacket extends AbstractPacket { int z = in.readInt(); NBTTagCompound compound; - if (requestedAction == Action.Server) - compound = ServerCloneController.Instance.getCloneData(player, ByteBufUtils.readString(in), in.readInt()); - else + if (requestedAction == Action.Server) { + String name = ByteBufUtils.readString(in); + int tab = in.readInt(); + if (tab == -1) { + String folder = ByteBufUtils.readString(in); + compound = ServerCloneController.Instance.getCloneData(player, name, folder); + } else { + compound = ServerCloneController.Instance.getCloneData(player, name, tab); + } + } else { compound = ByteBufUtils.readNBT(in); + } if (compound == null) return; diff --git a/src/main/java/noppes/npcs/client/ClientProxy.java b/src/main/java/noppes/npcs/client/ClientProxy.java index bb90e0659..3bc05aa41 100644 --- a/src/main/java/noppes/npcs/client/ClientProxy.java +++ b/src/main/java/noppes/npcs/client/ClientProxy.java @@ -334,7 +334,6 @@ public class ClientProxy extends CommonProxy { ClientCloneController.Instance = new ClientCloneController(); ClientTagMapController.Instance = new ClientTagMapController(); - MinecraftForge.EVENT_BUS.register(new ClientEventHandler()); // Telegraph rendering system diff --git a/src/main/java/noppes/npcs/client/controllers/ClientCloneController.java b/src/main/java/noppes/npcs/client/controllers/ClientCloneController.java index 81f174170..9e62177f0 100644 --- a/src/main/java/noppes/npcs/client/controllers/ClientCloneController.java +++ b/src/main/java/noppes/npcs/client/controllers/ClientCloneController.java @@ -41,6 +41,26 @@ public class ClientCloneController extends ServerCloneController { return true; } + @Override + public boolean addToTagMap(NBTTagCompound nbttagcompound, String name, String folderName) { + HashSet tagUUIDs = new HashSet<>(); + if (nbttagcompound.hasKey("TagUUIDs")) { + NBTTagList nbtTagList = nbttagcompound.getTagList("TagUUIDs", 8); + for (int i = 0; i < nbtTagList.tagCount(); i++) { + tagUUIDs.add(UUID.fromString(nbtTagList.getStringTagAt(i))); + } + } + + TagMap tagMap = ClientTagMapController.Instance.getTagMap(folderName); + if (!tagUUIDs.isEmpty()) { + tagMap.putClone(name, tagUUIDs); + } else { + tagMap.removeClone(name); + } + ClientTagMapController.Instance.saveTagMap(tagMap); + return true; + } + @Override public boolean removeFromTagMap(String name, int tab) { TagMap tagMap = ClientTagMapController.Instance.getTagMap(tab); @@ -51,4 +71,43 @@ public class ClientCloneController extends ServerCloneController { return false; } + @Override + public boolean removeFromTagMap(String name, String folderName) { + TagMap tagMap = ClientTagMapController.Instance.getTagMap(folderName); + if (tagMap.removeClone(name)) { + ClientTagMapController.Instance.saveTagMap(tagMap); + return true; + } + return false; + } + + @Override + protected HashSet getTagUUIDsFromTab(String cloneName, int tab) { + TagMap tagMap = ClientTagMapController.Instance.getTagMap(tab); + HashSet uuids = tagMap.getUUIDs(cloneName); + return uuids != null ? new HashSet<>(uuids) : new HashSet<>(); + } + + @Override + protected HashSet getTagUUIDsFromFolder(String cloneName, String folderName) { + TagMap tagMap = ClientTagMapController.Instance.getTagMap(folderName); + HashSet uuids = tagMap.getUUIDs(cloneName); + return uuids != null ? new HashSet<>(uuids) : new HashSet<>(); + } + + @Override + protected void setTagUUIDsForTab(String cloneName, int tab, HashSet tagUUIDs) { + if (tagUUIDs == null || tagUUIDs.isEmpty()) return; + TagMap tagMap = ClientTagMapController.Instance.getTagMap(tab); + tagMap.putClone(cloneName, tagUUIDs); + ClientTagMapController.Instance.saveTagMap(tagMap); + } + + @Override + protected void setTagUUIDsForFolder(String cloneName, String folderName, HashSet tagUUIDs) { + if (tagUUIDs == null || tagUUIDs.isEmpty()) return; + TagMap tagMap = ClientTagMapController.Instance.getTagMap(folderName); + tagMap.putClone(cloneName, tagUUIDs); + ClientTagMapController.Instance.saveTagMap(tagMap); + } } diff --git a/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawner.java b/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawner.java index b3670d9e8..05174404d 100644 --- a/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawner.java +++ b/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawner.java @@ -2,7 +2,10 @@ package noppes.npcs.client.gui; import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.packets.request.clone.CloneAllTagsPacket; +import kamkeel.npcs.network.packets.request.clone.CloneFolderCrudPacket; +import kamkeel.npcs.network.packets.request.clone.CloneFolderListPacket; import kamkeel.npcs.network.packets.request.clone.CloneListPacket; +import kamkeel.npcs.network.packets.request.clone.CloneMovePacket; import kamkeel.npcs.network.packets.request.clone.CloneRemovePacket; import kamkeel.npcs.network.packets.request.clone.CloneTagListPacket; import kamkeel.npcs.network.packets.request.clone.MobSpawnerPacket; @@ -16,6 +19,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.StatCollector; import net.minecraft.world.World; +import noppes.npcs.client.NoppesUtil; import noppes.npcs.client.controllers.ClientCloneController; import noppes.npcs.client.controllers.ClientTagMapController; import noppes.npcs.client.gui.util.GuiCustomScroll; @@ -26,9 +30,16 @@ import noppes.npcs.client.gui.util.GuiNPCInterface; import noppes.npcs.client.gui.util.GuiNpcButton; import noppes.npcs.client.gui.util.GuiNpcLabel; import noppes.npcs.client.gui.util.GuiNpcTextField; +import noppes.npcs.client.gui.util.IClonerGui; +import noppes.npcs.client.gui.util.ICustomScrollListener; import noppes.npcs.client.gui.util.IGuiData; +import noppes.npcs.client.gui.util.ISubGuiListener; +import noppes.npcs.client.gui.util.SubGuiInterface; +import noppes.npcs.controllers.data.CloneFolder; import noppes.npcs.controllers.data.Tag; import noppes.npcs.controllers.data.TagMap; +import noppes.npcs.util.ValueUtil; +import org.lwjgl.input.Mouse; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -38,51 +49,81 @@ import java.util.List; import java.util.Map; import java.util.UUID; -public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { +public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData, ICustomScrollListener, ISubGuiListener, IClonerGui { + + // ==================== STATIC STATE (persists across GUI opens, shared with fullscreen) ==================== + private static int sidebarMode = 0; // 0=Tabs, 1=Folders + public static int activeTab = 1; + public static String activeFolder = null; + public static int showingClones = 0; // 0=Clones, 1=Entities, 2=Server, 3=Filters + public static String search = ""; + public static String tagSearch = ""; + public static HashMap tags = new HashMap<>(); public static HashMap tagNames = new HashMap<>(); public static HashSet tagFilters = new HashSet<>(); public static byte displayTags = 0; - // 0 - Any, 1 - All, 2 - Not Any, 3 - Not All public static byte filterCondition = 0; - public static byte ascending = 0; - // 0 - By Name, 1 - By Date public static byte sortType = 0; + // ==================== INSTANCE STATE ==================== public TagMap tagMap; - private final GuiCustomScrollCloner scroll = new GuiCustomScrollCloner(this, 0); private final GuiCustomScroll filterScroll = new GuiCustomScroll(this, 1); - private int posX, posY, posZ; - + public int posX, posY, posZ; private List list; private List rawList; private List tagList; + private List folderNames = new ArrayList<>(); - private static int showingClones = 0; - - private static String search = ""; - private static String tagSearch = ""; - - private int activeTab = 1; + // Scrollable folder side buttons (folders mode only) + private HashMap folderSideButtons = new HashMap<>(); + private float folderScroll = 0; + private float destFolderScroll = 0; + // ==================== CONSTRUCTOR ==================== public GuiNpcMobSpawner(int i, int j, int k) { super(); - xSize = 354; posX = i; posY = j; posZ = k; - - this.closeOnEsc = true; - + closeOnEsc = true; + xSize = 354; setBackground("menubg.png"); PacketClient.sendClient(new CloneAllTagsPacket()); + loadFolderNames(); } + private void loadFolderNames() { + folderNames.clear(); + if (ClientCloneController.Instance != null) { + for (CloneFolder folder : ClientCloneController.Instance.getFolderList()) { + folderNames.add(folder.name); + } + } + } + + // ==================== IClonerGui ==================== + @Override + public int getShowingClones() { + return showingClones; + } + + @Override + public HashMap getTags() { + return tags; + } + + @Override + public TagMap getTagMap() { + return tagMap; + } + + // ==================== INIT GUI ==================== public void initGui() { super.initGui(); guiTop += 10; @@ -92,6 +133,7 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { scroll.setSize(293, 188); scroll.guiLeft = guiLeft + 4; scroll.guiTop = guiTop + 26; + filterScroll.clear(); filterScroll.setSize(140, 166); filterScroll.guiLeft = guiLeft + 4; @@ -99,16 +141,18 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { filterScroll.multipleSelection = true; filterScroll.setSelectedList(tagFilters); - GuiMenuTopButton button; - addTopButton(button = new GuiMenuTopButton(3, guiLeft + 4, guiTop - 17, "spawner.clones")); - button.active = showingClones == 0; - addTopButton(button = new GuiMenuTopButton(5, button, "gui.server")); - button.active = showingClones == 2; - addTopButton(button = new GuiMenuTopButton(4, button, "spawner.entities")); - button.active = showingClones == 1; - addTopButton(button = new GuiMenuTopButton(16, guiLeft + (xSize - 67), guiTop - 17, "gui.filters")); - button.active = showingClones == 3; - addTopButton(button = new GuiMenuTopButton(17, guiLeft + (xSize - 22), guiTop - 17, "X")); + // Top buttons + GuiMenuTopButton btn; + addTopButton(btn = new GuiMenuTopButton(3, guiLeft + 4, guiTop - 17, "spawner.clones")); + btn.active = showingClones == 0; + addTopButton(btn = new GuiMenuTopButton(5, btn, "gui.server")); + btn.active = showingClones == 2; + addTopButton(btn = new GuiMenuTopButton(4, btn, "spawner.entities")); + btn.active = showingClones == 1; + addTopButton(btn = new GuiMenuTopButton(16, guiLeft + (xSize - 82), guiTop - 17, "gui.filters")); + btn.active = showingClones == 3; + addTopButton(new GuiMenuTopButton(61, guiLeft + (xSize - 37), guiTop - 17, "+")); + addTopButton(new GuiMenuTopButton(17, guiLeft + (xSize - 22), guiTop - 17, "X")); if (showingClones < 3) { addScroll(scroll); @@ -117,95 +161,136 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { addButton(new GuiNpcButton(2, guiLeft + 298, guiTop + 140, 52, 20, "spawner.mobspawner")); if (showingClones == 0 || showingClones == 2) { - - addSideButton(new GuiMenuSideButton(21, guiLeft - 70, this.guiTop + 2, 70, 22, "1")); - addSideButton(new GuiMenuSideButton(22, guiLeft - 70, this.guiTop + 23, 70, 22, "2")); - addSideButton(new GuiMenuSideButton(23, guiLeft - 70, this.guiTop + 44, 70, 22, "3")); - addSideButton(new GuiMenuSideButton(24, guiLeft - 70, this.guiTop + 65, 70, 22, "4")); - addSideButton(new GuiMenuSideButton(25, guiLeft - 70, this.guiTop + 86, 70, 22, "5")); - addSideButton(new GuiMenuSideButton(26, guiLeft - 70, this.guiTop + 107, 35, 22, "6")); - addSideButton(new GuiMenuSideButton(27, guiLeft - 35, this.guiTop + 107, 35, 22, "7")); - addSideButton(new GuiMenuSideButton(28, guiLeft - 70, this.guiTop + 128, 35, 22, "8")); - addSideButton(new GuiMenuSideButton(29, guiLeft - 35, this.guiTop + 128, 35, 22, "9")); - addSideButton(new GuiMenuSideButton(30, guiLeft - 70, this.guiTop + 149, 35, 22, "10")); - addSideButton(new GuiMenuSideButton(31, guiLeft - 35, this.guiTop + 149, 35, 22, "11")); - addSideButton(new GuiMenuSideButton(32, guiLeft - 70, this.guiTop + 170, 35, 22, "12")); - addSideButton(new GuiMenuSideButton(33, guiLeft - 35, this.guiTop + 170, 35, 22, "13")); - addSideButton(new GuiMenuSideButton(34, guiLeft - 70, this.guiTop + 191, 35, 22, "14")); - addSideButton(new GuiMenuSideButton(35, guiLeft - 35, this.guiTop + 191, 35, 22, "15")); - addButton(new GuiNpcButton(6, guiLeft + 298, guiTop + 190, 52, 20, "gui.remove")); - getSideButton(20 + activeTab).active = true; + // Tabs/Folders toggle above side buttons + addButton(new GuiNpcButton(60, guiLeft - 70, guiTop - 18, 70, 16, + new String[]{"Tabs", "Folders"}, sidebarMode)); + + if (sidebarMode == 0) { + initTabSideButtons(); + } else { + initFolderSideButtons(); + } + showClones(); } else { showEntities(); } } else { - // Show Filters - addLabel(new GuiNpcLabel(1, StatCollector.translateToLocal("cloner.tagFilters"), guiLeft + 7, guiTop + 7)); - addScroll(filterScroll); - addTextField(new GuiNpcTextField(2, this, fontRendererObj, guiLeft + 4, guiTop + 190, 140, 20, tagSearch)); - - addLabel(new GuiNpcLabel(2, StatCollector.translateToLocal("cloner.tagVisibility"), guiLeft + 150, guiTop + 27)); - addButton(new GuiNpcButton(12, guiLeft + 215, guiTop + 20, new String[]{"display.show", "display.all", "display.hide"}, displayTags)); - - addLabel(new GuiNpcLabel(3, StatCollector.translateToLocal("filter.contains"), guiLeft + 150, guiTop + 50)); - addButton(new GuiNpcButton(13, guiLeft + 215, guiTop + 43, new String[]{"filter.any", "filter.all", "filter.notany", "filter.notall"}, filterCondition)); - - addButton(new GuiNpcButton(11, guiLeft + 150, guiTop + 66, 130, 20, "gui.deselectAll")); - - - addLabel(new GuiNpcLabel(4, StatCollector.translateToLocal("cloner.order"), guiLeft + 150, guiTop + 112)); - addButton(new GuiNpcButton(14, guiLeft + 215, guiTop + 105, new String[]{"cloner.ascending", "cloner.descending"}, ascending)); - - addLabel(new GuiNpcLabel(5, StatCollector.translateToLocal("cloner.type"), guiLeft + 150, guiTop + 135)); - addButton(new GuiNpcButton(15, guiLeft + 215, guiTop + 128, new String[]{"cloner.name", "cloner.date"}, sortType)); - - - getButton(12).width = 65; - getButton(12).height = 20; - - getButton(13).width = 65; - getButton(13).height = 20; - - getButton(14).width = 65; - getButton(14).height = 20; - - getButton(15).width = 65; - getButton(15).height = 20; - - showFilters(); + showFiltersPage(); } } - public int getShowingClones() { - return showingClones; + private void initTabSideButtons() { + // Tabs 1-5: full width (70px) + addSideButton(new GuiMenuSideButton(21, guiLeft - 70, guiTop + 2, 70, 22, "1")); + addSideButton(new GuiMenuSideButton(22, guiLeft - 70, guiTop + 23, 70, 22, "2")); + addSideButton(new GuiMenuSideButton(23, guiLeft - 70, guiTop + 44, 70, 22, "3")); + addSideButton(new GuiMenuSideButton(24, guiLeft - 70, guiTop + 65, 70, 22, "4")); + addSideButton(new GuiMenuSideButton(25, guiLeft - 70, guiTop + 86, 70, 22, "5")); + // Tabs 6-15: half width (35px), 2-column grid + addSideButton(new GuiMenuSideButton(26, guiLeft - 70, guiTop + 107, 35, 22, "6")); + addSideButton(new GuiMenuSideButton(27, guiLeft - 35, guiTop + 107, 35, 22, "7")); + addSideButton(new GuiMenuSideButton(28, guiLeft - 70, guiTop + 128, 35, 22, "8")); + addSideButton(new GuiMenuSideButton(29, guiLeft - 35, guiTop + 128, 35, 22, "9")); + addSideButton(new GuiMenuSideButton(30, guiLeft - 70, guiTop + 149, 35, 22, "10")); + addSideButton(new GuiMenuSideButton(31, guiLeft - 35, guiTop + 149, 35, 22, "11")); + addSideButton(new GuiMenuSideButton(32, guiLeft - 70, guiTop + 170, 35, 22, "12")); + addSideButton(new GuiMenuSideButton(33, guiLeft - 35, guiTop + 170, 35, 22, "13")); + addSideButton(new GuiMenuSideButton(34, guiLeft - 70, guiTop + 191, 35, 22, "14")); + addSideButton(new GuiMenuSideButton(35, guiLeft - 35, guiTop + 191, 35, 22, "15")); + + if (activeFolder == null && activeTab >= 1 && activeTab <= 15) { + GuiMenuSideButton active = getSideButton(20 + activeTab); + if (active != null) active.active = true; + } } - public void showFilters() { + private void initFolderSideButtons() { + folderSideButtons.clear(); + for (int i = 0; i < folderNames.size(); i++) { + String name = folderNames.get(i); + GuiMenuSideButton btn = new GuiMenuSideButton(i, guiLeft - 70, guiTop + 2 + i * 21, 70, 22, name); + if (name.equals(activeFolder)) { + btn.active = true; + } + folderSideButtons.put(i, btn); + } + + // Folder CRUD buttons below visible scroll area (8 slots) + int crudY = guiTop + 2 + 8 * 21; + addButton(new GuiNpcButton(40, guiLeft - 70, crudY, 22, 20, "+")); + if (activeFolder != null) { + addButton(new GuiNpcButton(41, guiLeft - 47, crudY, 28, 20, "Ren")); + GuiNpcButton delBtn = new GuiNpcButton(42, guiLeft - 18, crudY, 22, 20, "-"); + if (ClientCloneController.Instance != null) { + List clones = ClientCloneController.Instance.getClones(activeFolder); + delBtn.enabled = clones == null || clones.isEmpty(); + } + addButton(delBtn); + } + } + + // ==================== FILTERS PAGE ==================== + private void showFiltersPage() { + int baseY = guiTop; + + addLabel(new GuiNpcLabel(1, StatCollector.translateToLocal("cloner.tagFilters"), + guiLeft + 7, baseY + 7)); + + filterScroll.clear(); + filterScroll.setSize(140, 166); + filterScroll.guiLeft = guiLeft + 4; + filterScroll.guiTop = baseY + 19; + filterScroll.multipleSelection = true; + filterScroll.setSelectedList(tagFilters); + addScroll(filterScroll); + + addTextField(new GuiNpcTextField(2, this, fontRendererObj, + guiLeft + 4, baseY + 190, 140, 20, tagSearch)); + + addLabel(new GuiNpcLabel(2, StatCollector.translateToLocal("cloner.tagVisibility"), + guiLeft + 150, baseY + 27)); + addButton(new GuiNpcButton(12, guiLeft + 215, baseY + 20, + new String[]{"display.show", "display.all", "display.hide"}, displayTags)); + + addLabel(new GuiNpcLabel(3, StatCollector.translateToLocal("filter.contains"), + guiLeft + 150, baseY + 50)); + addButton(new GuiNpcButton(13, guiLeft + 215, baseY + 43, + new String[]{"filter.any", "filter.all", "filter.notany", "filter.notall"}, filterCondition)); + + addButton(new GuiNpcButton(11, guiLeft + 150, baseY + 66, 130, 20, "gui.deselectAll")); + + addLabel(new GuiNpcLabel(4, StatCollector.translateToLocal("cloner.order"), + guiLeft + 150, baseY + 112)); + addButton(new GuiNpcButton(14, guiLeft + 215, baseY + 105, + new String[]{"cloner.ascending", "cloner.descending"}, ascending)); + + addLabel(new GuiNpcLabel(5, StatCollector.translateToLocal("cloner.type"), + guiLeft + 150, baseY + 135)); + addButton(new GuiNpcButton(15, guiLeft + 215, baseY + 128, + new String[]{"cloner.name", "cloner.date"}, sortType)); + + getButton(12).width = 65; + getButton(12).height = 20; + getButton(13).width = 65; + getButton(13).height = 20; + getButton(14).width = 65; + getButton(14).height = 20; + getButton(15).width = 65; + getButton(15).height = 20; + filterScroll.setList(getTagList()); } - private void showEntities() { - Map data = EntityList.stringToClassMapping; - ArrayList list = new ArrayList(); - for (Object name : data.keySet()) { - Class c = (Class) data.get(name); - try { - if (EntityLiving.class.isAssignableFrom(c) && c.getConstructor(new Class[]{World.class}) != null && !Modifier.isAbstract(c.getModifiers())) - list.add(name.toString()); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - } - } - this.list = list; - this.rawList = new ArrayList(this.list); - scroll.setList(getSearchList(), ascending == 0, sortType == 0); - } - + // ==================== CLONE / ENTITY DISPLAY ==================== private void showClones() { + if (activeFolder != null) { + showFolderClones(); + return; + } + if (showingClones == 2) { PacketClient.sendClient(new CloneTagListPacket(activeTab)); PacketClient.sendClient(new CloneListPacket(activeTab)); @@ -219,16 +304,64 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { } this.tagMap = ClientTagMapController.Instance.getTagMap(activeTab); + populateRawListWithTags(); + scroll.setList(getSearchList(), ascending == 0, sortType == 0); + } + + private void showFolderClones() { + if (showingClones == 2) { + PacketClient.sendClient(new CloneTagListPacket(activeFolder)); + PacketClient.sendClient(new CloneListPacket(activeFolder)); + return; + } + + if (ClientCloneController.Instance != null) { + if (sortType == 0) { + this.list = ClientCloneController.Instance.getClones(activeFolder); + } else { + this.list = ClientCloneController.Instance.getClonesDate(activeFolder); + } + this.tagMap = ClientTagMapController.Instance.getTagMap(activeFolder); + } else { + this.list = new ArrayList<>(); + this.tagMap = null; + } + + populateRawListWithTags(); + scroll.setList(getSearchList(), ascending == 0, sortType == 0); + } + + private void showEntities() { + Map data = EntityList.stringToClassMapping; + ArrayList list = new ArrayList(); + for (Object name : data.keySet()) { + Class c = (Class) data.get(name); + try { + if (EntityLiving.class.isAssignableFrom(c) + && c.getConstructor(new Class[]{World.class}) != null + && !Modifier.isAbstract(c.getModifiers())) + list.add(name.toString()); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + } + } + this.list = list; this.rawList = new ArrayList(this.list); + scroll.setList(getSearchList(), ascending == 0, sortType == 0); + } + + private void populateRawListWithTags() { + this.rawList = new ArrayList<>(this.list); if (this.tagMap != null) { - if (GuiNpcMobSpawner.displayTags == 0 || GuiNpcMobSpawner.displayTags == 1) { + if (displayTags == 0 || displayTags == 1) { for (int i = 0; i < this.list.size(); i++) { StringBuilder npcName = new StringBuilder(list.get(i)); if (this.tagMap.hasClone(list.get(i))) { for (UUID tagUUID : this.tagMap.getUUIDsList(list.get(i))) { Tag tag = tags.get(tagUUID); if (tag != null) { - if (GuiNpcMobSpawner.displayTags == 1 || !tag.getIsHidden()) { + if (displayTags == 1 || !tag.getIsHidden()) { npcName.append(" [" + tag.name + "]"); } } @@ -238,27 +371,29 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { } } } - - scroll.setList(getSearchList(), ascending == 0, sortType == 0); } - public void keyTyped(char c, int i) { - super.keyTyped(c, i); - if (getTextField(1) != null) { - if (search.equals(getTextField(1).getText())) - return; - search = getTextField(1).getText().toLowerCase(); - scroll.setList(getSearchList(), ascending == 0, sortType == 0); - } - if (getTextField(2) != null) { - if (tagSearch.equals(getTextField(2).getText())) - return; - tagSearch = getTextField(2).getText().toLowerCase(); - filterScroll.setList(getTagList()); - } + private NBTTagCompound getCompound() { + String sel = scroll.getSelected(); + if (sel == null) + return null; + if (showingClones == 0) { + if (activeFolder != null && ClientCloneController.Instance != null) { + return ClientCloneController.Instance.getCloneData(player, sel, activeFolder); + } + return ClientCloneController.Instance.getCloneData(player, sel, activeTab); + } else { + Entity entity = EntityList.createEntityByName(sel, Minecraft.getMinecraft().theWorld); + if (entity == null) + return null; + NBTTagCompound compound = new NBTTagCompound(); + entity.writeToNBTOptional(compound); + return compound; + } } + // ==================== SEARCH / FILTER ==================== private List getSearchList() { if (this.list == null) { this.list = new ArrayList(); @@ -269,10 +404,8 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { // No Filters - or - Entities if (tagFilters.size() == 0 || showingClones == 1) { - // No Search if (search.isEmpty()) return new ArrayList(this.list); - // Yes Search else { List list = new ArrayList(); for (int i = 0; i < this.list.size(); i++) { @@ -284,10 +417,7 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { } } - //////////////////////// - //// WITH FILTERS - - // No Search + // With Filters if (search.isEmpty()) { List list = new ArrayList(); for (String name : this.list) { @@ -300,7 +430,6 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { return list; } - // Yes Search List list = new ArrayList(); for (int i = 0; i < this.list.size(); i++) { if (this.rawList.get(i).toLowerCase().contains(search)) { @@ -311,7 +440,6 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { } } } - } return list; } @@ -346,7 +474,6 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { } } } - return conditionMet; } @@ -354,10 +481,8 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { if (this.tagList == null) { this.tagList = new ArrayList(); } - // No Search // No Filters - or - Entities if (tagSearch.isEmpty()) return new ArrayList(tagNames.keySet()); - // Yes Search // No Filters - or - Entities List list = new ArrayList(); for (String name : this.tagList) { @@ -367,60 +492,111 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { return list; } - private NBTTagCompound getCompound() { - String sel = scroll.getSelected(); - if (sel == null) - return null; + // ==================== RENDERING ==================== + @Override + public void drawScreen(int i, int j, float f) { + super.drawScreen(i, j, f); - if (showingClones == 0) { - return ClientCloneController.Instance.getCloneData(player, sel, activeTab); - } else { - Entity entity = EntityList.createEntityByName(sel, Minecraft.getMinecraft().theWorld); - if (entity == null) - return null; - NBTTagCompound compound = new NBTTagCompound(); - entity.writeToNBTOptional(compound); - return compound; + // Render scrollable folder side buttons (folders mode only) + if (sidebarMode == 1 && (showingClones == 0 || showingClones == 2)) { + renderFolderSideButtons(i, j); } } + private void renderFolderSideButtons(int mouseX, int mouseY) { + final float SMOOTHING_FACTOR = 0.1F; + int maxScroll = Math.max(0, folderSideButtons.size() - 8) * 21; + + if (isMouseInFolderScrollZone(mouseX, mouseY)) { + int wheel = Mouse.getDWheel(); + if (wheel != 0) { + destFolderScroll = ValueUtil.clamp( + destFolderScroll - Math.signum(wheel) * 21, -maxScroll, 0); + } + } + + folderScroll += (destFolderScroll - folderScroll) * SMOOTHING_FACTOR; + + for (Map.Entry entry : folderSideButtons.entrySet()) { + int idx = entry.getKey(); + GuiMenuSideButton button = entry.getValue(); + + float rawY = guiTop + 2 + idx * 21 + folderScroll; + int smoothedY = Math.round(rawY); + + if (smoothedY >= guiTop && smoothedY < guiTop + 8 * 21) { + button.yPosition = smoothedY; + button.drawButton(mc, mouseX, mouseY); + } + } + } + + private boolean isMouseInFolderScrollZone(int x, int y) { + return x >= guiLeft - 70 && x <= guiLeft + && y >= guiTop + 2 && y <= guiTop + 2 + 8 * 21; + } + + // ==================== MOUSE / KEYBOARD EVENTS ==================== + @Override + public void mouseClicked(int i, int j, int k) { + super.mouseClicked(i, j, k); + + // Handle folder side button clicks (folders mode) + if (sidebarMode == 1 && (showingClones == 0 || showingClones == 2) && k == 0) { + for (GuiMenuSideButton button : new ArrayList<>(folderSideButtons.values())) { + if (button.mousePressed(mc, i, j)) { + folderSideButtonPressed(button); + } + } + } + } + + private void folderSideButtonPressed(GuiMenuSideButton button) { + if (button.active) return; + NoppesUtil.clickSound(); + activeFolder = button.displayString; + activeTab = -1; + initGui(); + } + + @Override + public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) { + } + + @Override + public void customScrollDoubleClicked(String selection, GuiCustomScroll guiCustomScroll) { + } + + public void keyTyped(char c, int i) { + super.keyTyped(c, i); + if (getTextField(1) != null) { + if (search.equals(getTextField(1).getText())) + return; + search = getTextField(1).getText().toLowerCase(); + scroll.setList(getSearchList(), ascending == 0, sortType == 0); + } + if (getTextField(2) != null) { + if (tagSearch.equals(getTextField(2).getText())) + return; + tagSearch = getTextField(2).getText().toLowerCase(); + filterScroll.setList(getTagList()); + } + } + + // ==================== BUTTON ACTIONS ==================== @Override protected void actionPerformed(GuiButton guibutton) { int id = guibutton.id; - if (id == 0) { - close(); - } - if (id == 1) { - if (showingClones == 2) { - String sel = scroll.getSelected(); - if (sel == null) - return; - SpawnMobPacket.Server(posX, posY, posZ, sel, activeTab); - close(); - } else { - NBTTagCompound compound = getCompound(); - if (compound == null) - return; - SpawnMobPacket.Client(posX, posY, posZ, compound); - close(); - } - } - if (id == 2) { - if (showingClones == 2) { - String sel = scroll.getSelected(); - if (sel == null) - return; - MobSpawnerPacket.Server(posX, posY, posZ, sel, activeTab); - close(); - } else { - NBTTagCompound compound = getCompound(); - if (compound == null) - return; - MobSpawnerPacket.Client(posX, posY, posZ, compound); - close(); - } + // Tab side buttons (IDs 21-35) + if (id >= 21 && id <= 35) { + activeTab = id - 20; + activeFolder = null; + initGui(); + return; } + + // Mode buttons if (id == 3) { showingClones = 0; initGui(); @@ -440,53 +616,222 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { if (id == 17) { close(); } + + // Open fullscreen GUI + if (id == 61) { + Minecraft.getMinecraft().displayGuiScreen( + new GuiNpcMobSpawnerFullscreen(posX, posY, posZ)); + } + + // Tabs/Folders toggle + if (id == 60) { + sidebarMode = ((GuiNpcButton) guibutton).getValue(); + if (sidebarMode == 1 && activeFolder == null && !folderNames.isEmpty()) { + activeFolder = folderNames.get(0); + activeTab = -1; + } + if (sidebarMode == 0 && (activeTab < 1 || activeTab > 15)) { + activeTab = 1; + activeFolder = null; + } + initGui(); + } + + // Spawn + if (id == 1) { + if (showingClones == 2) { + String sel = scroll.getSelected(); + if (sel == null) return; + if (activeFolder != null) + SpawnMobPacket.ServerFolder(posX, posY, posZ, sel, activeFolder); + else + SpawnMobPacket.Server(posX, posY, posZ, sel, activeTab); + close(); + } else { + NBTTagCompound compound = getCompound(); + if (compound == null) return; + SpawnMobPacket.Client(posX, posY, posZ, compound); + close(); + } + } + + // Mob Spawner + if (id == 2) { + if (showingClones == 2) { + String sel = scroll.getSelected(); + if (sel == null) return; + if (activeFolder != null) + MobSpawnerPacket.ServerFolder(posX, posY, posZ, sel, activeFolder); + else + MobSpawnerPacket.Server(posX, posY, posZ, sel, activeTab); + close(); + } else { + NBTTagCompound compound = getCompound(); + if (compound == null) return; + MobSpawnerPacket.Client(posX, posY, posZ, compound); + close(); + } + } + + // Remove if (id == 6) { if (scroll.getSelected() != null) { if (showingClones == 2) { - PacketClient.sendClient(new CloneRemovePacket(activeTab, scroll.getSelected())); + if (activeFolder != null) + PacketClient.sendClient(new CloneRemovePacket(activeFolder, scroll.getSelected())); + else + PacketClient.sendClient(new CloneRemovePacket(activeTab, scroll.getSelected())); return; } - ClientCloneController.Instance.removeClone(scroll.getSelected(), activeTab); + if (activeFolder != null && ClientCloneController.Instance != null) + ClientCloneController.Instance.removeClone(scroll.getSelected(), activeFolder); + else + ClientCloneController.Instance.removeClone(scroll.getSelected(), activeTab); scroll.selected = -1; initGui(); } } + + // Move + if (id == 50) { + if (scroll.getSelected() != null) { + setSubGui(new SubGuiCloneMove()); + } + } + + // Filter controls if (id == 11) { tagFilters = new HashSet<>(); filterScroll.setSelectedList(tagFilters); initGui(); } if (id == 12) { - GuiNpcButton button = (GuiNpcButton) guibutton; - displayTags = (byte) button.getValue(); + displayTags = (byte) ((GuiNpcButton) guibutton).getValue(); } if (id == 13) { - GuiNpcButton button = (GuiNpcButton) guibutton; - filterCondition = (byte) button.getValue(); + filterCondition = (byte) ((GuiNpcButton) guibutton).getValue(); } if (id == 14) { - GuiNpcButton button = (GuiNpcButton) guibutton; - ascending = (byte) button.getValue(); + ascending = (byte) ((GuiNpcButton) guibutton).getValue(); } if (id == 15) { - GuiNpcButton button = (GuiNpcButton) guibutton; - sortType = (byte) button.getValue(); + sortType = (byte) ((GuiNpcButton) guibutton).getValue(); } - if (id > 20) { - activeTab = id - 20; - initGui(); + + // Create folder + if (id == 40) { + setSubGui(new SubGuiCloneFolderName("")); + } + // Rename folder + if (id == 41) { + if (activeFolder != null) + setSubGui(new SubGuiCloneFolderName(activeFolder)); + } + // Delete folder + if (id == 42) { + if (activeFolder != null && ClientCloneController.Instance != null) { + if (showingClones == 2) { + PacketClient.sendClient(new CloneFolderCrudPacket( + CloneFolderCrudPacket.ACTION_DELETE, activeFolder)); + } else { + ClientCloneController.Instance.deleteFolder(activeFolder); + loadFolderNames(); + } + activeFolder = null; + activeTab = 1; + initGui(); + } } } + // ==================== SUB GUI ==================== + @Override + public void subGuiClosed(SubGuiInterface subgui) { + if (subgui instanceof SubGuiCloneFolderName) { + SubGuiCloneFolderName folderGui = (SubGuiCloneFolderName) subgui; + if (folderGui.cancelled) return; + + String newName = folderGui.getFolderName(); + if (newName == null || newName.isEmpty()) return; + + if (folderGui.isRename()) { + if (showingClones == 2) { + PacketClient.sendClient(new CloneFolderCrudPacket( + CloneFolderCrudPacket.ACTION_RENAME, folderGui.getOriginalName(), newName)); + } else if (ClientCloneController.Instance != null) { + ClientCloneController.Instance.renameFolder(folderGui.getOriginalName(), newName); + loadFolderNames(); + } + activeFolder = newName; + activeTab = -1; + } else { + if (showingClones == 2) { + PacketClient.sendClient(new CloneFolderCrudPacket( + CloneFolderCrudPacket.ACTION_CREATE, newName)); + } else if (ClientCloneController.Instance != null) { + ClientCloneController.Instance.createFolder(newName); + loadFolderNames(); + } + activeFolder = newName; + activeTab = -1; + } + initGui(); + } + + if (subgui instanceof SubGuiCloneMove) { + SubGuiCloneMove moveGui = (SubGuiCloneMove) subgui; + if (!moveGui.cancelled) { + String cloneName = scroll.getSelected(); + if (cloneName != null) { + int fromTab = activeFolder != null ? -1 : activeTab; + String fromFolder = activeFolder; + int toTab = moveGui.getDestTab(); + String toFolder = moveGui.getDestFolder(); + + if (showingClones == 2) { + PacketClient.sendClient(new CloneMovePacket( + cloneName, fromTab, fromFolder, toTab, toFolder)); + } else if (ClientCloneController.Instance != null) { + if (fromFolder != null && toFolder != null) { + ClientCloneController.Instance.moveClone(cloneName, fromFolder, toFolder); + } else if (fromFolder != null) { + ClientCloneController.Instance.moveClone(cloneName, fromFolder, toTab); + } else if (toFolder != null) { + ClientCloneController.Instance.moveClone(cloneName, fromTab, toFolder); + } else { + ClientCloneController.Instance.moveClone(cloneName, fromTab, toTab); + } + } + initGui(); + } + } + } + } + + // ==================== DATA FROM SERVER ==================== @Override public void save() { - } @Override public void setGuiData(NBTTagCompound compound) { - if (compound.hasKey("CloneTags")) { - this.tagMap = new TagMap(activeTab); + if (compound.hasKey("CloneFolders")) { + NBTTagList folderList = compound.getTagList("CloneFolders", 10); + folderNames.clear(); + for (int i = 0; i < folderList.tagCount(); i++) { + CloneFolder folder = new CloneFolder(); + folder.readNBT(folderList.getCompoundTagAt(i)); + folderNames.add(folder.name); + } + initGui(); + } else if (compound.hasKey("MoveSuccess")) { + initGui(); + } else if (compound.hasKey("CloneTags")) { + if (activeFolder != null) { + this.tagMap = new TagMap(activeFolder); + } else { + this.tagMap = new TagMap(activeTab); + } NBTTagCompound cloneTags = compound.getCompoundTag("CloneTags"); this.tagMap.readNBT(cloneTags); } else if (compound.hasKey("AllTags")) { @@ -521,29 +866,8 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData { } this.list = list; - - this.rawList = new ArrayList(this.list); - if (this.tagMap != null) { - if (GuiNpcMobSpawner.displayTags == 0 || GuiNpcMobSpawner.displayTags == 1) { - for (int i = 0; i < this.list.size(); i++) { - StringBuilder npcName = new StringBuilder(list.get(i)); - if (this.tagMap.hasClone(list.get(i))) { - for (UUID tagUUID : this.tagMap.getUUIDsList(list.get(i))) { - Tag tag = tags.get(tagUUID); - if (tag != null) { - if (GuiNpcMobSpawner.displayTags == 1 || !tag.getIsHidden()) { - npcName.append(" [" + tag.name + "]"); - } - } - } - } - this.rawList.set(i, npcName.toString()); - } - } - } - + populateRawListWithTags(); scroll.setList(getSearchList(), ascending == 0, sortType == 0); } } - } diff --git a/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerAdd.java b/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerAdd.java index d2895fc44..be816eb38 100644 --- a/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerAdd.java +++ b/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerAdd.java @@ -22,6 +22,7 @@ import noppes.npcs.client.gui.util.GuiNpcTextField; import noppes.npcs.client.gui.util.IGuiData; import noppes.npcs.client.gui.util.ISubGuiListener; import noppes.npcs.client.gui.util.SubGuiInterface; +import noppes.npcs.controllers.data.CloneFolder; import noppes.npcs.controllers.data.Tag; import noppes.npcs.entity.EntityNPCInterface; @@ -35,9 +36,14 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall private Entity toClone; private NBTTagCompound compound; private static boolean serverSide = false; + private static int saveMode = 0; // 0=Tab, 1=Folder private static int tab = 1; + private static String folder = null; public boolean isNPC = false; + // Folder names cache for the selector + private String[] folderLabels = new String[0]; + // Selected Tags to Add public static NBTTagList tagsCompound; public static HashSet addTags; @@ -68,6 +74,18 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall if (isNPC) { PacketClient.sendClient(new CloneAllTagsShortPacket()); } + + buildFolderLabels(); + } + + private void buildFolderLabels() { + ArrayList names = new ArrayList<>(); + if (ClientCloneController.Instance != null) { + for (CloneFolder f : ClientCloneController.Instance.getFolderList()) { + names.add(f.name); + } + } + folderLabels = names.toArray(new String[0]); } @Override @@ -77,21 +95,63 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall addLabel(new GuiNpcLabel(0, "Save as", guiLeft + 4, guiTop + 6)); addTextField(new GuiNpcTextField(0, this, fontRendererObj, guiLeft + 4, guiTop + 18, 200, 20, name)); + // Tab/Folder toggle + addButton(new GuiNpcButton(6, guiLeft + 4, guiTop + 45, 50, 20, + new String[]{"Tab", "Folder"}, saveMode)); - addLabel(new GuiNpcLabel(1, "Tab", guiLeft + 10, guiTop + 50)); - addButton(new GuiButtonBiDirectional(2, guiLeft + 40, guiTop + 45, 60, 20, new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}, tab - 1)); - - addButton(new GuiNpcButton(3, guiLeft + 4, guiTop + 95, new String[]{"Client side", "Server side"}, serverSide ? 1 : 0)); - addLabel(new GuiNpcLabel(1, "Tab", guiLeft + 10, guiTop + 50)); + // Destination selector based on saveMode + if (saveMode == 0) { + // Tab selector: 1-15 + String[] tabLabels = new String[15]; + for (int i = 0; i < 15; i++) tabLabels[i] = String.valueOf(i + 1); + int selectedTab = (tab >= 1 && tab <= 15) ? tab - 1 : 0; + addButton(new GuiButtonBiDirectional(2, guiLeft + 56, guiTop + 45, 90, 20, tabLabels, selectedTab)); + } else { + // Folder selector + if (folderLabels.length > 0) { + int selectedFolder = 0; + if (folder != null) { + for (int i = 0; i < folderLabels.length; i++) { + if (folder.equals(folderLabels[i])) { + selectedFolder = i; + break; + } + } + } + addButton(new GuiButtonBiDirectional(2, guiLeft + 56, guiTop + 45, 90, 20, folderLabels, selectedFolder)); + } else { + addLabel(new GuiNpcLabel(7, "No folders", guiLeft + 60, guiTop + 51)); + } + } addButton(new GuiNpcButton(0, guiLeft + 4, guiTop + 70, 80, 20, "gui.save")); addButton(new GuiNpcButton(1, guiLeft + 86, guiTop + 70, 80, 20, "gui.cancel")); + addButton(new GuiNpcButton(3, guiLeft + 4, guiTop + 95, new String[]{"Client side", "Server side"}, serverSide ? 1 : 0)); + if (isNPC) { addButton(new GuiNpcButton(4, guiLeft + 4, guiTop + 120, 99, 20, "cloner.wandTags")); addButton(new GuiNpcButton(5, guiLeft + 106, guiTop + 120, 99, 20, "cloner.npcTags")); if (addTags.size() > 0) { - addLabel(new GuiNpcLabel(6, "cloner.wandtagsapplied", guiLeft + 10, guiTop + 160)); + addLabel(new GuiNpcLabel(8, "cloner.wandtagsapplied", guiLeft + 10, guiTop + 160)); + } + } + } + + private void updateDestinationFromSelector() { + GuiNpcButton selector = getButton(2); + if (selector == null) return; + + int index = selector.getValue(); + if (saveMode == 0) { + // Tab mode + tab = index + 1; + folder = null; + } else { + // Folder mode + if (index >= 0 && index < folderLabels.length) { + folder = folderLabels[index]; + tab = -1; } } } @@ -99,23 +159,38 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall public void buttonEvent(GuiButton guibutton) { int id = guibutton.id; if (id == 0) { + updateDestinationFromSelector(); String name = getTextField(0).getText(); if (name.isEmpty()) return; - int tab = ((GuiNpcButton) guibutton).getValue() + 1; + + if (saveMode == 1 && folderLabels.length == 0) + return; + if (!serverSide) { - if (ClientCloneController.Instance.getCloneData(null, name, tab) != null) + boolean exists; + if (saveMode == 1 && folder != null && ClientCloneController.Instance != null) { + exists = ClientCloneController.Instance.getCloneData(null, name, folder) != null; + } else { + exists = ClientCloneController.Instance.getCloneData(null, name, tab) != null; + } + if (exists) displayGuiScreen(new GuiYesNo(this, "Warning", "You are about to overwrite a clone", 1)); else confirmClicked(true, 0); - } else - PacketClient.sendClient(new ClonePreSavePacket(name, tab)); + } else { + if (saveMode == 1 && folder != null) { + PacketClient.sendClient(new ClonePreSavePacket(name, folder)); + } else { + PacketClient.sendClient(new ClonePreSavePacket(name, tab)); + } + } } if (id == 1) { close(); } if (id == 2) { - tab = ((GuiNpcButton) guibutton).getValue() + 1; + updateDestinationFromSelector(); } if (id == 3) { serverSide = ((GuiNpcButton) guibutton).getValue() == 1; @@ -130,6 +205,10 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall this.setSubGui(new SubGuiClonerNPCTags((EntityNPCInterface) toClone, this)); } } + if (id == 6) { + saveMode = ((GuiNpcButton) guibutton).getValue(); + initGui(); + } } @@ -145,13 +224,21 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall if (isNPC) { compound.setTag("TagUUIDs", tagsCompound); } - ClientCloneController.Instance.addClone(compound, name, tab, extraTags); + if (saveMode == 1 && folder != null && ClientCloneController.Instance != null) { + ClientCloneController.Instance.addClone(compound, name, folder, extraTags); + } else { + ClientCloneController.Instance.addClone(compound, name, tab, extraTags); + } } else { NBTTagCompound compounder = new NBTTagCompound(); if (isNPC) { compounder.setTag("TagUUIDs", tagsCompound); } - PacketClient.sendClient(new CloneSavePacket(name, tab, extraTags, compounder)); + if (saveMode == 1 && folder != null) { + PacketClient.sendClient(new CloneSavePacket(name, folder, extraTags, compounder)); + } else { + PacketClient.sendClient(new CloneSavePacket(name, tab, extraTags, compounder)); + } } close(); diff --git a/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerFullscreen.java b/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerFullscreen.java new file mode 100644 index 000000000..5199a9051 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/GuiNpcMobSpawnerFullscreen.java @@ -0,0 +1,789 @@ +package noppes.npcs.client.gui; + +import kamkeel.npcs.network.PacketClient; +import kamkeel.npcs.network.packets.request.clone.CloneAllTagsPacket; +import kamkeel.npcs.network.packets.request.clone.CloneFolderCrudPacket; +import kamkeel.npcs.network.packets.request.clone.CloneListPacket; +import kamkeel.npcs.network.packets.request.clone.CloneMovePacket; +import kamkeel.npcs.network.packets.request.clone.CloneRemovePacket; +import kamkeel.npcs.network.packets.request.clone.CloneTagListPacket; +import kamkeel.npcs.network.packets.request.clone.MobSpawnerPacket; +import kamkeel.npcs.network.packets.request.clone.SpawnMobPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLiving; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import noppes.npcs.client.controllers.ClientCloneController; +import noppes.npcs.client.controllers.ClientTagMapController; +import noppes.npcs.client.gui.util.GuiCustomScroll; +import noppes.npcs.client.gui.util.GuiCustomScrollCloner; +import noppes.npcs.client.gui.util.GuiCustomScrollIcons; +import noppes.npcs.client.gui.util.GuiMenuTopButton; +import noppes.npcs.client.gui.util.GuiNPCInterface; +import noppes.npcs.client.gui.util.GuiNpcButton; +import noppes.npcs.client.gui.util.GuiNpcLabel; +import noppes.npcs.client.gui.util.GuiNpcTextField; +import noppes.npcs.client.gui.util.IClonerGui; +import noppes.npcs.client.gui.util.ICustomScrollListener; +import noppes.npcs.client.gui.util.IGuiData; +import noppes.npcs.client.gui.util.ISubGuiListener; +import noppes.npcs.client.gui.util.SubGuiInterface; +import noppes.npcs.controllers.data.CloneFolder; +import noppes.npcs.controllers.data.Tag; +import noppes.npcs.controllers.data.TagMap; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class GuiNpcMobSpawnerFullscreen extends GuiNPCInterface implements IGuiData, ICustomScrollListener, ISubGuiListener, IClonerGui { + + // ==================== LAYOUT CONSTANTS ==================== + private static final int PAD = 10; + private static final int LEFT_NAV_W = 120; + private static final int RIGHT_ACT_W = 60; + private static final int TOP_BAR_H = 22; + private static final int GAP = 2; + + // ==================== INSTANCE STATE ==================== + public TagMap tagMap; + private final GuiCustomScrollCloner scroll = new GuiCustomScrollCloner(this, 0); + private final GuiCustomScroll filterScroll = new GuiCustomScroll(this, 1); + private final GuiCustomScrollIcons navScroll = new GuiCustomScrollIcons(this, 2); + + private int posX, posY, posZ; + private List list; + private List rawList; + private List tagList; + private List folderNames = new ArrayList<>(); + + // Left nav list data + private List navNames = new ArrayList<>(); + private List navIcons = new ArrayList<>(); + private String navSearch = ""; + + // ==================== CONSTRUCTOR ==================== + public GuiNpcMobSpawnerFullscreen(int posX, int posY, int posZ) { + super(); + this.posX = posX; + this.posY = posY; + this.posZ = posZ; + closeOnEsc = true; + drawDefaultBackground = true; + // No setBackground() call — no menubg.png tiling + PacketClient.sendClient(new CloneAllTagsPacket()); + loadFolderNames(); + } + + private void loadFolderNames() { + folderNames.clear(); + if (ClientCloneController.Instance != null) { + for (CloneFolder folder : ClientCloneController.Instance.getFolderList()) { + folderNames.add(folder.name); + } + } + } + + // ==================== IClonerGui ==================== + @Override + public int getShowingClones() { + return GuiNpcMobSpawner.showingClones; + } + + @Override + public HashMap getTags() { + return GuiNpcMobSpawner.tags; + } + + @Override + public TagMap getTagMap() { + return tagMap; + } + + // ==================== INIT GUI ==================== + @Override + public void initGui() { + super.initGui(); + + int originX = PAD; + int originY = PAD; + int usableW = width - 2 * PAD; + int usableH = height - 2 * PAD; + + int contentX = originX + LEFT_NAV_W + GAP; + int contentY = originY + TOP_BAR_H + GAP; + int contentW = usableW - LEFT_NAV_W - RIGHT_ACT_W - 3 * GAP; + int contentH = usableH - TOP_BAR_H - GAP; + int rightX = contentX + contentW + GAP; + + // Top bar — mode buttons + GuiMenuTopButton btn; + addTopButton(btn = new GuiMenuTopButton(3, originX, originY, "spawner.clones")); + btn.active = GuiNpcMobSpawner.showingClones == 0; + addTopButton(btn = new GuiMenuTopButton(5, btn, "gui.server")); + btn.active = GuiNpcMobSpawner.showingClones == 2; + addTopButton(btn = new GuiMenuTopButton(4, btn, "spawner.entities")); + btn.active = GuiNpcMobSpawner.showingClones == 1; + + // Top bar — right side + int topRight = originX + usableW; + addTopButton(new GuiMenuTopButton(17, topRight - 22, originY, "X")); + addTopButton(new GuiMenuTopButton(61, topRight - 38, originY, "-")); + addTopButton(btn = new GuiMenuTopButton(16, topRight - 82, originY, "gui.filters")); + btn.active = GuiNpcMobSpawner.showingClones == 3; + + if (GuiNpcMobSpawner.showingClones < 3) { + // Search field in top bar + addTextField(new GuiNpcTextField(1, this, fontRendererObj, + contentX, originY + 2, contentW, 20, GuiNpcMobSpawner.search)); + + // Left navigation panel + buildNavList(); + navScroll.clear(); + navScroll.setSize(LEFT_NAV_W, contentH - 44); + navScroll.guiLeft = originX; + navScroll.guiTop = contentY; + navScroll.setListWithIcons(navNames, navIcons); + navScroll.setSelected(getNavSelection()); + addScroll(navScroll); + + // Nav search field + addTextField(new GuiNpcTextField(3, this, fontRendererObj, + originX, contentY + contentH - 42, LEFT_NAV_W, 20, navSearch)); + + // Add folder button + addButton(new GuiNpcButton(40, originX, contentY + contentH - 20, LEFT_NAV_W, 20, "+ Add Folder")); + + // Center content — clone/entity list + scroll.clear(); + scroll.setSize(contentW, contentH); + scroll.guiLeft = contentX; + scroll.guiTop = contentY; + addScroll(scroll); + + // Right action panel + int btnW = RIGHT_ACT_W; + int btnH = 20; + int btnGap = 2; + addButton(new GuiNpcButton(1, rightX, contentY, btnW, btnH, "item.monsterPlacer.name")); + addButton(new GuiNpcButton(2, rightX, contentY + btnH + btnGap, btnW, btnH, "spawner.mobspawner")); + + if (GuiNpcMobSpawner.showingClones == 0 || GuiNpcMobSpawner.showingClones == 2) { + addButton(new GuiNpcButton(6, rightX, contentY + 2 * (btnH + btnGap), btnW, btnH, "gui.remove")); + addButton(new GuiNpcButton(50, rightX, contentY + 3 * (btnH + btnGap), btnW, btnH, "Move")); + showClones(); + } else { + showEntities(); + } + } else { + showFiltersPage(); + } + } + + // ==================== LEFT NAVIGATION ==================== + private void buildNavList() { + navNames.clear(); + navIcons.clear(); + + String searchLower = navSearch.toLowerCase(); + + for (int i = 1; i <= 15; i++) { + String name = "Tab " + i; + if (searchLower.isEmpty() || name.toLowerCase().contains(searchLower)) { + navNames.add(name); + navIcons.add(GuiCustomScrollIcons.ICON_TAB); + } + } + for (String folder : folderNames) { + if (searchLower.isEmpty() || folder.toLowerCase().contains(searchLower)) { + navNames.add(folder); + navIcons.add(GuiCustomScrollIcons.ICON_FOLDER); + } + } + } + + private String getNavSelection() { + if (GuiNpcMobSpawner.activeFolder != null) return GuiNpcMobSpawner.activeFolder; + if (GuiNpcMobSpawner.activeTab >= 1 && GuiNpcMobSpawner.activeTab <= 15) { + return "Tab " + GuiNpcMobSpawner.activeTab; + } + return "Tab 1"; + } + + private void handleNavClick(String selected) { + if (selected == null) return; + + // Check if it's a tab + if (selected.startsWith("Tab ")) { + try { + int tabNum = Integer.parseInt(selected.substring(4)); + if (tabNum >= 1 && tabNum <= 15) { + GuiNpcMobSpawner.activeTab = tabNum; + GuiNpcMobSpawner.activeFolder = null; + initGui(); + return; + } + } catch (NumberFormatException ignored) { + } + } + + // It's a folder name + GuiNpcMobSpawner.activeFolder = selected; + GuiNpcMobSpawner.activeTab = -1; + initGui(); + } + + // ==================== FILTERS PAGE ==================== + private void showFiltersPage() { + int originX = PAD; + int originY = PAD; + int baseY = originY + TOP_BAR_H + GAP; + + addLabel(new GuiNpcLabel(1, StatCollector.translateToLocal("cloner.tagFilters"), + originX + 7, baseY + 7)); + + filterScroll.clear(); + filterScroll.setSize(140, 166); + filterScroll.guiLeft = originX + 4; + filterScroll.guiTop = baseY + 19; + filterScroll.multipleSelection = true; + filterScroll.setSelectedList(GuiNpcMobSpawner.tagFilters); + addScroll(filterScroll); + + addTextField(new GuiNpcTextField(2, this, fontRendererObj, + originX + 4, baseY + 190, 140, 20, GuiNpcMobSpawner.tagSearch)); + + addLabel(new GuiNpcLabel(2, StatCollector.translateToLocal("cloner.tagVisibility"), + originX + 150, baseY + 27)); + addButton(new GuiNpcButton(12, originX + 215, baseY + 20, + new String[]{"display.show", "display.all", "display.hide"}, GuiNpcMobSpawner.displayTags)); + + addLabel(new GuiNpcLabel(3, StatCollector.translateToLocal("filter.contains"), + originX + 150, baseY + 50)); + addButton(new GuiNpcButton(13, originX + 215, baseY + 43, + new String[]{"filter.any", "filter.all", "filter.notany", "filter.notall"}, GuiNpcMobSpawner.filterCondition)); + + addButton(new GuiNpcButton(11, originX + 150, baseY + 66, 130, 20, "gui.deselectAll")); + + addLabel(new GuiNpcLabel(4, StatCollector.translateToLocal("cloner.order"), + originX + 150, baseY + 112)); + addButton(new GuiNpcButton(14, originX + 215, baseY + 105, + new String[]{"cloner.ascending", "cloner.descending"}, GuiNpcMobSpawner.ascending)); + + addLabel(new GuiNpcLabel(5, StatCollector.translateToLocal("cloner.type"), + originX + 150, baseY + 135)); + addButton(new GuiNpcButton(15, originX + 215, baseY + 128, + new String[]{"cloner.name", "cloner.date"}, GuiNpcMobSpawner.sortType)); + + getButton(12).width = 65; + getButton(12).height = 20; + getButton(13).width = 65; + getButton(13).height = 20; + getButton(14).width = 65; + getButton(14).height = 20; + getButton(15).width = 65; + getButton(15).height = 20; + + filterScroll.setList(getTagList()); + } + + // ==================== CLONE / ENTITY DISPLAY ==================== + private void showClones() { + if (GuiNpcMobSpawner.activeFolder != null) { + showFolderClones(); + return; + } + + if (GuiNpcMobSpawner.showingClones == 2) { + PacketClient.sendClient(new CloneTagListPacket(GuiNpcMobSpawner.activeTab)); + PacketClient.sendClient(new CloneListPacket(GuiNpcMobSpawner.activeTab)); + return; + } + + if (GuiNpcMobSpawner.sortType == 0) { + this.list = ClientCloneController.Instance.getClones(GuiNpcMobSpawner.activeTab); + } else { + this.list = ClientCloneController.Instance.getClonesDate(GuiNpcMobSpawner.activeTab); + } + + this.tagMap = ClientTagMapController.Instance.getTagMap(GuiNpcMobSpawner.activeTab); + populateRawListWithTags(); + scroll.setList(getSearchList(), GuiNpcMobSpawner.ascending == 0, GuiNpcMobSpawner.sortType == 0); + } + + private void showFolderClones() { + if (GuiNpcMobSpawner.showingClones == 2) { + PacketClient.sendClient(new CloneTagListPacket(GuiNpcMobSpawner.activeFolder)); + PacketClient.sendClient(new CloneListPacket(GuiNpcMobSpawner.activeFolder)); + return; + } + + if (ClientCloneController.Instance != null) { + if (GuiNpcMobSpawner.sortType == 0) { + this.list = ClientCloneController.Instance.getClones(GuiNpcMobSpawner.activeFolder); + } else { + this.list = ClientCloneController.Instance.getClonesDate(GuiNpcMobSpawner.activeFolder); + } + this.tagMap = ClientTagMapController.Instance.getTagMap(GuiNpcMobSpawner.activeFolder); + } else { + this.list = new ArrayList<>(); + this.tagMap = null; + } + + populateRawListWithTags(); + scroll.setList(getSearchList(), GuiNpcMobSpawner.ascending == 0, GuiNpcMobSpawner.sortType == 0); + } + + private void showEntities() { + Map data = EntityList.stringToClassMapping; + ArrayList list = new ArrayList(); + for (Object name : data.keySet()) { + Class c = (Class) data.get(name); + try { + if (EntityLiving.class.isAssignableFrom(c) + && c.getConstructor(new Class[]{World.class}) != null + && !Modifier.isAbstract(c.getModifiers())) + list.add(name.toString()); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + } + } + this.list = list; + this.rawList = new ArrayList(this.list); + scroll.setList(getSearchList(), GuiNpcMobSpawner.ascending == 0, GuiNpcMobSpawner.sortType == 0); + } + + private void populateRawListWithTags() { + this.rawList = new ArrayList<>(this.list); + if (this.tagMap != null) { + if (GuiNpcMobSpawner.displayTags == 0 || GuiNpcMobSpawner.displayTags == 1) { + for (int i = 0; i < this.list.size(); i++) { + StringBuilder npcName = new StringBuilder(list.get(i)); + if (this.tagMap.hasClone(list.get(i))) { + for (UUID tagUUID : this.tagMap.getUUIDsList(list.get(i))) { + Tag tag = GuiNpcMobSpawner.tags.get(tagUUID); + if (tag != null) { + if (GuiNpcMobSpawner.displayTags == 1 || !tag.getIsHidden()) { + npcName.append(" [" + tag.name + "]"); + } + } + } + } + this.rawList.set(i, npcName.toString()); + } + } + } + } + + private NBTTagCompound getCompound() { + String sel = scroll.getSelected(); + if (sel == null) + return null; + + if (GuiNpcMobSpawner.showingClones == 0) { + if (GuiNpcMobSpawner.activeFolder != null && ClientCloneController.Instance != null) { + return ClientCloneController.Instance.getCloneData(player, sel, GuiNpcMobSpawner.activeFolder); + } + return ClientCloneController.Instance.getCloneData(player, sel, GuiNpcMobSpawner.activeTab); + } else { + Entity entity = EntityList.createEntityByName(sel, Minecraft.getMinecraft().theWorld); + if (entity == null) + return null; + NBTTagCompound compound = new NBTTagCompound(); + entity.writeToNBTOptional(compound); + return compound; + } + } + + // ==================== SEARCH / FILTER ==================== + private List getSearchList() { + if (this.list == null) { + this.list = new ArrayList(); + } + if (this.rawList == null) { + this.rawList = new ArrayList(); + } + + if (GuiNpcMobSpawner.tagFilters.size() == 0 || GuiNpcMobSpawner.showingClones == 1) { + if (GuiNpcMobSpawner.search.isEmpty()) + return new ArrayList(this.list); + else { + List list = new ArrayList(); + for (int i = 0; i < this.list.size(); i++) { + if (this.rawList.get(i).toLowerCase().contains(GuiNpcMobSpawner.search)) { + list.add(this.list.get(i)); + } + } + return list; + } + } + + if (GuiNpcMobSpawner.search.isEmpty()) { + List list = new ArrayList(); + for (String name : this.list) { + if (tagMap.hasClone(name)) { + if (meetsCondition(name)) { + list.add(name); + } + } + } + return list; + } + + List list = new ArrayList(); + for (int i = 0; i < this.list.size(); i++) { + if (this.rawList.get(i).toLowerCase().contains(GuiNpcMobSpawner.search)) { + String npcName = this.list.get(i); + if (tagMap.hasClone(npcName)) { + if (meetsCondition(npcName)) { + list.add(npcName); + } + } + } + } + return list; + } + + private boolean meetsCondition(String name) { + if (tagMap == null) { + return true; + } + boolean conditionMet = true; + boolean allRequirement = GuiNpcMobSpawner.filterCondition == 1 || GuiNpcMobSpawner.filterCondition == 3; + + for (String tagName : GuiNpcMobSpawner.tagFilters) { + UUID tagUUID = GuiNpcMobSpawner.tagNames.get(tagName); + if (tagUUID != null) { + boolean hasTag = tagMap.hasTag(name, tagUUID); + if (!allRequirement) { + if (!hasTag && GuiNpcMobSpawner.filterCondition == 0) { + conditionMet = false; + break; + } else if (hasTag && GuiNpcMobSpawner.filterCondition == 2) { + conditionMet = false; + break; + } + } else { + if (!hasTag && GuiNpcMobSpawner.filterCondition == 1) { + conditionMet = false; + break; + } else if (hasTag && GuiNpcMobSpawner.filterCondition == 3) { + conditionMet = false; + break; + } + } + } + } + return conditionMet; + } + + private List getTagList() { + if (this.tagList == null) { + this.tagList = new ArrayList(); + } + if (GuiNpcMobSpawner.tagSearch.isEmpty()) + return new ArrayList(GuiNpcMobSpawner.tagNames.keySet()); + + List list = new ArrayList(); + for (String name : this.tagList) { + if (name.toLowerCase().contains(GuiNpcMobSpawner.tagSearch)) + list.add(name); + } + return list; + } + + // ==================== SCROLL EVENTS ==================== + @Override + public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) { + if (guiCustomScroll.id == 2) { + String selected = navScroll.getSelected(); + handleNavClick(selected); + } + } + + @Override + public void customScrollDoubleClicked(String selection, GuiCustomScroll guiCustomScroll) { + } + + // ==================== KEYBOARD ==================== + public void keyTyped(char c, int i) { + super.keyTyped(c, i); + if (getTextField(1) != null) { + if (GuiNpcMobSpawner.search.equals(getTextField(1).getText())) + return; + GuiNpcMobSpawner.search = getTextField(1).getText().toLowerCase(); + scroll.setList(getSearchList(), GuiNpcMobSpawner.ascending == 0, GuiNpcMobSpawner.sortType == 0); + } + if (getTextField(2) != null) { + if (GuiNpcMobSpawner.tagSearch.equals(getTextField(2).getText())) + return; + GuiNpcMobSpawner.tagSearch = getTextField(2).getText().toLowerCase(); + filterScroll.setList(getTagList()); + } + if (getTextField(3) != null) { + String newNavSearch = getTextField(3).getText().toLowerCase(); + if (!navSearch.equals(newNavSearch)) { + navSearch = newNavSearch; + buildNavList(); + navScroll.setListWithIcons(navNames, navIcons); + navScroll.setSelected(getNavSelection()); + } + } + } + + // ==================== BUTTON ACTIONS ==================== + @Override + protected void actionPerformed(GuiButton guibutton) { + int id = guibutton.id; + + // Mode buttons + if (id == 3) { + GuiNpcMobSpawner.showingClones = 0; + initGui(); + } + if (id == 4) { + GuiNpcMobSpawner.showingClones = 1; + initGui(); + } + if (id == 5) { + GuiNpcMobSpawner.showingClones = 2; + initGui(); + } + if (id == 16) { + GuiNpcMobSpawner.showingClones = 3; + initGui(); + } + if (id == 17) { + close(); + } + + // Minimize — return to normal GUI + if (id == 61) { + Minecraft.getMinecraft().displayGuiScreen( + new GuiNpcMobSpawner(posX, posY, posZ)); + } + + // Spawn + if (id == 1) { + if (GuiNpcMobSpawner.showingClones == 2) { + String sel = scroll.getSelected(); + if (sel == null) return; + if (GuiNpcMobSpawner.activeFolder != null) + SpawnMobPacket.ServerFolder(posX, posY, posZ, sel, GuiNpcMobSpawner.activeFolder); + else + SpawnMobPacket.Server(posX, posY, posZ, sel, GuiNpcMobSpawner.activeTab); + close(); + } else { + NBTTagCompound compound = getCompound(); + if (compound == null) return; + SpawnMobPacket.Client(posX, posY, posZ, compound); + close(); + } + } + + // Mob Spawner + if (id == 2) { + if (GuiNpcMobSpawner.showingClones == 2) { + String sel = scroll.getSelected(); + if (sel == null) return; + if (GuiNpcMobSpawner.activeFolder != null) + MobSpawnerPacket.ServerFolder(posX, posY, posZ, sel, GuiNpcMobSpawner.activeFolder); + else + MobSpawnerPacket.Server(posX, posY, posZ, sel, GuiNpcMobSpawner.activeTab); + close(); + } else { + NBTTagCompound compound = getCompound(); + if (compound == null) return; + MobSpawnerPacket.Client(posX, posY, posZ, compound); + close(); + } + } + + // Remove + if (id == 6) { + if (scroll.getSelected() != null) { + if (GuiNpcMobSpawner.showingClones == 2) { + if (GuiNpcMobSpawner.activeFolder != null) + PacketClient.sendClient(new CloneRemovePacket(GuiNpcMobSpawner.activeFolder, scroll.getSelected())); + else + PacketClient.sendClient(new CloneRemovePacket(GuiNpcMobSpawner.activeTab, scroll.getSelected())); + return; + } + if (GuiNpcMobSpawner.activeFolder != null && ClientCloneController.Instance != null) + ClientCloneController.Instance.removeClone(scroll.getSelected(), GuiNpcMobSpawner.activeFolder); + else + ClientCloneController.Instance.removeClone(scroll.getSelected(), GuiNpcMobSpawner.activeTab); + scroll.selected = -1; + initGui(); + } + } + + // Move + if (id == 50) { + if (scroll.getSelected() != null) { + setSubGui(new SubGuiCloneMove()); + } + } + + // Filter controls + if (id == 11) { + GuiNpcMobSpawner.tagFilters = new HashSet<>(); + filterScroll.setSelectedList(GuiNpcMobSpawner.tagFilters); + initGui(); + } + if (id == 12) { + GuiNpcMobSpawner.displayTags = (byte) ((GuiNpcButton) guibutton).getValue(); + } + if (id == 13) { + GuiNpcMobSpawner.filterCondition = (byte) ((GuiNpcButton) guibutton).getValue(); + } + if (id == 14) { + GuiNpcMobSpawner.ascending = (byte) ((GuiNpcButton) guibutton).getValue(); + } + if (id == 15) { + GuiNpcMobSpawner.sortType = (byte) ((GuiNpcButton) guibutton).getValue(); + } + + // Create folder + if (id == 40) { + setSubGui(new SubGuiCloneFolderName("")); + } + } + + // ==================== SUB GUI ==================== + @Override + public void subGuiClosed(SubGuiInterface subgui) { + if (subgui instanceof SubGuiCloneFolderName) { + SubGuiCloneFolderName folderGui = (SubGuiCloneFolderName) subgui; + if (folderGui.cancelled) return; + + String newName = folderGui.getFolderName(); + if (newName == null || newName.isEmpty()) return; + + if (folderGui.isRename()) { + if (GuiNpcMobSpawner.showingClones == 2) { + PacketClient.sendClient(new CloneFolderCrudPacket( + CloneFolderCrudPacket.ACTION_RENAME, folderGui.getOriginalName(), newName)); + } else if (ClientCloneController.Instance != null) { + ClientCloneController.Instance.renameFolder(folderGui.getOriginalName(), newName); + loadFolderNames(); + } + GuiNpcMobSpawner.activeFolder = newName; + GuiNpcMobSpawner.activeTab = -1; + } else { + if (GuiNpcMobSpawner.showingClones == 2) { + PacketClient.sendClient(new CloneFolderCrudPacket( + CloneFolderCrudPacket.ACTION_CREATE, newName)); + } else if (ClientCloneController.Instance != null) { + ClientCloneController.Instance.createFolder(newName); + loadFolderNames(); + } + GuiNpcMobSpawner.activeFolder = newName; + GuiNpcMobSpawner.activeTab = -1; + } + initGui(); + } + + if (subgui instanceof SubGuiCloneMove) { + SubGuiCloneMove moveGui = (SubGuiCloneMove) subgui; + if (!moveGui.cancelled) { + String cloneName = scroll.getSelected(); + if (cloneName != null) { + int fromTab = GuiNpcMobSpawner.activeFolder != null ? -1 : GuiNpcMobSpawner.activeTab; + String fromFolder = GuiNpcMobSpawner.activeFolder; + int toTab = moveGui.getDestTab(); + String toFolder = moveGui.getDestFolder(); + + if (GuiNpcMobSpawner.showingClones == 2) { + PacketClient.sendClient(new CloneMovePacket( + cloneName, fromTab, fromFolder, toTab, toFolder)); + } else if (ClientCloneController.Instance != null) { + if (fromFolder != null && toFolder != null) { + ClientCloneController.Instance.moveClone(cloneName, fromFolder, toFolder); + } else if (fromFolder != null) { + ClientCloneController.Instance.moveClone(cloneName, fromFolder, toTab); + } else if (toFolder != null) { + ClientCloneController.Instance.moveClone(cloneName, fromTab, toFolder); + } else { + ClientCloneController.Instance.moveClone(cloneName, fromTab, toTab); + } + } + initGui(); + } + } + } + } + + // ==================== DATA FROM SERVER ==================== + @Override + public void save() { + } + + @Override + public void setGuiData(NBTTagCompound compound) { + if (compound.hasKey("CloneFolders")) { + NBTTagList folderList = compound.getTagList("CloneFolders", 10); + folderNames.clear(); + for (int i = 0; i < folderList.tagCount(); i++) { + CloneFolder folder = new CloneFolder(); + folder.readNBT(folderList.getCompoundTagAt(i)); + folderNames.add(folder.name); + } + initGui(); + } else if (compound.hasKey("MoveSuccess")) { + initGui(); + } else if (compound.hasKey("CloneTags")) { + if (GuiNpcMobSpawner.activeFolder != null) { + this.tagMap = new TagMap(GuiNpcMobSpawner.activeFolder); + } else { + this.tagMap = new TagMap(GuiNpcMobSpawner.activeTab); + } + NBTTagCompound cloneTags = compound.getCompoundTag("CloneTags"); + this.tagMap.readNBT(cloneTags); + } else if (compound.hasKey("AllTags")) { + NBTTagList validTags = compound.getTagList("AllTags", 10); + if (validTags != null) { + HashMap tagsUpdate = new HashMap<>(); + HashMap tagNamesUpdate = new HashMap<>(); + for (int j = 0; j < validTags.tagCount(); j++) { + NBTTagCompound tagStructure = validTags.getCompoundTagAt(j); + Tag tag = new Tag(); + tag.readNBT(tagStructure); + tagsUpdate.put(tag.uuid, tag); + tagNamesUpdate.put(tag.name, tag.uuid); + } + GuiNpcMobSpawner.tags = tagsUpdate; + GuiNpcMobSpawner.tagNames = tagNamesUpdate; + this.tagList = new ArrayList(GuiNpcMobSpawner.tagNames.keySet()); + filterScroll.setList(getTagList()); + } + } else { + List list = new ArrayList(); + if (GuiNpcMobSpawner.sortType == 1) { + NBTTagList nbtlist = compound.getTagList("ListDate", 8); + for (int i = 0; i < nbtlist.tagCount(); i++) { + list.add(nbtlist.getStringTagAt(i)); + } + } else { + NBTTagList nbtlist = compound.getTagList("List", 8); + for (int i = 0; i < nbtlist.tagCount(); i++) { + list.add(nbtlist.getStringTagAt(i)); + } + } + + this.list = list; + populateRawListWithTags(); + scroll.setList(getSearchList(), GuiNpcMobSpawner.ascending == 0, GuiNpcMobSpawner.sortType == 0); + } + } +} diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiCloneFolderName.java b/src/main/java/noppes/npcs/client/gui/SubGuiCloneFolderName.java new file mode 100644 index 000000000..508b00518 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/SubGuiCloneFolderName.java @@ -0,0 +1,67 @@ +package noppes.npcs.client.gui; + +import net.minecraft.client.gui.GuiButton; +import noppes.npcs.client.gui.util.GuiNpcButton; +import noppes.npcs.client.gui.util.GuiNpcLabel; +import noppes.npcs.client.gui.util.GuiNpcTextField; +import noppes.npcs.client.gui.util.SubGuiInterface; +import noppes.npcs.controllers.data.CloneFolder; + +public class SubGuiCloneFolderName extends SubGuiInterface { + private String originalName; + public boolean cancelled = true; + private String folderName = ""; + + public SubGuiCloneFolderName(String existingName) { + this.originalName = existingName; + this.folderName = existingName; + xSize = 200; + ySize = 80; + this.closeOnEsc = true; + } + + @Override + public void initGui() { + super.initGui(); + + String title = isRename() ? "Rename Folder" : "New Folder"; + addLabel(new GuiNpcLabel(0, title, guiLeft + 5, guiTop + 5)); + addTextField(new GuiNpcTextField(0, this, fontRendererObj, guiLeft + 5, guiTop + 20, 190, 20, folderName)); + + addButton(new GuiNpcButton(0, guiLeft + 5, guiTop + 50, 90, 20, "gui.done")); + addButton(new GuiNpcButton(1, guiLeft + 100, guiTop + 50, 90, 20, "gui.cancel")); + } + + @Override + protected void actionPerformed(GuiButton guibutton) { + if (guibutton.id == 0) { + // Done + folderName = getTextField(0).getText().trim(); + if (CloneFolder.isValidName(folderName)) { + cancelled = false; + close(); + } + } + if (guibutton.id == 1) { + // Cancel + cancelled = true; + close(); + } + } + + public boolean isRename() { + return originalName != null && !originalName.isEmpty(); + } + + public String getFolderName() { + return folderName; + } + + public String getOriginalName() { + return originalName; + } + + @Override + public void save() { + } +} diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiCloneMove.java b/src/main/java/noppes/npcs/client/gui/SubGuiCloneMove.java new file mode 100644 index 000000000..5f2575cd6 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/SubGuiCloneMove.java @@ -0,0 +1,100 @@ +package noppes.npcs.client.gui; + +import net.minecraft.client.gui.GuiButton; +import noppes.npcs.client.controllers.ClientCloneController; +import noppes.npcs.client.gui.util.GuiButtonBiDirectional; +import noppes.npcs.client.gui.util.GuiNpcButton; +import noppes.npcs.client.gui.util.GuiNpcLabel; +import noppes.npcs.client.gui.util.SubGuiInterface; +import noppes.npcs.controllers.data.CloneFolder; + +import java.util.ArrayList; + +public class SubGuiCloneMove extends SubGuiInterface { + public boolean cancelled = true; + private int destTab = 1; + private String destFolder = null; + + private int[] tabValues; + private String[] folderValues; + + public SubGuiCloneMove() { + xSize = 200; + ySize = 80; + closeOnEsc = true; + } + + @Override + public void initGui() { + super.initGui(); + + ArrayList labels = new ArrayList<>(); + ArrayList tabs = new ArrayList<>(); + ArrayList folders = new ArrayList<>(); + + for (int i = 1; i <= 15; i++) { + labels.add("Tab " + i); + tabs.add(i); + folders.add(null); + } + + if (ClientCloneController.Instance != null) { + for (CloneFolder f : ClientCloneController.Instance.getFolderList()) { + labels.add(f.name); + tabs.add(-1); + folders.add(f.name); + } + } + + tabValues = new int[tabs.size()]; + for (int i = 0; i < tabs.size(); i++) tabValues[i] = tabs.get(i); + folderValues = folders.toArray(new String[0]); + + String[] labelArr = labels.toArray(new String[0]); + int selectedIndex = 0; + if (destFolder != null) { + for (int i = 0; i < folderValues.length; i++) { + if (destFolder.equals(folderValues[i])) { + selectedIndex = i; + break; + } + } + } else if (destTab >= 1 && destTab <= 15) { + selectedIndex = destTab - 1; + } + + addLabel(new GuiNpcLabel(0, "Move to:", guiLeft + 5, guiTop + 5)); + addButton(new GuiButtonBiDirectional(2, guiLeft + 5, guiTop + 20, 190, 20, labelArr, selectedIndex)); + addButton(new GuiNpcButton(0, guiLeft + 5, guiTop + 50, 90, 20, "gui.done")); + addButton(new GuiNpcButton(1, guiLeft + 100, guiTop + 50, 90, 20, "gui.cancel")); + } + + @Override + protected void actionPerformed(GuiButton guibutton) { + if (guibutton.id == 0) { + int index = getButton(2).getValue(); + if (index >= 0 && index < tabValues.length) { + destTab = tabValues[index]; + destFolder = folderValues[index]; + } + cancelled = false; + close(); + } + if (guibutton.id == 1) { + cancelled = true; + close(); + } + } + + public int getDestTab() { + return destTab; + } + + public String getDestFolder() { + return destFolder; + } + + @Override + public void save() { + } +} diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiCustomScrollCloner.java b/src/main/java/noppes/npcs/client/gui/util/GuiCustomScrollCloner.java index a69d449e7..ef0c48ae9 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiCustomScrollCloner.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiCustomScrollCloner.java @@ -1,5 +1,6 @@ package noppes.npcs.client.gui.util; +import net.minecraft.client.gui.GuiScreen; import net.minecraft.util.StatCollector; import noppes.npcs.client.gui.GuiNpcMobSpawner; import noppes.npcs.controllers.data.Tag; @@ -7,10 +8,10 @@ import noppes.npcs.controllers.data.Tag; import java.util.UUID; public class GuiCustomScrollCloner extends GuiCustomScroll { - private final GuiNpcMobSpawner parent; + private final IClonerGui parent; - public GuiCustomScrollCloner(GuiNpcMobSpawner parent, int id) { - super(parent, id); + public GuiCustomScrollCloner(IClonerGui parent, int id) { + super((GuiScreen) parent, id); this.parent = parent; } @@ -50,12 +51,12 @@ public class GuiCustomScrollCloner extends GuiCustomScroll { fontRendererObj.drawString(text, j, k, this.colors.getOrDefault(text, 0xffffff)); } - if ((this.parent.getShowingClones() == 0 || this.parent.getShowingClones() == 2) && this.parent.tagMap != null) { + if ((parent.getShowingClones() == 0 || parent.getShowingClones() == 2) && parent.getTagMap() != null) { int tagStartX = j + fontRendererObj.getStringWidth(displayString) + 3; if (GuiNpcMobSpawner.displayTags == 0 || GuiNpcMobSpawner.displayTags == 1) { - if (this.parent.tagMap.hasClone(list.get(i))) { - for (UUID tagUUID : this.parent.tagMap.getUUIDsList(list.get(i))) { - Tag tag = this.parent.tags.get(tagUUID); + if (parent.getTagMap().hasClone(list.get(i))) { + for (UUID tagUUID : parent.getTagMap().getUUIDsList(list.get(i))) { + Tag tag = parent.getTags().get(tagUUID); if (tag != null) { if (GuiNpcMobSpawner.displayTags == 1 || !tag.getIsHidden()) { fontRendererObj.drawString("[" + tag.name + "]", tagStartX, k, tag.color); diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiCustomScrollIcons.java b/src/main/java/noppes/npcs/client/gui/util/GuiCustomScrollIcons.java new file mode 100644 index 000000000..d2b2a64b8 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/util/GuiCustomScrollIcons.java @@ -0,0 +1,81 @@ +package noppes.npcs.client.gui.util; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.util.StatCollector; + +import java.util.ArrayList; +import java.util.List; + +public class GuiCustomScrollIcons extends GuiCustomScroll { + public static final int ICON_NONE = 0; + public static final int ICON_TAB = 1; + public static final int ICON_FOLDER = 2; + + private static final int ICON_WIDTH = 14; + private static final int FOLDER_COLOR = 0xFFDDAA00; + private static final int TAB_COLOR = 0xFF6688AA; + + private List iconTypes = new ArrayList<>(); + + public GuiCustomScrollIcons(GuiScreen parent, int id) { + super(parent, id); + } + + public void setListWithIcons(List names, List icons) { + this.setUnsortedList(names); + this.iconTypes = new ArrayList<>(icons); + } + + @Override + protected void drawItems() { + for (int i = 0; i < list.size(); i++) { + int j = 4; + int k = (14 * i + 4) - scrollY; + if (k >= 4 && k + 12 < ySize) { + int xOffset = scrollHeight < ySize - 8 ? 0 : 10; + + int iconType = (i < iconTypes.size()) ? iconTypes.get(i) : ICON_NONE; + int textX = j; + if (iconType != ICON_NONE) { + textX = j + ICON_WIDTH; + } + + // Draw icon + if (iconType == ICON_FOLDER) { + drawRect(j, k - 1, j + 10, k + 7, FOLDER_COLOR); + } else if (iconType == ICON_TAB) { + drawRect(j, k - 1, j + 10, k + 7, TAB_COLOR); + } + + // Draw text + String displayString = StatCollector.translateToLocal(list.get(i)); + String text = ""; + float maxWidth = (xSize + xOffset - 8 - (iconType != ICON_NONE ? ICON_WIDTH : 0)) * 0.8f; + if (fontRendererObj.getStringWidth(displayString) > maxWidth) { + for (int h = 0; h < displayString.length(); h++) { + char c = displayString.charAt(h); + text += c; + if (fontRendererObj.getStringWidth(text) > maxWidth) + break; + } + if (displayString.length() > text.length()) + text += "..."; + } else { + text = displayString; + } + + if ((multipleSelection && selectedList.contains(list.get(i))) || (!multipleSelection && selected == i)) { + drawVerticalLine(j - 2, k - 4, k + 10, 0xffffffff); + drawVerticalLine(j + xSize - 18 + xOffset, k - 4, k + 10, 0xffffffff); + drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k - 3, 0xffffffff); + drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k + 10, 0xffffffff); + fontRendererObj.drawString(text, textX, k, 0xffffff); + } else if (i == hover) { + fontRendererObj.drawString(text, textX, k, 0x00ff00); + } else { + fontRendererObj.drawString(text, textX, k, 0xffffff); + } + } + } + } +} diff --git a/src/main/java/noppes/npcs/client/gui/util/IClonerGui.java b/src/main/java/noppes/npcs/client/gui/util/IClonerGui.java new file mode 100644 index 000000000..db4989b56 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/util/IClonerGui.java @@ -0,0 +1,13 @@ +package noppes.npcs.client.gui.util; + +import noppes.npcs.controllers.data.Tag; +import noppes.npcs.controllers.data.TagMap; + +import java.util.HashMap; +import java.util.UUID; + +public interface IClonerGui { + int getShowingClones(); + HashMap getTags(); + TagMap getTagMap(); +} diff --git a/src/main/java/noppes/npcs/controllers/ServerCloneController.java b/src/main/java/noppes/npcs/controllers/ServerCloneController.java index ea61a0112..5a0744089 100644 --- a/src/main/java/noppes/npcs/controllers/ServerCloneController.java +++ b/src/main/java/noppes/npcs/controllers/ServerCloneController.java @@ -15,6 +15,7 @@ import noppes.npcs.api.IPos; import noppes.npcs.api.IWorld; import noppes.npcs.api.entity.IEntity; import noppes.npcs.api.handler.ICloneHandler; +import noppes.npcs.controllers.data.CloneFolder; import noppes.npcs.controllers.data.TagMap; import noppes.npcs.scripted.CustomNPCsException; import noppes.npcs.scripted.NpcAPI; @@ -27,6 +28,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -34,8 +36,11 @@ import java.util.UUID; public class ServerCloneController implements ICloneHandler { public static ServerCloneController Instance; + protected Map folders = new LinkedHashMap<>(); + public ServerCloneController() { loadClones(); + loadFolders(); } private void loadClones() { @@ -103,6 +108,8 @@ public class ServerCloneController implements ICloneHandler { return clones; } + // ==================== Tab-Based Clone Operations ==================== + public NBTTagCompound getCloneData(ICommandSender player, String name, int tab) { File file = new File(new File(getDir(), tab + ""), name + ".json"); if (!file.exists()) { @@ -194,6 +201,270 @@ public class ServerCloneController implements ICloneHandler { return name; } + // ==================== Folder Registry ==================== + + public void loadFolders() { + folders.clear(); + try { + File file = new File(getDir(), "___folders.json"); + if (file.exists()) { + NBTTagCompound compound = NBTJsonUtil.LoadFile(file); + NBTTagList list = compound.getTagList("Folders", 10); + for (int i = 0; i < list.tagCount(); i++) { + CloneFolder folder = new CloneFolder(); + folder.readNBT(list.getCompoundTagAt(i)); + if (CloneFolder.isValidName(folder.name)) { + folders.put(folder.name, folder); + } + } + } + } catch (Exception e) { + LogWriter.except(e); + } + } + + public void saveFolders() { + try { + NBTTagCompound compound = new NBTTagCompound(); + NBTTagList list = new NBTTagList(); + for (CloneFolder folder : folders.values()) { + list.appendTag(folder.writeNBT(new NBTTagCompound())); + } + compound.setTag("Folders", list); + + File file = new File(getDir(), "___folders.json_new"); + File file2 = new File(getDir(), "___folders.json"); + NBTJsonUtil.SaveFile(file, compound); + if (file2.exists()) { + file2.delete(); + } + file.renameTo(file2); + } catch (Exception e) { + LogWriter.except(e); + } + } + + public List getFolderList() { + return new ArrayList<>(folders.values()); + } + + public List getFolderNames() { + return new ArrayList<>(folders.keySet()); + } + + public boolean hasFolder(String name) { + return folders.containsKey(name); + } + + // ==================== Folder CRUD ==================== + + public CloneFolder createFolder(String name) { + if (!CloneFolder.isValidName(name) || folders.containsKey(name)) { + return null; + } + CloneFolder folder = new CloneFolder(name); + File dir = getFolderDir(name); + if (!dir.exists()) { + dir.mkdir(); + } + folders.put(name, folder); + saveFolders(); + return folder; + } + + public boolean renameFolder(String oldName, String newName) { + if (!folders.containsKey(oldName) || !CloneFolder.isValidName(newName) || folders.containsKey(newName)) { + return false; + } + CloneFolder folder = folders.get(oldName); + File oldDir = getFolderDir(oldName); + File newDir = new File(getDir(), "folder_" + newName); + if (!oldDir.renameTo(newDir)) { + return false; + } + folders.remove(oldName); + folder.name = newName; + folders.put(newName, folder); + saveFolders(); + return true; + } + + public boolean deleteFolder(String name) { + if (!folders.containsKey(name)) { + return false; + } + List clones = getClones(name); + if (!clones.isEmpty()) { + return false; + } + File dir = getFolderDir(name); + if (dir.exists()) { + File tagMapFile = new File(dir, "___tagmap.dat"); + if (tagMapFile.exists()) tagMapFile.delete(); + File tagMapOld = new File(dir, "___tagmap.dat_old"); + if (tagMapOld.exists()) tagMapOld.delete(); + File tagMapNew = new File(dir, "___tagmap.dat_new"); + if (tagMapNew.exists()) tagMapNew.delete(); + dir.delete(); + } + folders.remove(name); + saveFolders(); + return true; + } + + // ==================== Folder-Based Clone Operations ==================== + + public File getFolderDir(String folderName) { + File dir = new File(getDir(), "folder_" + folderName); + if (!dir.exists()) + dir.mkdir(); + return dir; + } + + public List getClones(String folderName) { + List list = new ArrayList<>(); + File dir = getFolderDir(folderName); + if (!dir.exists() || !dir.isDirectory()) + return list; + for (String file : dir.list()) { + if (file.endsWith(".json")) + list.add(file.substring(0, file.length() - 5)); + } + return list; + } + + public List getClonesDate(String folderName) { + List list = new ArrayList<>(); + File dir = getFolderDir(folderName); + if (!dir.exists() || !dir.isDirectory()) + return list; + File[] files = dir.listFiles(); + if (files == null) return list; + Arrays.sort(files, new Comparator() { + public int compare(File f1, File f2) { + return Long.compare(f1.lastModified(), f2.lastModified()); + } + }); + for (File file : files) { + String fileName = file.getName(); + if (fileName.endsWith(".json")) + list.add(fileName.substring(0, fileName.length() - 5)); + } + return list; + } + + public NBTTagCompound getCloneData(ICommandSender player, String name, String folderName) { + File file = new File(getFolderDir(folderName), name + ".json"); + if (!file.exists()) { + if (player != null) + player.addChatMessage(new ChatComponentText("Could not find clone file")); + return null; + } + try { + return NBTJsonUtil.LoadFile(file); + } catch (Exception e) { + LogWriter.error("Error loading: " + file.getAbsolutePath(), e); + if (player != null) + player.addChatMessage(new ChatComponentText(e.getMessage())); + } + return null; + } + + public void saveClone(String folderName, String name, NBTTagCompound compound) { + try { + File dir = getFolderDir(folderName); + String filename = name + ".json"; + File file = new File(dir, filename + "_new"); + File file2 = new File(dir, filename); + NBTJsonUtil.SaveFile(file, compound); + addToTagMap(compound, name, folderName); + if (file2.exists()) { + file2.delete(); + } + file.renameTo(file2); + } catch (Exception e) { + LogWriter.except(e); + } + } + + public boolean removeClone(String name, String folderName) { + File file = new File(getFolderDir(folderName), name + ".json"); + if (!file.exists()) + return false; + file.delete(); + removeFromTagMap(name, folderName); + return true; + } + + public String addClone(NBTTagCompound nbttagcompound, String name, String folderName) { + cleanTags(nbttagcompound); + saveClone(folderName, name, nbttagcompound); + return name; + } + + public String addClone(NBTTagCompound nbttagcompound, String name, String folderName, NBTTagCompound tempTags) { + cleanTagList(nbttagcompound, tempTags); + cleanTags(nbttagcompound); + saveClone(folderName, name, nbttagcompound); + return name; + } + + // ==================== Move Operations ==================== + + public boolean moveClone(String cloneName, int fromTab, String toFolder) { + NBTTagCompound data = getCloneData(null, cloneName, fromTab); + if (data == null) return false; + + HashSet tagUUIDs = getTagUUIDsFromTab(cloneName, fromTab); + + saveClone(toFolder, cloneName, data); + setTagUUIDsForFolder(cloneName, toFolder, tagUUIDs); + + removeClone(cloneName, fromTab); + return true; + } + + public boolean moveClone(String cloneName, String fromFolder, int toTab) { + NBTTagCompound data = getCloneData(null, cloneName, fromFolder); + if (data == null) return false; + + HashSet tagUUIDs = getTagUUIDsFromFolder(cloneName, fromFolder); + + saveClone(toTab, cloneName, data); + setTagUUIDsForTab(cloneName, toTab, tagUUIDs); + + removeClone(cloneName, fromFolder); + return true; + } + + public boolean moveClone(String cloneName, String fromFolder, String toFolder) { + NBTTagCompound data = getCloneData(null, cloneName, fromFolder); + if (data == null) return false; + + HashSet tagUUIDs = getTagUUIDsFromFolder(cloneName, fromFolder); + + saveClone(toFolder, cloneName, data); + setTagUUIDsForFolder(cloneName, toFolder, tagUUIDs); + + removeClone(cloneName, fromFolder); + return true; + } + + public boolean moveClone(String cloneName, int fromTab, int toTab) { + NBTTagCompound data = getCloneData(null, cloneName, fromTab); + if (data == null) return false; + + HashSet tagUUIDs = getTagUUIDsFromTab(cloneName, fromTab); + + saveClone(toTab, cloneName, data); + setTagUUIDsForTab(cloneName, toTab, tagUUIDs); + + removeClone(cloneName, fromTab); + return true; + } + + // ==================== Tag Utilities ==================== + public NBTTagCompound cleanTagList(NBTTagCompound nbttagcompound, NBTTagCompound tempTags) { HashSet tagUUIDs = new HashSet(); if (nbttagcompound.hasKey("TagUUIDs")) { @@ -243,6 +514,25 @@ public class ServerCloneController implements ICloneHandler { return true; } + public boolean addToTagMap(NBTTagCompound nbttagcompound, String name, String folderName) { + HashSet tagUUIDs = new HashSet<>(); + if (nbttagcompound.hasKey("TagUUIDs")) { + NBTTagList nbtTagList = nbttagcompound.getTagList("TagUUIDs", 8); + for (int i = 0; i < nbtTagList.tagCount(); i++) { + tagUUIDs.add(UUID.fromString(nbtTagList.getStringTagAt(i))); + } + } + + TagMap tagMap = ServerTagMapController.Instance.getTagMap(folderName); + if (!tagUUIDs.isEmpty()) { + tagMap.putClone(name, tagUUIDs); + } else { + tagMap.removeClone(name); + } + ServerTagMapController.Instance.saveTagMap(tagMap); + return true; + } + public boolean removeFromTagMap(String name, int tab) { TagMap tagMap = ServerTagMapController.Instance.getTagMap(tab); if (tagMap.removeClone(name)) { @@ -252,6 +542,41 @@ public class ServerCloneController implements ICloneHandler { return false; } + public boolean removeFromTagMap(String name, String folderName) { + TagMap tagMap = ServerTagMapController.Instance.getTagMap(folderName); + if (tagMap.removeClone(name)) { + ServerTagMapController.Instance.saveTagMap(tagMap); + return true; + } + return false; + } + + protected HashSet getTagUUIDsFromTab(String cloneName, int tab) { + TagMap tagMap = ServerTagMapController.Instance.getTagMap(tab); + HashSet uuids = tagMap.getUUIDs(cloneName); + return uuids != null ? new HashSet<>(uuids) : new HashSet<>(); + } + + protected HashSet getTagUUIDsFromFolder(String cloneName, String folderName) { + TagMap tagMap = ServerTagMapController.Instance.getTagMap(folderName); + HashSet uuids = tagMap.getUUIDs(cloneName); + return uuids != null ? new HashSet<>(uuids) : new HashSet<>(); + } + + protected void setTagUUIDsForTab(String cloneName, int tab, HashSet tagUUIDs) { + if (tagUUIDs == null || tagUUIDs.isEmpty()) return; + TagMap tagMap = ServerTagMapController.Instance.getTagMap(tab); + tagMap.putClone(cloneName, tagUUIDs); + ServerTagMapController.Instance.saveTagMap(tagMap); + } + + protected void setTagUUIDsForFolder(String cloneName, String folderName, HashSet tagUUIDs) { + if (tagUUIDs == null || tagUUIDs.isEmpty()) return; + TagMap tagMap = ServerTagMapController.Instance.getTagMap(folderName); + tagMap.putClone(cloneName, tagUUIDs); + ServerTagMapController.Instance.saveTagMap(tagMap); + } + public void cleanTags(NBTTagCompound nbttagcompound) { if (nbttagcompound.hasKey("ItemGiverId")) nbttagcompound.setInteger("ItemGiverId", 0); @@ -288,6 +613,8 @@ public class ServerCloneController implements ICloneHandler { } } + // ==================== API Methods (ICloneHandler) ==================== + public IEntity spawn(double x, double y, double z, int tab, String name, IWorld world, boolean ignoreProtection) { NBTTagCompound compound = this.getCloneData((ICommandSender) null, name, tab); if (compound == null) { @@ -327,7 +654,7 @@ public class ServerCloneController implements ICloneHandler { for (File file : dir.listFiles()) { if (file.getName().endsWith(".json")) { NBTTagCompound compound = NBTJsonUtil.LoadFile(file); - Instance.cleanTags(compound); + cleanTags(compound); Entity entity = EntityList.createEntityFromNBT(compound, world.getMCWorld()); arrayList.add(entity == null ? null : NpcAPI.Instance().getIEntity(entity)); } @@ -343,7 +670,7 @@ public class ServerCloneController implements ICloneHandler { if (compound == null) { throw new CustomNPCsException("Unknown clone tab:" + tab + " name:" + name, new Object[0]); } else { - Instance.cleanTags(compound); + cleanTags(compound); Entity entity = EntityList.createEntityFromNBT(compound, world.getMCWorld()); return entity == null ? null : NpcAPI.Instance().getIEntity(entity); } @@ -354,7 +681,6 @@ public class ServerCloneController implements ICloneHandler { return compound != null; } - public void set(int tab, String name, IEntity entity) { NBTTagCompound compound = new NBTTagCompound(); if (!entity.getMCEntity().writeMountToNBT(compound)) @@ -367,4 +693,85 @@ public class ServerCloneController implements ICloneHandler { public void remove(int tab, String name) { this.removeClone(name, tab); } + + // --- Folder API Methods --- + + public String[] getFolders() { + return getFolderNames().toArray(new String[0]); + } + + public IEntity spawn(double x, double y, double z, String folderName, String name, IWorld world, boolean ignoreProtection) { + NBTTagCompound compound = getCloneData(null, name, folderName); + if (compound == null) { + throw new CustomNPCsException("Unknown clone folder:" + folderName + " name:" + name, new Object[0]); + } + Entity entity; + if (!ignoreProtection) { + entity = NoppesUtilServer.spawnCloneWithProtection(compound, (int) x, (int) y, (int) z, world.getMCWorld()); + } else { + entity = NoppesUtilServer.spawnClone(compound, (int) x, (int) y, (int) z, world.getMCWorld()); + } + return entity == null ? null : NpcAPI.Instance().getIEntity(entity); + } + + public IEntity spawn(IPos pos, String folderName, String name, IWorld world, boolean ignoreProtection) { + return this.spawn(pos.getX(), pos.getY(), pos.getZ(), folderName, name, world, ignoreProtection); + } + + public IEntity spawn(double x, double y, double z, String folderName, String name, IWorld world) { + return spawn(x, y, z, folderName, name, world, true); + } + + public IEntity spawn(IPos pos, String folderName, String name, IWorld world) { + return this.spawn(pos.getX(), pos.getY(), pos.getZ(), folderName, name, world); + } + + public IEntity[] getFolder(String folderName, IWorld world) { + File dir = getFolderDir(folderName); + if (!dir.exists() || !dir.isDirectory() || dir.listFiles() == null) { + return new IEntity[]{}; + } + + ArrayList arrayList = new ArrayList<>(); + try { + for (File file : dir.listFiles()) { + if (file.getName().endsWith(".json")) { + NBTTagCompound compound = NBTJsonUtil.LoadFile(file); + cleanTags(compound); + Entity entity = EntityList.createEntityFromNBT(compound, world.getMCWorld()); + arrayList.add(entity == null ? null : NpcAPI.Instance().getIEntity(entity)); + } + } + } catch (Exception ignored) { + } + return arrayList.toArray(new IEntity[]{}); + } + + public IEntity get(String folderName, String name, IWorld world) { + NBTTagCompound compound = getCloneData(null, name, folderName); + if (compound == null) { + throw new CustomNPCsException("Unknown clone folder:" + folderName + " name:" + name, new Object[0]); + } + cleanTags(compound); + Entity entity = EntityList.createEntityFromNBT(compound, world.getMCWorld()); + return entity == null ? null : NpcAPI.Instance().getIEntity(entity); + } + + public boolean has(String folderName, String name) { + NBTTagCompound compound = getCloneData(null, name, folderName); + return compound != null; + } + + public void set(String folderName, String name, IEntity entity) { + NBTTagCompound compound = new NBTTagCompound(); + if (!entity.getMCEntity().writeMountToNBT(compound)) + throw new CustomNPCsException("Cannot save dead entities", new Object[0]); + + this.cleanTags(compound); + this.saveClone(folderName, name, compound); + } + + public void remove(String folderName, String name) { + this.removeClone(name, folderName); + } } diff --git a/src/main/java/noppes/npcs/controllers/ServerTagMapController.java b/src/main/java/noppes/npcs/controllers/ServerTagMapController.java index 0221481e3..ce71cc089 100644 --- a/src/main/java/noppes/npcs/controllers/ServerTagMapController.java +++ b/src/main/java/noppes/npcs/controllers/ServerTagMapController.java @@ -35,6 +35,13 @@ public class ServerTagMapController { return dir; } + public File getCloneFolderDir(String folderName) { + File dir = new File(getDir(), "folder_" + folderName); + if (!dir.exists()) + dir.mkdir(); + return dir; + } + public TagMap getTagMap(int tab) { this.tagMap = new TagMap(tab); try { @@ -65,9 +72,33 @@ public class ServerTagMapController { this.tagMap.readNBT(nbtCompound); } + public TagMap getTagMap(String folderName) { + this.tagMap = new TagMap(folderName); + try { + File file = new File(getCloneFolderDir(folderName), "___tagmap.dat"); + if (file.exists()) { + loadTagMapFile(file); + } + } catch (Exception e) { + try { + File file = new File(getCloneFolderDir(folderName), "___tagmap.dat_old"); + if (file.exists()) { + loadTagMapFile(file); + } + } catch (Exception ignored) { + } + } + return this.tagMap; + } + public void saveTagMap(TagMap tagMap) { try { - File saveDir = getCloneTabDir(tagMap.cloneTab); + File saveDir; + if (tagMap.cloneFolder != null) { + saveDir = getCloneFolderDir(tagMap.cloneFolder); + } else { + saveDir = getCloneTabDir(tagMap.cloneTab); + } File file = new File(saveDir, "___tagmap.dat_new"); File file1 = new File(saveDir, "___tagmap.dat_old"); File file2 = new File(saveDir, "___tagmap.dat"); diff --git a/src/main/java/noppes/npcs/controllers/data/CloneFolder.java b/src/main/java/noppes/npcs/controllers/data/CloneFolder.java new file mode 100644 index 000000000..ceca2e1ca --- /dev/null +++ b/src/main/java/noppes/npcs/controllers/data/CloneFolder.java @@ -0,0 +1,59 @@ +package noppes.npcs.controllers.data; + +import net.minecraft.nbt.NBTTagCompound; + +public class CloneFolder { + public String name; + public long createdDate; + + public CloneFolder() { + this.name = ""; + this.createdDate = System.currentTimeMillis(); + } + + public CloneFolder(String name) { + this.name = name; + this.createdDate = System.currentTimeMillis(); + } + + public void readNBT(NBTTagCompound compound) { + name = compound.getString("Name"); + createdDate = compound.getLong("Created"); + } + + public NBTTagCompound writeNBT(NBTTagCompound compound) { + compound.setString("Name", name); + compound.setLong("Created", createdDate); + return compound; + } + + public String getDirName() { + return "folder_" + name; + } + + public static boolean isValidName(String name) { + if (name == null || name.isEmpty()) { + return false; + } + if (name.length() > 32) { + return false; + } + if (!name.equals(name.trim())) { + return false; + } + try { + Integer.parseInt(name); + return false; + } catch (NumberFormatException ignored) { + } + for (char c : name.toCharArray()) { + if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' || c == '|') { + return false; + } + if (c < 32) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/noppes/npcs/controllers/data/TagMap.java b/src/main/java/noppes/npcs/controllers/data/TagMap.java index e4c0ad57f..2205d96fd 100644 --- a/src/main/java/noppes/npcs/controllers/data/TagMap.java +++ b/src/main/java/noppes/npcs/controllers/data/TagMap.java @@ -13,10 +13,18 @@ import java.util.UUID; public class TagMap { public int cloneTab; + public String cloneFolder; public HashMap> tagMap; public TagMap(int tab) { this.cloneTab = tab; + this.cloneFolder = null; + this.tagMap = new HashMap>(); + } + + public TagMap(String folder) { + this.cloneTab = -1; + this.cloneFolder = folder; this.tagMap = new HashMap>(); }