Merge branch 'cloner_tabs' into dev

This commit is contained in:
Kam committed 2026-02-04 12:55:57 -05:00
commit db7ae16ee9
35 files changed
+3311 -318

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 KiB

+1 -1
Submodule src/api updated: 29ad8637d6...3f8ccc087f.
@@ -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 = "<npc> <tab> [clonedname]",
usage = "<npc> <tab|folder> [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 = "<tab>",
usage = "<tab|folder>",
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<String> 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 = "<name> <tab>",
usage = "<name> <tab|folder>",
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 = "<name> <tab> [[world:]x,y,z]] [newname]",
usage = "<name> <tab|folder> [[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 = "<name> <tab> [[world:]x,y,z]] [newname]",
desc = "Spawn cloned NPC in a grid",
usage = "<name> <tab|folder> <width> <height> [[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 = "<name>",
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 = "<oldname> <newname>",
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 = "<name>",
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 = "<clonename> <from_tab|folder> <to_tab|folder>",
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) {
@@ -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
@@ -163,6 +163,9 @@ public enum EnumRequestPacket {
CloneAllTags,
CloneAllTagsShort,
ClonePreSave,
CloneFolderList,
CloneFolderCrud,
CloneMove,
EventScript,
PlayerScript,
@@ -0,0 +1,111 @@
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:
if (CloneFolder.isValidName(name))
success = ServerCloneController.Instance.createFolder(name) != null;
break;
case ACTION_RENAME:
String newName = ByteBufUtils.readString(in);
if (CloneFolder.isValidName(newName))
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);
}
}
@@ -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);
}
}
@@ -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);
@@ -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);
}
}
@@ -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);
@@ -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);
@@ -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);
}
}
}
@@ -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);
@@ -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;
@@ -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;
@@ -76,6 +76,7 @@ import noppes.npcs.client.gui.GuiBorderBlock;
import noppes.npcs.client.gui.GuiMerchantAdd;
import noppes.npcs.client.gui.GuiNpcDimension;
import noppes.npcs.client.gui.GuiNpcMobSpawner;
import noppes.npcs.client.gui.GuiNpcMobSpawnerFullscreen;
import noppes.npcs.client.gui.GuiNpcMobSpawnerMounter;
import noppes.npcs.client.gui.GuiNpcPather;
import noppes.npcs.client.gui.GuiNpcRedstoneBlock;
@@ -334,7 +335,6 @@ public class ClientProxy extends CommonProxy {
ClientCloneController.Instance = new ClientCloneController();
ClientTagMapController.Instance = new ClientTagMapController();
MinecraftForge.EVENT_BUS.register(new ClientEventHandler());
// Telegraph rendering system
@@ -603,7 +603,9 @@ public class ClientProxy extends CommonProxy {
return new GuiNpcRedstoneBlock(x, y, z);
else if (gui == EnumGuiType.Cloner)
return new GuiNpcMobSpawner(x, y, z);
return GuiNpcMobSpawner.isFullscreen
? new GuiNpcMobSpawnerFullscreen(x, y, z)
: new GuiNpcMobSpawner(x, y, z);
else if (gui == EnumGuiType.MobSpawnerMounter)
return new GuiNpcMobSpawnerMounter(x, y, z);
@@ -41,6 +41,26 @@ public class ClientCloneController extends ServerCloneController {
return true;
}
@Override
public boolean addToTagMap(NBTTagCompound nbttagcompound, String name, String folderName) {
HashSet<UUID> 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<UUID> getTagUUIDsFromTab(String cloneName, int tab) {
TagMap tagMap = ClientTagMapController.Instance.getTagMap(tab);
HashSet<UUID> uuids = tagMap.getUUIDs(cloneName);
return uuids != null ? new HashSet<>(uuids) : new HashSet<>();
}
@Override
protected HashSet<UUID> getTagUUIDsFromFolder(String cloneName, String folderName) {
TagMap tagMap = ClientTagMapController.Instance.getTagMap(folderName);
HashSet<UUID> uuids = tagMap.getUUIDs(cloneName);
return uuids != null ? new HashSet<>(uuids) : new HashSet<>();
}
@Override
protected void setTagUUIDsForTab(String cloneName, int tab, HashSet<UUID> 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<UUID> tagUUIDs) {
if (tagUUIDs == null || tagUUIDs.isEmpty()) return;
TagMap tagMap = ClientTagMapController.Instance.getTagMap(folderName);
tagMap.putClone(cloneName, tagUUIDs);
ClientTagMapController.Instance.saveTagMap(tagMap);
}
}
@@ -2,6 +2,7 @@ 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.CloneFolderListPacket;
import kamkeel.npcs.network.packets.request.clone.CloneListPacket;
import kamkeel.npcs.network.packets.request.clone.CloneRemovePacket;
import kamkeel.npcs.network.packets.request.clone.CloneTagListPacket;
@@ -26,6 +27,8 @@ 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.controllers.data.Tag;
import noppes.npcs.controllers.data.TagMap;
@@ -38,52 +41,84 @@ 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, IClonerGui {
// ==================== STATIC STATE (persists across GUI opens, shared with fullscreen) ====================
public static boolean isFullscreen = false;
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<UUID, Tag> tags = new HashMap<>();
public static HashMap<String, UUID> tagNames = new HashMap<>();
public static HashSet<String> 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;
public TagMap tagMap;
// Scroll position preservation (shared with fullscreen)
public static int savedContentScrollY = 0;
public static int savedNavScrollY = 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<String> list;
private List<String> rawList;
private List<String> tagList;
private static int showingClones = 0;
private static String search = "";
private static String tagSearch = "";
private int activeTab = 1;
// ==================== 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());
if (showingClones == 2) {
PacketClient.sendClient(new CloneFolderListPacket());
}
// Minimized view doesn't support folders — ensure we're on a tab
if (activeFolder != null) {
if (activeTab < 1 || activeTab > 15) {
activeTab = 1;
}
activeFolder = null;
}
}
// ==================== IClonerGui ====================
@Override
public int getShowingClones() {
return showingClones;
}
@Override
public HashMap<UUID, Tag> getTags() {
return tags;
}
@Override
public TagMap getTagMap() {
return tagMap;
}
// ==================== INIT GUI ====================
public void initGui() {
// Save scroll position before clear
savedContentScrollY = scroll.scrollY;
super.initGui();
guiTop += 10;
guiLeft += 30;
@@ -92,6 +127,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,112 +135,133 @@ 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 — build right-to-left to avoid overlap
GuiMenuTopButton closeBtn = new GuiMenuTopButton(17, guiLeft + (xSize - 22), guiTop - 17, "X");
addTopButton(closeBtn);
// Position "+" dynamically to the left of "X"
int plusX = closeBtn.xPosition - (fontRendererObj.getStringWidth("+") + 12) - 2;
GuiMenuTopButton fullscreenBtn = new GuiMenuTopButton(61, plusX, guiTop - 17, "+");
addTopButton(fullscreenBtn);
// Position "Filters" dynamically to the left of "+"
int filterBtnW = fontRendererObj.getStringWidth(StatCollector.translateToLocal("gui.filters")) + 12;
int filterX = fullscreenBtn.xPosition - filterBtnW - 2;
GuiMenuTopButton filterBtn = new GuiMenuTopButton(16, filterX, guiTop - 17, "gui.filters");
filterBtn.active = showingClones == 3;
addTopButton(filterBtn);
// Mode buttons from left
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;
if (showingClones < 3) {
addScroll(scroll);
addTextField(new GuiNpcTextField(1, this, fontRendererObj, guiLeft + 4, guiTop + 4, 293, 20, search));
addButton(new GuiNpcButton(1, guiLeft + 298, guiTop + 6, 52, 20, "item.monsterPlacer.name"));
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;
initTabSideButtons();
showClones();
// Restore scroll position
scroll.scrollY = Math.min(savedContentScrollY, Math.max(0, scroll.maxScrollY));
} else {
showEntities();
scroll.scrollY = Math.min(savedContentScrollY, Math.max(0, scroll.maxScrollY));
}
} 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 (activeTab >= 1 && activeTab <= 15) {
GuiMenuSideButton active = getSideButton(20 + activeTab);
if (active != null) active.active = true;
}
}
public void showFilters() {
// ==================== 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<String> list = new ArrayList<String>();
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<String>(this.list);
scroll.setList(getSearchList(), ascending == 0, sortType == 0);
}
// ==================== CLONE / ENTITY DISPLAY ====================
private void showClones() {
if (showingClones == 2) {
PacketClient.sendClient(new CloneTagListPacket(activeTab));
@@ -219,16 +276,41 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData {
}
this.tagMap = ClientTagMapController.Instance.getTagMap(activeTab);
populateRawListWithTags();
scroll.setList(getSearchList(), ascending == 0, sortType == 0);
}
private void showEntities() {
Map<?, ?> data = EntityList.stringToClassMapping;
ArrayList<String> list = new ArrayList<String>();
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<String>(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 +320,26 @@ 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) {
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<String> getSearchList() {
if (this.list == null) {
this.list = new ArrayList<String>();
@@ -269,10 +350,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<String>(this.list);
// Yes Search
else {
List<String> list = new ArrayList<String>();
for (int i = 0; i < this.list.size(); i++) {
@@ -284,10 +363,7 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData {
}
}
////////////////////////
//// WITH FILTERS
// No Search
// With Filters
if (search.isEmpty()) {
List<String> list = new ArrayList<String>();
for (String name : this.list) {
@@ -300,7 +376,6 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData {
return list;
}
// Yes Search
List<String> list = new ArrayList<String>();
for (int i = 0; i < this.list.size(); i++) {
if (this.rawList.get(i).toLowerCase().contains(search)) {
@@ -311,7 +386,6 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData {
}
}
}
}
return list;
}
@@ -346,7 +420,6 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData {
}
}
}
return conditionMet;
}
@@ -354,10 +427,8 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData {
if (this.tagList == null) {
this.tagList = new ArrayList<String>();
}
// No Search // No Filters - or - Entities
if (tagSearch.isEmpty())
return new ArrayList<String>(tagNames.keySet());
// Yes Search // No Filters - or - Entities
List<String> list = new ArrayList<String>();
for (String name : this.tagList) {
@@ -367,125 +438,162 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData {
return list;
}
private NBTTagCompound getCompound() {
String sel = scroll.getSelected();
if (sel == null)
return null;
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;
}
// ==================== MOUSE / KEYBOARD EVENTS ====================
@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);
GuiNpcTextField searchField = getTextField(1);
if (searchField != null) {
String newText = searchField.getText().toLowerCase();
if (!search.equals(newText)) {
search = newText;
scroll.setList(getSearchList(), ascending == 0, sortType == 0);
}
}
GuiNpcTextField tagSearchField = getTextField(2);
if (tagSearchField != null) {
String newText = tagSearchField.getText().toLowerCase();
if (!tagSearch.equals(newText)) {
tagSearch = newText;
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) {
savedContentScrollY = 0;
showingClones = 0;
initGui();
}
if (id == 4) {
savedContentScrollY = 0;
showingClones = 1;
initGui();
}
if (id == 5) {
savedContentScrollY = 0;
showingClones = 2;
PacketClient.sendClient(new CloneFolderListPacket());
initGui();
}
if (id == 16) {
savedContentScrollY = 0;
showingClones = 3;
initGui();
}
if (id == 17) {
close();
}
// Open fullscreen GUI
if (id == 61) {
isFullscreen = true;
Minecraft.getMinecraft().displayGuiScreen(
new GuiNpcMobSpawnerFullscreen(posX, posY, posZ));
}
// Spawn
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();
}
}
// Mob Spawner
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();
}
}
// Remove
if (id == 6) {
if (scroll.getSelected() != null) {
int prevSelected = scroll.selected;
if (showingClones == 2) {
PacketClient.sendClient(new CloneRemovePacket(activeTab, scroll.getSelected()));
return;
}
ClientCloneController.Instance.removeClone(scroll.getSelected(), activeTab);
scroll.selected = -1;
initGui();
if (scroll.list != null && !scroll.list.isEmpty()) {
scroll.selected = Math.min(prevSelected, scroll.list.size() - 1);
} else {
scroll.selected = -1;
}
}
}
// 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();
}
if (id > 20) {
activeTab = id - 20;
initGui();
sortType = (byte) ((GuiNpcButton) guibutton).getValue();
}
}
// ==================== DATA FROM SERVER ====================
@Override
public void save() {
}
@Override
public void setGuiData(NBTTagCompound compound) {
if (compound.hasKey("CloneTags")) {
if (compound.hasKey("CloneFolders")) {
// Minimized doesn't use folders, but still handle the packet
initGui();
} else if (compound.hasKey("CloneTags")) {
this.tagMap = new TagMap(activeTab);
NBTTagCompound cloneTags = compound.getCompoundTag("CloneTags");
this.tagMap.readNBT(cloneTags);
@@ -521,29 +629,8 @@ public class GuiNpcMobSpawner extends GuiNPCInterface implements IGuiData {
}
this.list = list;
this.rawList = new ArrayList<String>(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);
}
}
}
@@ -2,6 +2,7 @@ package noppes.npcs.client.gui;
import kamkeel.npcs.network.PacketClient;
import kamkeel.npcs.network.packets.request.clone.CloneAllTagsShortPacket;
import kamkeel.npcs.network.packets.request.clone.CloneFolderListPacket;
import kamkeel.npcs.network.packets.request.clone.ClonePreSavePacket;
import kamkeel.npcs.network.packets.request.clone.CloneSavePacket;
import net.minecraft.client.Minecraft;
@@ -22,12 +23,14 @@ 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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCallback, IGuiData, ISubGuiListener {
@@ -35,9 +38,16 @@ 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;
// Client-side folder names
private ArrayList<String> clientFolderNames = new ArrayList<>();
// Server-side folder names (populated via CloneFolderListPacket)
private static ArrayList<String> serverFolderNames = new ArrayList<>();
// Selected Tags to Add
public static NBTTagList tagsCompound;
public static HashSet<String> addTags;
@@ -68,6 +78,37 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall
if (isNPC) {
PacketClient.sendClient(new CloneAllTagsShortPacket());
}
buildClientFolderNames();
// Request server folders if in server mode
if (serverSide) {
PacketClient.sendClient(new CloneFolderListPacket());
}
// Validate saved folder selection
validateFolder();
}
private void buildClientFolderNames() {
clientFolderNames.clear();
if (ClientCloneController.Instance != null) {
for (CloneFolder f : ClientCloneController.Instance.getFolderList()) {
clientFolderNames.add(f.name);
}
}
}
private List<String> getActiveFolderList() {
return serverSide ? serverFolderNames : clientFolderNames;
}
private void validateFolder() {
if (saveMode == 1 && folder != null) {
if (!getActiveFolderList().contains(folder)) {
folder = null;
}
}
}
@Override
@@ -77,61 +118,138 @@ 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));
// 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 button — opens SubGuiFolderSelect
String btnText = folder != null ? folder : "Select a Folder";
addButton(new GuiNpcButton(2, guiLeft + 56, guiTop + 45, 148, 20, btnText));
}
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));
addButton(new GuiNpcButton(0, guiLeft + 4, guiTop + 70, 80, 20, "gui.save"));
// Save / Cancel
GuiNpcButton saveBtn = new GuiNpcButton(0, guiLeft + 4, guiTop + 70, 80, 20, "gui.save");
if (saveMode == 1 && folder == null) {
saveBtn.enabled = false;
}
addButton(saveBtn);
addButton(new GuiNpcButton(1, guiLeft + 86, guiTop + 70, 80, 20, "gui.cancel"));
// Client / Server toggle
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() {
if (saveMode == 0) {
GuiNpcButton selector = getButton(2);
if (selector == null) return;
tab = selector.getValue() + 1;
folder = null;
}
// In folder mode, folder is already set via SubGuiFolderSelect
}
public void buttonEvent(GuiButton guibutton) {
int id = guibutton.id;
// Save
if (id == 0) {
updateDestinationFromSelector();
String name = getTextField(0).getText();
if (name.isEmpty())
return;
int tab = ((GuiNpcButton) guibutton).getValue() + 1;
if (saveMode == 1 && folder == null)
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));
}
}
}
// Cancel
if (id == 1) {
close();
}
// Destination selector
if (id == 2) {
tab = ((GuiNpcButton) guibutton).getValue() + 1;
if (saveMode == 0) {
// Tab mode — update from BiDirectional
updateDestinationFromSelector();
} else {
// Folder mode — open folder selector Sub GUI
List<String> folders = getActiveFolderList();
setSubGui(new SubGuiFolderSelect(folders, folder));
}
}
// Client / Server toggle
if (id == 3) {
boolean wasServerSide = serverSide;
serverSide = ((GuiNpcButton) guibutton).getValue() == 1;
if (serverSide && !wasServerSide) {
// Switching to server mode — request fresh folder list
PacketClient.sendClient(new CloneFolderListPacket());
}
// Validate folder exists on the new side
validateFolder();
initGui();
}
// Wand Tags
if (id == 4) {
if (isNPC) {
this.setSubGui(new SubGuiClonerQuickTags(this));
}
}
// NPC Tags
if (id == 5) {
if (isNPC) {
this.setSubGui(new SubGuiClonerNPCTags((EntityNPCInterface) toClone, this));
}
}
}
// Tab/Folder mode toggle
if (id == 6) {
saveMode = ((GuiNpcButton) guibutton).getValue();
initGui();
}
}
@Override
public void confirmClicked(boolean confirm, int id) {
@@ -145,13 +263,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();
@@ -159,7 +285,6 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall
displayGuiScreen(this);
}
@Override
public void save() {
}
@@ -171,6 +296,17 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall
displayGuiScreen(new GuiYesNo(this, "Warning", "You are about to overwrite a clone", 1));
else
confirmClicked(true, 0);
} else if (compound.hasKey("CloneFolders")) {
// Server folder list arrived
NBTTagList folderList = compound.getTagList("CloneFolders", 10);
serverFolderNames.clear();
for (int i = 0; i < folderList.tagCount(); i++) {
CloneFolder f = new CloneFolder();
f.readNBT(folderList.getCompoundTagAt(i));
serverFolderNames.add(f.name);
}
validateFolder();
initGui();
} else if (compound.hasKey("ShortTags")) {
NBTTagList validTags = compound.getTagList("ShortTags", 10);
tagMap.clear();
@@ -193,6 +329,12 @@ public class GuiNpcMobSpawnerAdd extends GuiNPCInterface implements GuiYesNoCall
@Override
public void subGuiClosed(SubGuiInterface subgui) {
if (subgui instanceof SubGuiFolderSelect) {
SubGuiFolderSelect sel = (SubGuiFolderSelect) subgui;
if (sel.selectedFolder != null) {
folder = sel.selectedFolder;
}
}
initGui();
}
File diff suppressed because it is too large. Load diff
@@ -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() {
}
}
@@ -0,0 +1,74 @@
package noppes.npcs.client.gui;
import net.minecraft.client.gui.GuiButton;
import noppes.npcs.client.gui.util.GuiCustomScroll;
import noppes.npcs.client.gui.util.GuiNpcButton;
import noppes.npcs.client.gui.util.ICustomScrollListener;
import noppes.npcs.client.gui.util.SubGuiInterface;
import java.util.ArrayList;
import java.util.List;
public class SubGuiFolderSelect extends SubGuiInterface implements ICustomScrollListener {
public String selectedFolder;
private final List<String> folderList;
private GuiCustomScroll folderScroll;
public SubGuiFolderSelect(List<String> folders, String currentSelection) {
this.folderList = new ArrayList<>(folders);
this.selectedFolder = currentSelection;
setBackground("menubg.png");
xSize = 200;
ySize = 220;
closeOnEsc = true;
}
@Override
public void initGui() {
super.initGui();
folderScroll = new GuiCustomScroll(this, 0);
folderScroll.setSize(xSize - 8, ySize - 50);
folderScroll.guiLeft = guiLeft + 4;
folderScroll.guiTop = guiTop + 4;
folderScroll.setList(new ArrayList<>(folderList));
if (selectedFolder != null) {
folderScroll.setSelected(selectedFolder);
}
addScroll(folderScroll);
int btnW = (xSize - 12) / 2;
addButton(new GuiNpcButton(0, guiLeft + 4, guiTop + ySize - 44, btnW, 20, "gui.done"));
addButton(new GuiNpcButton(1, guiLeft + 4 + btnW + 4, guiTop + ySize - 44, btnW, 20, "gui.cancel"));
getButton(0).enabled = folderScroll.hasSelected();
}
@Override
public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) {
getButton(0).enabled = folderScroll.hasSelected();
}
@Override
public void customScrollDoubleClicked(String selection, GuiCustomScroll guiCustomScroll) {
selectedFolder = selection;
close();
}
@Override
protected void actionPerformed(GuiButton guibutton) {
if (guibutton.id == 0) {
selectedFolder = folderScroll.getSelected();
close();
}
if (guibutton.id == 1) {
selectedFolder = null;
close();
}
}
@Override
public void save() {
}
}
@@ -0,0 +1,57 @@
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.SubGuiInterface;
public class SubGuiMoveConfirm extends SubGuiInterface {
public boolean confirmed = false;
private final int count;
public final int destTab;
public final String destFolder;
public SubGuiMoveConfirm(int count, int destTab, String destFolder) {
this.count = count;
this.destTab = destTab;
this.destFolder = destFolder;
xSize = 220;
ySize = 90;
closeOnEsc = true;
}
@Override
public void initGui() {
super.initGui();
String line1 = "Move " + count + " clone" + (count != 1 ? "s" : "") + " to:";
addLabel(new GuiNpcLabel(0, line1, guiLeft + 10, guiTop + 10, 0xFFFFFF));
String dest;
if (destFolder != null) {
dest = "Folder: \"" + destFolder + "\"";
} else {
dest = "Tab: " + destTab;
}
addLabel(new GuiNpcLabel(1, " " + dest, guiLeft + 10, guiTop + 25, 0xFFFFFF));
addButton(new GuiNpcButton(0, guiLeft + 10, guiTop + 55, 95, 20, "gui.yes"));
addButton(new GuiNpcButton(1, guiLeft + 115, guiTop + 55, 95, 20, "gui.cancel"));
}
@Override
protected void actionPerformed(GuiButton guibutton) {
if (guibutton.id == 0) {
confirmed = true;
close();
}
if (guibutton.id == 1) {
close();
}
}
@Override
public void save() {
}
}
@@ -367,6 +367,13 @@ public class GuiCustomScroll extends GuiScreen {
return this;
}
public void setSelectable(boolean selectable) {
this.selectable = selectable;
if (!selectable) {
hover = -1;
}
}
public boolean isMouseOver(int x, int y) {
return x >= this.guiLeft && x <= this.guiLeft + this.xSize && y >= this.guiTop && y <= this.guiTop + this.ySize;
}
@@ -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;
}
@@ -38,24 +39,25 @@ public class GuiCustomScrollCloner extends GuiCustomScroll {
text += "...";
} else
text = displayString;
if ((multipleSelection && selectedList.contains(text)) || (!multipleSelection && selected == i)) {
int itemColor = this.colors.getOrDefault(list.get(i), 0xffffff);
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, j, k, this.colors.getOrDefault(text, 0xffffff));
fontRendererObj.drawString(text, j, k, itemColor);
} else if (i == hover) {
fontRendererObj.drawString(text, j, k, 0x00ff00);
} else {
fontRendererObj.drawString(text, j, k, this.colors.getOrDefault(text, 0xffffff));
fontRendererObj.drawString(text, j, k, itemColor);
}
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);
@@ -0,0 +1,108 @@
package noppes.npcs.client.gui.util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import org.lwjgl.opengl.GL11;
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 ResourceLocation TAB_TEXTURE = new ResourceLocation("customnpcs", "textures/gui/cloner/tab.png");
private static final ResourceLocation FOLDER_TEXTURE = new ResourceLocation("customnpcs", "textures/gui/cloner/folder.png");
private static final int ICON_RENDER_SIZE = 10;
private static final int ICON_PADDING = 14;
private List<Integer> iconTypes = new ArrayList<>();
public GuiCustomScrollIcons(GuiScreen parent, int id) {
super(parent, id);
}
public void setListWithIcons(List<String> names, List<Integer> icons) {
this.setUnsortedList(names);
this.iconTypes = new ArrayList<>(icons);
}
@Override
protected void drawItems() {
Minecraft mc = Minecraft.getMinecraft();
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_PADDING;
}
// Draw icon texture
if (iconType == ICON_FOLDER || iconType == ICON_TAB) {
ResourceLocation tex = (iconType == ICON_FOLDER) ? FOLDER_TEXTURE : TAB_TEXTURE;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
mc.getTextureManager().bindTexture(tex);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
drawFullTexture(j, k - 1, ICON_RENDER_SIZE, ICON_RENDER_SIZE);
GL11.glDisable(GL11.GL_BLEND);
}
// Draw text
String displayString = StatCollector.translateToLocal(list.get(i));
String text = "";
float maxWidth = (xSize + xOffset - 8 - (iconType != ICON_NONE ? ICON_PADDING : 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;
}
int itemColor = this.colors.getOrDefault(list.get(i), 0xffffff);
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, itemColor);
} else if (i == hover) {
fontRendererObj.drawString(text, textX, k, 0x00ff00);
} else {
fontRendererObj.drawString(text, textX, k, itemColor);
}
}
}
}
/**
* Draw a full texture (UV 0,0 to 1,1) scaled to the given width/height.
* Works with any texture size (not just 256x256).
*/
private void drawFullTexture(int x, int y, int w, int h) {
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(x, y + h, 0, 0, 1);
tessellator.addVertexWithUV(x + w, y + h, 0, 1, 1);
tessellator.addVertexWithUV(x + w, y, 0, 1, 0);
tessellator.addVertexWithUV(x, y, 0, 0, 0);
tessellator.draw();
}
}
@@ -0,0 +1,206 @@
package noppes.npcs.client.gui.util;
/**
* Abstract base class for fullscreen directory-browsing GUIs.
* Provides a configurable three-panel layout (left nav, center content, right actions)
* with percentage-based sizing, panel backgrounds/borders, and an optional resizable divider.
*
* Subclasses override abstract hooks to populate each panel region.
* Designed for reuse across Cloner, Quest Manager, Dialog Manager, etc.
*/
public abstract class GuiDirectory extends GuiNPCInterface implements ICustomScrollListener, ISubGuiListener {
// ===== LAYOUT CONFIG (subclasses may override in constructor) =====
protected float leftPanelPercent = 0.20f;
protected float rightPanelPercent = 0.10f;
protected int minLeftPanelW = 80;
protected int minRightPanelW = 40;
protected int minCenterW = 100;
// ===== LAYOUT CONSTANTS =====
protected int pad = 10;
protected int topBarH = 24;
protected int gap = 4;
protected int btnH = 20;
// ===== PANEL COLORS =====
protected int panelBg = 0xC0101010;
protected int panelBorder = 0xFF333333;
protected int topBarBg = 0xC0181818;
// ===== COMPUTED LAYOUT (populated by computeLayout) =====
protected int originX, originY, usableW, usableH;
protected int leftPanelW, rightPanelW;
protected int contentX, contentY, contentW, contentH;
protected int rightX;
// ===== OPTIONAL RESIZABLE DIVIDER (between left and center) =====
protected boolean enableDivider = false;
protected int dividerWidth = 5;
protected int dividerLineHeight = 20;
protected int minDividerPanelW = 50;
private int dividerOffset;
private boolean isDragging = false;
private int dragStartX;
// ===== CONSTRUCTOR =====
public GuiDirectory() {
super();
closeOnEsc = true;
drawDefaultBackground = true;
}
// ===== LAYOUT COMPUTATION =====
protected void computeLayout() {
originX = pad;
originY = pad;
usableW = width - 2 * pad;
usableH = height - 2 * pad;
leftPanelW = Math.max(minLeftPanelW, (int)(usableW * leftPanelPercent));
rightPanelW = Math.max(minRightPanelW, (int)(usableW * rightPanelPercent));
if (rightPanelPercent <= 0) {
rightPanelW = 0;
}
int gapCount = rightPanelW > 0 ? 3 : 2;
contentW = usableW - leftPanelW - rightPanelW - gapCount * gap;
if (contentW < minCenterW) {
contentW = minCenterW;
}
contentX = originX + leftPanelW + gap;
contentY = originY + topBarH + gap;
contentH = usableH - topBarH - gap;
rightX = contentX + contentW + gap;
if (enableDivider && dividerOffset == 0) {
dividerOffset = leftPanelW;
}
}
// ===== INIT GUI =====
@Override
public void initGui() {
super.initGui();
computeLayout();
initTopBar(originY + 2);
initLeftPanel();
initCenterPanel();
if (rightPanelW > 0) {
initRightPanel(contentY + 4);
}
}
// ===== DRAWING =====
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
drawTopBar();
drawPanels();
if (enableDivider) {
drawDivider(mouseX, mouseY);
}
super.drawScreen(mouseX, mouseY, partialTicks);
drawOverlay(mouseX, mouseY, partialTicks);
}
protected void drawTopBar() {
GuiUtil.drawRectD(originX, originY, originX + usableW, originY + topBarH, topBarBg);
}
protected void drawPanels() {
// Left nav panel border
GuiUtil.drawRectD(originX - 1, contentY - 1, originX + leftPanelW + 1, originY + usableH + 1, panelBorder);
// Right action panel border (if present)
if (rightPanelW > 0) {
GuiUtil.drawRectD(rightX - 1, contentY - 1, rightX + rightPanelW + 1, originY + usableH + 1, panelBorder);
}
}
protected void drawDivider(int mouseX, int mouseY) {
if (!enableDivider) return;
int divX = originX + dividerOffset;
int regionTop = contentY;
int regionHeight = contentH;
int handleTop = regionTop + (regionHeight - dividerLineHeight) / 2;
drawRect(divX + 1, handleTop, divX + dividerWidth - 1, handleTop + dividerLineHeight, 0xFF707070);
}
/** Override for extra drawing after super.drawScreen (e.g. status text) */
protected void drawOverlay(int mouseX, int mouseY, float partialTicks) {
}
// ===== DIVIDER MOUSE HANDLING =====
@Override
public void mouseClicked(int mouseX, int mouseY, int mouseButton) {
if (enableDivider && !hasSubGui()) {
int divX = originX + dividerOffset;
int regionTop = contentY;
int handleTop = regionTop + (contentH - dividerLineHeight) / 2;
int handleBottom = handleTop + dividerLineHeight;
if (mouseX >= divX && mouseX <= divX + dividerWidth &&
mouseY >= handleTop && mouseY <= handleBottom) {
isDragging = true;
resizingActive = true;
dragStartX = mouseX;
return;
}
}
super.mouseClicked(mouseX, mouseY, mouseButton);
}
@Override
public void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
if (isDragging) {
int dx = mouseX - dragStartX;
dragStartX = mouseX;
dividerOffset += dx;
int maxOffset = (usableW - (rightPanelW > 0 ? rightPanelW + gap : 0)) - dividerWidth - minDividerPanelW;
dividerOffset = Math.max(minDividerPanelW, Math.min(dividerOffset, maxOffset));
onDividerMoved(dividerOffset);
return;
}
super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick);
}
@Override
protected void mouseMovedOrUp(int mouseX, int mouseY, int state) {
if (isDragging) {
isDragging = false;
resizingActive = false;
return;
}
super.mouseMovedOrUp(mouseX, mouseY, state);
}
/** Called when divider is dragged. Override to update scroll sizes. */
protected void onDividerMoved(int newOffset) {
}
// Accessors for divider offset
protected int getDividerOffset() {
return dividerOffset;
}
protected void setDividerOffset(int offset) {
this.dividerOffset = offset;
}
// ===== ABSTRACT HOOKS (subclass must implement) =====
/** Populate top bar buttons. topBtnY is the y-coordinate for the button row. */
protected abstract void initTopBar(int topBtnY);
/** Populate the left navigation panel (scrolls, search fields, buttons). */
protected abstract void initLeftPanel();
/** Populate the center content panel (main scroll list). */
protected abstract void initCenterPanel();
/** Populate the right action panel. startY is the first button y-coordinate. */
protected abstract void initRightPanel(int startY);
}
@@ -15,6 +15,7 @@ public class GuiMenuSideButton extends GuiNpcButton {
public boolean active;
public boolean rightSided;
public int textColor = -1;
public ItemStack renderStack;
public ResourceLocation renderResource;
public int renderIconPosX = 0;
@@ -96,13 +97,15 @@ public class GuiMenuSideButton extends GuiNpcButton {
text += "...";
} else
text = displayString;
if (active) {
drawCenteredString(fontrenderer, text, xPosition + width / 2, yPosition + (height - 8) / 2, 0xffffa0);
} else if (field_146123_n) {
drawCenteredString(fontrenderer, text, xPosition + width / 2, yPosition + (height - 8) / 2, 0xffffa0);
int color;
if (textColor != -1) {
color = textColor;
} else if (active || field_146123_n) {
color = 0xffffa0;
} else {
drawCenteredString(fontrenderer, text, xPosition + width / 2, yPosition + (height - 8) / 2, 0xe0e0e0);
color = 0xe0e0e0;
}
drawCenteredString(fontrenderer, text, xPosition + width / 2, yPosition + (height - 8) / 2, color);
}
@Override
@@ -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<UUID, Tag> getTags();
TagMap getTagMap();
}
@@ -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<String, CloneFolder> 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,250 @@ public class ServerCloneController implements ICloneHandler {
return name;
}
// ==================== Folder Registry ====================
public void loadFolders() {
folders.clear();
File dir = getDir();
File[] files = dir.listFiles();
if (files != null) {
List<File> folderDirs = new ArrayList<>();
for (File f : files) {
if (!f.isDirectory()) continue;
String name = f.getName();
if (name.startsWith("___")) continue;
try {
Integer.parseInt(name);
continue;
} catch (NumberFormatException ignored) {
}
folderDirs.add(f);
}
folderDirs.sort(Comparator.comparing(File::getName));
for (File f : folderDirs) {
CloneFolder folder = new CloneFolder(f.getName());
folder.createdDate = f.lastModified();
folders.put(f.getName(), folder);
}
}
}
public List<CloneFolder> getFolderList() {
return new ArrayList<>(folders.values());
}
public List<String> 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);
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(), newName);
if (!oldDir.renameTo(newDir)) {
return false;
}
folders.remove(oldName);
folder.name = newName;
folders.put(newName, folder);
return true;
}
public boolean deleteFolder(String name) {
if (!folders.containsKey(name)) {
return false;
}
List<String> 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);
return true;
}
// ==================== Folder-Based Clone Operations ====================
public File getFolderDir(String folderName) {
return new File(getDir(), folderName);
}
public List<String> getClones(String folderName) {
List<String> 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<String> getClonesDate(String folderName) {
List<String> 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<File>() {
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);
if (!dir.exists()) dir.mkdir();
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<UUID> 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<UUID> 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<UUID> 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<UUID> 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<UUID> tagUUIDs = new HashSet<UUID>();
if (nbttagcompound.hasKey("TagUUIDs")) {
@@ -243,6 +494,25 @@ public class ServerCloneController implements ICloneHandler {
return true;
}
public boolean addToTagMap(NBTTagCompound nbttagcompound, String name, String folderName) {
HashSet<UUID> 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 +522,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<UUID> getTagUUIDsFromTab(String cloneName, int tab) {
TagMap tagMap = ServerTagMapController.Instance.getTagMap(tab);
HashSet<UUID> uuids = tagMap.getUUIDs(cloneName);
return uuids != null ? new HashSet<>(uuids) : new HashSet<>();
}
protected HashSet<UUID> getTagUUIDsFromFolder(String cloneName, String folderName) {
TagMap tagMap = ServerTagMapController.Instance.getTagMap(folderName);
HashSet<UUID> uuids = tagMap.getUUIDs(cloneName);
return uuids != null ? new HashSet<>(uuids) : new HashSet<>();
}
protected void setTagUUIDsForTab(String cloneName, int tab, HashSet<UUID> 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<UUID> 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 +593,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 +634,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 +650,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 +661,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 +673,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<IEntity> 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);
}
}
@@ -35,6 +35,13 @@ public class ServerTagMapController {
return dir;
}
public File getCloneFolderDir(String folderName) {
File dir = new File(getDir(), 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");
@@ -0,0 +1,61 @@
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 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;
}
if (name.equals(".") || name.equals("..")) {
return false;
}
if (name.startsWith("___")) {
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;
}
}
@@ -13,10 +13,18 @@ import java.util.UUID;
public class TagMap {
public int cloneTab;
public String cloneFolder;
public HashMap<String, HashSet<UUID>> tagMap;
public TagMap(int tab) {
this.cloneTab = tab;
this.cloneFolder = null;
this.tagMap = new HashMap<String, HashSet<UUID>>();
}
public TagMap(String folder) {
this.cloneTab = -1;
this.cloneFolder = folder;
this.tagMap = new HashMap<String, HashSet<UUID>>();
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B