From cd123979279b87762c39b5279d55d770552f5534 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 19 Feb 2025 05:43:28 -0500 Subject: [PATCH] Profile GUI and Effect Bar --- .../kamkeel/npcs/command/CommandKamkeel.java | 2 +- .../npcs/controllers/ProfileController.java | 14 +- .../npcs/controllers/data/CNPCData.java | 12 +- .../npcs/controllers/data/IProfileData.java | 4 +- .../npcs/controllers/data/InfoEntry.java | 50 +++ .../kamkeel/npcs/network/PacketHandler.java | 2 + .../npcs/network/enums/EnumRequestPacket.java | 2 +- .../request/profile/ProfileGetInfoPacket.java | 97 +++++ .../player/inventory/GuiCNPCInventory.java | 66 +++- .../gui/player/inventory/GuiProfiles.java | 365 ++++++++++++------ .../npcs/client/gui/util/GuiEffectBar.java | 195 ++++++++++ .../npcs/controllers/data/CustomEffect.java | 1 - .../client/tabs/InventoryTabCustomNpc.java | 2 +- .../assets/customnpcs/lang/en_US.lang | 4 + 14 files changed, 671 insertions(+), 145 deletions(-) create mode 100644 src/main/java/kamkeel/npcs/controllers/data/InfoEntry.java create mode 100644 src/main/java/kamkeel/npcs/network/packets/request/profile/ProfileGetInfoPacket.java create mode 100644 src/main/java/noppes/npcs/client/gui/util/GuiEffectBar.java diff --git a/src/main/java/kamkeel/npcs/command/CommandKamkeel.java b/src/main/java/kamkeel/npcs/command/CommandKamkeel.java index 85adfce4c..59920807e 100644 --- a/src/main/java/kamkeel/npcs/command/CommandKamkeel.java +++ b/src/main/java/kamkeel/npcs/command/CommandKamkeel.java @@ -47,7 +47,7 @@ public class CommandKamkeel extends CommandBase{ @Override public String getCommandName() { - return "kamkeel/npcs"; + return "kamkeel"; } @Override diff --git a/src/main/java/kamkeel/npcs/controllers/ProfileController.java b/src/main/java/kamkeel/npcs/controllers/ProfileController.java index cdad4795d..71c2d9f5e 100644 --- a/src/main/java/kamkeel/npcs/controllers/ProfileController.java +++ b/src/main/java/kamkeel/npcs/controllers/ProfileController.java @@ -1,9 +1,6 @@ package kamkeel.npcs.controllers; -import kamkeel.npcs.controllers.data.IProfileData; -import kamkeel.npcs.controllers.data.Profile; -import kamkeel.npcs.controllers.data.ProfileOperation; -import kamkeel.npcs.controllers.data.Slot; +import kamkeel.npcs.controllers.data.*; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.CompressedStreamTools; @@ -552,6 +549,7 @@ public class ProfileController { dataCompound.setTag(profileData.getTagName(), cloned); } slot.setCompound(dataCompound); + slot.setLastLoaded(System.currentTimeMillis()); } public static void loadSlotData(EntityPlayer player) { @@ -576,8 +574,8 @@ public class ProfileController { } } - public static List getProfileInfo(EntityPlayer player, int slotId) { - List infoList = new ArrayList<>(); + public static List getProfileInfo(EntityPlayer player, int slotId) { + List infoList = new ArrayList<>(); Profile profile = getProfile(player); if (profile == null) return infoList; @@ -589,7 +587,7 @@ public class ProfileController { // Use current data (not stored slot data) for active slot. for (IProfileData pd : dataList) { NBTTagCompound currentNBT = pd.getCurrentNBT(player); - List subInfo = pd.getInfo(player, currentNBT); + List subInfo = pd.getInfo(player, currentNBT); infoList.addAll(subInfo); } } else { @@ -600,7 +598,7 @@ public class ProfileController { for (IProfileData pd : dataList) { if (slotCompound.hasKey(pd.getTagName())) { NBTTagCompound sub = slotCompound.getCompoundTag(pd.getTagName()); - List subInfo = pd.getInfo(player, sub); + List subInfo = pd.getInfo(player, sub); infoList.addAll(subInfo); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/CNPCData.java b/src/main/java/kamkeel/npcs/controllers/data/CNPCData.java index 60bf83cee..6a60ade72 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/CNPCData.java +++ b/src/main/java/kamkeel/npcs/controllers/data/CNPCData.java @@ -53,17 +53,15 @@ public class CNPCData implements IProfileData { } @Override - public List getInfo(EntityPlayer player, NBTTagCompound compound) { + public List getInfo(EntityPlayer player, NBTTagCompound compound) { PlayerData playerData = new PlayerData(); playerData.player = player; playerData.setNBT(compound); - List info = new ArrayList<>(); - - info.add("QuestsFinished" + "###" + playerData.questData.finishedQuests.size()); - info.add("QuestsActive" + "###" + playerData.questData.activeQuests.size()); - info.add("DialogsRead" + "###" + playerData.dialogData.dialogsRead.size()); - + List info = new ArrayList<>(); + info.add(new InfoEntry("profile.info.quest.finished", 0x60fa57, playerData.questData.finishedQuests.size(), 0xFFFFFF)); + info.add(new InfoEntry("profile.info.quest.active", 0xf75336, playerData.questData.activeQuests.size(), 0xFFFFFF)); + info.add(new InfoEntry("profile.info.dialog.read", 0x47acf5, playerData.dialogData.dialogsRead.size(), 0xFFFFFF)); return info; } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/IProfileData.java b/src/main/java/kamkeel/npcs/controllers/data/IProfileData.java index 00dc63929..2b5e308b5 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/IProfileData.java +++ b/src/main/java/kamkeel/npcs/controllers/data/IProfileData.java @@ -45,7 +45,7 @@ public interface IProfileData { * based on the provided NBTTagCompound from the slot. * @param player The player in question. * @param compound The NBT data from the slot under this profile data's tag. - * @return A list of strings with information (could be empty). + * @return A list of InfoEntry with information */ - public List getInfo(EntityPlayer player, NBTTagCompound compound); + public List getInfo(EntityPlayer player, NBTTagCompound compound); } diff --git a/src/main/java/kamkeel/npcs/controllers/data/InfoEntry.java b/src/main/java/kamkeel/npcs/controllers/data/InfoEntry.java new file mode 100644 index 000000000..7a8f29991 --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/data/InfoEntry.java @@ -0,0 +1,50 @@ +package kamkeel.npcs.controllers.data; + +import net.minecraft.nbt.NBTTagCompound; + +public class InfoEntry { + private String label; + private String result; + private int labelColor; + private int resultColor; + + public InfoEntry() {} + + public InfoEntry(String label, int labelColor, String result, int resultColor) { + this.label = label; + this.result = result; + this.labelColor = labelColor; + this.resultColor = resultColor; + } + + public InfoEntry(String label, int labelColor, int result, int resultColor) { + this(label, labelColor, String.valueOf(result), resultColor); + } + + public InfoEntry(String label, String result) { + this(label, 0xFFFFFF, result, 0xFFFFFF); + } + + public NBTTagCompound writeToNBT() { + NBTTagCompound tag = new NBTTagCompound(); + tag.setString("Label", label); + tag.setInteger("LabelColor", labelColor); + tag.setString("Result", result); + tag.setInteger("ResultColor", resultColor); + return tag; + } + + public static InfoEntry readFromNBT(NBTTagCompound tag) { + return new InfoEntry( + tag.getString("Label"), + tag.getInteger("LabelColor"), + tag.getString("Result"), + tag.getInteger("ResultColor") + ); + } + + public String getLabel() { return label; } + public String getResult() { return result; } + public int getLabelColor() { return labelColor; } + public int getResultColor() { return resultColor; } +} diff --git a/src/main/java/kamkeel/npcs/network/PacketHandler.java b/src/main/java/kamkeel/npcs/network/PacketHandler.java index c9c0dd5c2..de5799d2b 100644 --- a/src/main/java/kamkeel/npcs/network/PacketHandler.java +++ b/src/main/java/kamkeel/npcs/network/PacketHandler.java @@ -56,6 +56,7 @@ import kamkeel.npcs.network.packets.request.pather.MovingPathSavePacket; import kamkeel.npcs.network.packets.request.playerdata.PlayerDataGetPacket; import kamkeel.npcs.network.packets.request.playerdata.PlayerDataMapRegenPacket; import kamkeel.npcs.network.packets.request.playerdata.PlayerDataRemovePacket; +import kamkeel.npcs.network.packets.request.profile.ProfileGetInfoPacket; import kamkeel.npcs.network.packets.request.profile.ProfileGetPacket; import kamkeel.npcs.network.packets.request.quest.*; import kamkeel.npcs.network.packets.request.recipe.RecipeGetPacket; @@ -326,6 +327,7 @@ public class PacketHandler { // Profile Packets REQUEST_PACKET.registerPacket(new ProfileGetPacket()); + REQUEST_PACKET.registerPacket(new ProfileGetInfoPacket()); // Other Packets REQUEST_PACKET.registerPacket(new IsGuiOpenInform()); diff --git a/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java b/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java index 92d90a328..49e0b57b5 100644 --- a/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java +++ b/src/main/java/kamkeel/npcs/network/enums/EnumRequestPacket.java @@ -185,5 +185,5 @@ public enum EnumRequestPacket { EffectSave, ColorSet, - ProfileGet, ProfileGetAll, ColorBrush + ProfileGet, ProfileGetAll, ProfileGetInfo, ColorBrush } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/profile/ProfileGetInfoPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/profile/ProfileGetInfoPacket.java new file mode 100644 index 000000000..5d853db50 --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/request/profile/ProfileGetInfoPacket.java @@ -0,0 +1,97 @@ +package kamkeel.npcs.network.packets.request.profile; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +import kamkeel.npcs.controllers.ProfileController; +import kamkeel.npcs.controllers.data.InfoEntry; +import kamkeel.npcs.controllers.data.Profile; +import kamkeel.npcs.controllers.data.Slot; +import kamkeel.npcs.network.AbstractPacket; +import kamkeel.npcs.network.PacketChannel; +import kamkeel.npcs.network.PacketHandler; +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 net.minecraftforge.common.util.Constants; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class ProfileGetInfoPacket extends AbstractPacket { + public static String packetName = "Request|ProfileGetInfo"; + + public ProfileGetInfoPacket() {} + + @Override + public Enum getType() { + return EnumRequestPacket.ProfileGetInfo; + } + + @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; + + NBTTagCompound compound = new NBTTagCompound(); + compound.setBoolean("PROFILE_INFO", true); + NBTTagList slotList = new NBTTagList(); + Profile profile = ProfileController.getProfile(player); + if(profile != null){ + for(Slot slot : profile.slots.values()){ + NBTTagCompound slotCompound = new NBTTagCompound(); + NBTTagList infoList = new NBTTagList(); + slotCompound.setInteger("ID", slot.getId()); + List profileInfo = ProfileController.getProfileInfo(player, slot.getId()); + for(InfoEntry infoEntry : profileInfo){ + infoList.appendTag(infoEntry.writeToNBT()); + } + slotCompound.setTag("INFO", infoList); + slotList.appendTag(slotCompound); + } + compound.setTag("SLOTS", slotList); + } + GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); + } + + public static HashMap> readSlotsFromNBT(NBTTagCompound compound) { + HashMap> slotInfoMap = new HashMap<>(); + + if (!compound.getBoolean("PROFILE_INFO")) return slotInfoMap; + + NBTTagList slotList = compound.getTagList("SLOTS", Constants.NBT.TAG_COMPOUND); + + for (int i = 0; i < slotList.tagCount(); i++) { + NBTTagCompound slotCompound = slotList.getCompoundTagAt(i); + + int slotId = slotCompound.getInteger("ID"); + NBTTagList infoList = slotCompound.getTagList("INFO", Constants.NBT.TAG_COMPOUND); + + List infoEntries = new ArrayList<>(); + for (int j = 0; j < infoList.tagCount(); j++) { + NBTTagCompound infoTag = infoList.getCompoundTagAt(j); + infoEntries.add(InfoEntry.readFromNBT(infoTag)); // Convert back to InfoEntry + } + + slotInfoMap.put(slotId, infoEntries); + } + + return slotInfoMap; + } + +} diff --git a/src/main/java/noppes/npcs/client/gui/player/inventory/GuiCNPCInventory.java b/src/main/java/noppes/npcs/client/gui/player/inventory/GuiCNPCInventory.java index 68395a52f..ee607398b 100644 --- a/src/main/java/noppes/npcs/client/gui/player/inventory/GuiCNPCInventory.java +++ b/src/main/java/noppes/npcs/client/gui/player/inventory/GuiCNPCInventory.java @@ -4,10 +4,15 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; import net.minecraft.util.ResourceLocation; import noppes.npcs.client.ClientCacheHandler; +import noppes.npcs.client.gui.util.GuiEffectBar; import noppes.npcs.client.gui.util.GuiMenuSideButton; import noppes.npcs.client.gui.util.GuiNPCInterface; import noppes.npcs.config.ConfigClient; -import noppes.npcs.config.ConfigMain; +import noppes.npcs.controllers.CustomEffectController; +import noppes.npcs.controllers.data.CustomEffect; +import noppes.npcs.controllers.data.PlayerData; +import noppes.npcs.controllers.data.PlayerEffect; +import org.lwjgl.input.Mouse; import tconstruct.client.tabs.InventoryTabCustomNpc; import tconstruct.client.tabs.TabRegistry; @@ -16,6 +21,7 @@ public class GuiCNPCInventory extends GuiNPCInterface { public static int activeTab = -100; protected Minecraft mc = Minecraft.getMinecraft(); + private GuiEffectBar effectBar; public GuiCNPCInventory() { super(); @@ -38,7 +44,7 @@ public class GuiCNPCInventory extends GuiNPCInterface { questsButton.active = activeTab == -100; questsButton.renderIconPosX = 32; questsButton.renderResource = specialIcons; - addButton(questsButton); + addSideButton(questsButton); if(ClientCacheHandler.allowParties){ y += 21; @@ -46,7 +52,7 @@ public class GuiCNPCInventory extends GuiNPCInterface { partyButton.rightSided = true; partyButton.active = activeTab == -101; partyButton.renderResource = specialIcons; - addButton(partyButton); + addSideButton(partyButton); } if(ConfigClient.enableFactionTab){ y += 21; @@ -55,16 +61,16 @@ public class GuiCNPCInventory extends GuiNPCInterface { factionButton.active = activeTab == -102; factionButton.renderIconPosX = 48; factionButton.renderResource = specialIcons; - addButton(factionButton); + addSideButton(factionButton); } if(ClientCacheHandler.allowProfiles){ y += 21; GuiMenuSideButton profileButton = new GuiMenuSideButton(-104, guiLeft + xSize + 37, this.guiTop + y, 22, 22, ""); profileButton.rightSided = true; profileButton.active = activeTab == -104; - profileButton.renderIconPosX = 80; + profileButton.renderIconPosX = 64; profileButton.renderResource = specialIcons; - addButton(profileButton); + addSideButton(profileButton); } y += 21; @@ -73,9 +79,55 @@ public class GuiCNPCInventory extends GuiNPCInterface { clientButton.active = activeTab == -103; clientButton.renderIconPosX = 16; clientButton.renderResource = specialIcons; - addButton(clientButton); + addSideButton(clientButton); + + int effectBarX = guiLeft + xSize + 65; + int effectBarY = guiTop + 10; + int effectBarWidth = 24; + int effectBarHeight = ySize; + effectBar = new GuiEffectBar(effectBarX, effectBarY, effectBarWidth, effectBarHeight); } + private void updateEffectBar() { + if (effectBar != null) { + effectBar.entries.clear(); + PlayerData data = PlayerData.get(mc.thePlayer); + if (data != null && data.effectData != null) { + for (PlayerEffect pe : data.effectData.getEffects().values()) { + CustomEffect effect = CustomEffectController.getInstance().get(pe.id, pe.index); + if (effect != null) { + effectBar.entries.add(new GuiEffectBar.EffectEntry(effect, pe)); + } + } + } + } + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + updateEffectBar(); + super.drawScreen(mouseX, mouseY, partialTicks); + if (effectBar != null) { + effectBar.drawScreen(mouseX, mouseY, partialTicks); + } + } + + @Override + public void handleMouseInput() { + super.handleMouseInput(); + int delta = Mouse.getDWheel(); + if (delta != 0 && effectBar != null) { + int mouseX = Mouse.getX() * width / mc.displayWidth; + int mouseY = height - Mouse.getY() * height / mc.displayHeight - 1; + // Only scroll the effect bar if the mouse is over its area. + if (mouseX >= effectBar.x && mouseX < effectBar.x + effectBar.width && + mouseY >= effectBar.y && mouseY < effectBar.y + effectBar.height) { + effectBar.mouseScrolled(delta > 0 ? 1 : -1); + } + } + } + + @Override protected void actionPerformed(GuiButton guibutton){ if (guibutton.id <= -100) { diff --git a/src/main/java/noppes/npcs/client/gui/player/inventory/GuiProfiles.java b/src/main/java/noppes/npcs/client/gui/player/inventory/GuiProfiles.java index dfebad2b7..9c9f00140 100644 --- a/src/main/java/noppes/npcs/client/gui/player/inventory/GuiProfiles.java +++ b/src/main/java/noppes/npcs/client/gui/player/inventory/GuiProfiles.java @@ -1,36 +1,32 @@ package noppes.npcs.client.gui.player.inventory; +import kamkeel.npcs.controllers.data.InfoEntry; import kamkeel.npcs.controllers.data.Profile; import kamkeel.npcs.controllers.data.Slot; import kamkeel.npcs.network.PacketClient; -import kamkeel.npcs.network.packets.player.CheckPlayerValue; -import kamkeel.npcs.network.packets.request.linked.LinkedGetPacket; -import kamkeel.npcs.network.packets.request.linked.LinkedItemRemovePacket; import kamkeel.npcs.network.packets.request.linked.LinkedNPCAddPacket; -import kamkeel.npcs.network.packets.request.linked.LinkedNPCRemovePacket; +import kamkeel.npcs.network.packets.request.profile.ProfileGetInfoPacket; import kamkeel.npcs.network.packets.request.profile.ProfileGetPacket; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiYesNoCallback; +import net.minecraft.client.gui.ScaledResolution; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector; -import noppes.npcs.NoppesUtilPlayer; import noppes.npcs.client.CustomNpcResourceListener; import noppes.npcs.client.NoppesUtil; import noppes.npcs.client.gui.SubGuiEditText; import noppes.npcs.client.gui.util.*; -import noppes.npcs.constants.EnumPlayerPacket; -import noppes.npcs.controllers.data.Faction; -import noppes.npcs.controllers.data.LinkedItem; -import noppes.npcs.controllers.data.PlayerFactionData; +import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; import tconstruct.client.tabs.AbstractTab; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; -import java.util.Vector; public class GuiProfiles extends GuiCNPCInventory implements ISubGuiListener, ICustomScrollListener, IGuiData, GuiYesNoCallback { @@ -40,14 +36,23 @@ public class GuiProfiles extends GuiCNPCInventory implements ISubGuiListener, IC private String selected = null; private Profile profile; private Slot slot; + private HashMap> slotInfoMap = new HashMap<>(); + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy HH:mm"); - public GuiProfiles() { + private int scrollY = 0; // Vertical scrolling position + private int maxScrollY = 0; // Maximum scroll limit + + private boolean draggingScrollbar = false; // Track if user is dragging the scrollbar + private int lastMouseY = 0; // Last recorded mouse Y position when dragging + + public GuiProfiles() { super(); xSize = 280; ySize = 180; this.drawDefaultBackground = false; title = ""; PacketClient.sendClient(new ProfileGetPacket()); + PacketClient.sendClient(new ProfileGetInfoPacket()); } @Override @@ -55,17 +60,17 @@ public class GuiProfiles extends GuiCNPCInventory implements ISubGuiListener, IC { super.initGui(); - int y = guiTop + 8; - this.addButton(new GuiNpcButton(1,guiLeft + 368, y += 40, 45, 20, "gui.add")); - this.addButton(new GuiNpcButton(2,guiLeft + 368, y += 22, 45, 20, "gui.remove")); - this.addButton(new GuiNpcButton(3,guiLeft + 368, y += 22, 45, 20, "gui.rename")); - this.addButton(new GuiNpcButton(4,guiLeft + 368, y += 22, 45, 20, "gui.change")); + int y = guiTop + 4; + this.addButton(new GuiNpcButton(1,guiLeft + 4, y += 144, 60, 20, "gui.change")); + this.addButton(new GuiNpcButton(2,guiLeft + 4 + 63, y, 60, 20, "gui.rename")); + this.addButton(new GuiNpcButton(3,guiLeft + 4 + 63, y += 22, 60, 20, "gui.remove")); + this.addButton(new GuiNpcButton(4,guiLeft + 4, y, 60, 20, "gui.create")); if(scroll == null){ scroll = new GuiCustomScroll(this,0,0); - scroll.setSize(143, 185); + scroll.setSize(123, 141); } - scroll.guiLeft = guiLeft + 220; + scroll.guiLeft = guiLeft + 4; scroll.guiTop = guiTop + 4; scroll.setList(new ArrayList(this.data.keySet())); this.addScroll(scroll); @@ -82,102 +87,162 @@ public class GuiProfiles extends GuiCNPCInventory implements ISubGuiListener, IC } } - @Override - public void drawScreen(int i, int j, float f) - { - super.drawScreen(i, j, f); - if(hasSubGui()) - return; - } - @Override - public void drawBackground() { - super.drawBackground(); - renderScreen(); + public void drawScreen(int mouseX, int mouseY, float f) { + drawDefaultBackground(); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + mc.renderEngine.bindTexture(resource); + drawTexturedModalRect(guiLeft, guiTop, 0, 0, 252, 195); + drawTexturedModalRect(guiLeft + 252, guiTop, 188, 0, 67, 195); + + super.drawScreen(mouseX, mouseY, f); + if (hasSubGui()) return; + + renderScrollableScreen(mouseX, mouseY); } - private void renderScreen() { - // Draw the common background bars - drawGradientRect(guiLeft + 5, guiTop + 4, guiLeft + 218, guiTop + 24, 0xC0101010, 0xC0101010); - drawHorizontalLine(guiLeft + 5, guiLeft + 218, guiTop + 25, 0xFF000000 + CustomNpcResourceListener.DefaultTextColor); - drawGradientRect(guiLeft + 5, guiTop + 27, guiLeft + 218, guiTop + ySize + 9, 0xA0101010, 0xA0101010); + /** + * Renders the scrollable area inside the background box. + */ + private void renderScrollableScreen(int mouseX, int mouseY) { + drawGradientRect(guiLeft + 133, guiTop + 4, guiLeft + xSize + 33, guiTop + 24, 0xC0101010, 0xC0101010); + drawHorizontalLine(guiLeft + 133, guiLeft + xSize + 33, guiTop + 25, 0xFF000000 + CustomNpcResourceListener.DefaultTextColor); + drawGradientRect(guiLeft + 133, guiTop + 27, guiLeft + xSize + 33, guiTop + ySize + 9, 0xA0101010, 0xA0101010); - if (this.profile != null) { - // Top bar: display NPC display name (centered) -// String topBarText = npc.display.getName(); -// int textWidth = getStringWidthWithoutColor(topBarText); -// int centerX = guiLeft + 5 + ((218 - 10 - textWidth) / 2); -// fontRendererObj.drawString(topBarText, centerX, guiTop + 10, npc.getFaction().color, true); -// -// // Lower section: display NPC properties as label and value pairs -// int y = guiTop + 30; -// int xLabel = guiLeft + 8; -// int xValue = guiLeft + 120; -// int valueColor = 0xFFFFFF; -// String label, value; -// -// // Health -// label = StatCollector.translateToLocal("stats.health") + ": "; -// value = String.valueOf(npc.stats.maxHealth); -// fontRendererObj.drawString(label, xLabel, y, 0x29d6b9, false); -// fontRendererObj.drawString(value, xValue, y, valueColor, false); -// y += 15; -// -// // Damage (using getAttackStrength) -// label = StatCollector.translateToLocal("stats.meleestrength") + ": "; -// value = String.valueOf(npc.stats.getAttackStrength()); -// fontRendererObj.drawString(label, xLabel, y, 0xff5714, false); -// fontRendererObj.drawString(value, xValue, y, valueColor, false); -// y += 15; -// -// // Attack Speed -// label = StatCollector.translateToLocal("stats.meleespeed") + ": "; -// value = String.valueOf(npc.stats.attackSpeed); -// fontRendererObj.drawString(label, xLabel, y, 0xf7ca28, false); -// fontRendererObj.drawString(value, xValue, y, valueColor, false); -// y += 15; -// -// // AI Type (npc.ai.onAttack: 0 = fight, 1 = panic, 2 = retreat, 3 = nothing) -// label = StatCollector.translateToLocal("menu.ai") + ": "; -// int onAttack = npc.ais.onAttack; -// switch (onAttack) { -// case 0: -// value = StatCollector.translateToLocal("gui.retaliate"); -// break; -// case 1: -// value = StatCollector.translateToLocal("gui.panic"); -// break; -// case 2: -// value = StatCollector.translateToLocal("gui.retreat"); -// break; -// case 3: -// default: -// value = StatCollector.translateToLocal("gui.nothing"); -// break; -// } -// fontRendererObj.drawString(label, xLabel, y, 0xce75fa, false); -// fontRendererObj.drawString(value, xValue, y, valueColor, false); -// y += 15; -// -// // Walk Speed -// label = StatCollector.translateToLocal("stats.speed") + ": "; -// value = String.valueOf(npc.ais.getWalkingSpeed()); -// fontRendererObj.drawString(label, xLabel, y, 0xffae0d, false); -// fontRendererObj.drawString(value, xValue, y, valueColor, false); -// y += 15; -// -// // Movement Type (0 = Ground, 1 = Flying) -// label = StatCollector.translateToLocal("movement.type") + ": "; -// int movementType = npc.ais.movementType; -// if (movementType == 0) { -// value = StatCollector.translateToLocal("movement.ground"); -// } else { -// value = StatCollector.translateToLocal("movement.flying"); -// } -// fontRendererObj.drawString(label, xLabel, y, 0x7cff54, false); -// fontRendererObj.drawString(value, xValue, y, valueColor, false); -// y += 15; + if (this.profile != null && slot != null) { + + String topBarText = slot.getName(); + int textWidth = fontRendererObj.getStringWidth(topBarText); + int barCenterX = guiLeft + 133 + ((xSize + 33 - 133) / 2); + int textX = barCenterX - (textWidth / 2); + fontRendererObj.drawString(topBarText, textX, guiTop + 10, 0xFFFFFF, true); + + GL11.glEnable(GL11.GL_SCISSOR_TEST); + ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + int scaleFactor = sr.getScaleFactor(); + + // Define the SCISSOR RECTANGLE to restrict drawing to only within the background box + int scissorX = (guiLeft + 133) * scaleFactor; + int scissorY = (sr.getScaledHeight() - (guiTop + ySize + 6)) * scaleFactor; + int scissorW = (xSize + 33 - 133) * scaleFactor; + int scissorH = (ySize + 4 - 27) * scaleFactor; // The height from 27 to ySize+9 + GL11.glScissor(scissorX, scissorY, scissorW, scissorH); + + GL11.glPushMatrix(); + GL11.glTranslatef(0, -scrollY, 0); // Apply Scroll to Info + + int spacing = 14; + int y = guiTop + 30; + int xLabel = guiLeft + 136; + int boxRightX = guiLeft + xSize + 30; + + String label, result; + int labelColor, resultColor, resultWidth, resultX; + + label = StatCollector.translateToLocal("profile.lastLoaded"); + result = String.valueOf(DATE_FORMAT.format(new Date(slot.getLastLoaded()))); + labelColor = 0xff9c2b; + resultColor = 0xffdb59; + fontRendererObj.drawString(label, xLabel, y, labelColor, false); + resultWidth = fontRendererObj.getStringWidth(result); + resultX = boxRightX - resultWidth - 5; + fontRendererObj.drawString(result, resultX, y, resultColor, false); + y += spacing; + + if(slot.isTemporary()){ + label = StatCollector.translateToLocal("profile.temporary"); + result = (String.valueOf(slot.isTemporary())).toUpperCase(); + labelColor = 0xfc60dd; + resultColor = 0x35fc81; + fontRendererObj.drawString(label, xLabel, y, labelColor, false); + resultWidth = fontRendererObj.getStringWidth(result); + resultX = boxRightX - resultWidth - 5; + fontRendererObj.drawString(result, resultX, y, resultColor, false); + y += spacing; + } + + if (slotInfoMap.containsKey(slot.getId())) { + List infoEntries = slotInfoMap.get(slot.getId()); + for (InfoEntry entry : infoEntries) { + label = StatCollector.translateToLocal(entry.getLabel()); + result = StatCollector.translateToLocal(entry.getResult()); + labelColor = entry.getLabelColor(); + resultColor = entry.getResultColor(); + + // Draw Label + fontRendererObj.drawString(label, xLabel, y, labelColor, false); + + // Calculate Result X Position (Right-Aligned) + resultWidth = fontRendererObj.getStringWidth(result); + resultX = boxRightX - resultWidth - 5; + + // Draw Result at Right-Aligned Position + fontRendererObj.drawString(result, resultX, y, resultColor, false); + + // Move to next line + y += spacing; + } + } + + maxScrollY = Math.max(0, y - (guiTop + ySize - 35)); } + + GL11.glPopMatrix(); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + + // Draw scrollbar only if needed + if (maxScrollY > 0) { + drawScrollbar(); + } + } + + /** + * Handles mouse scroll input **only inside the background box**. + */ + @Override + public void handleMouseInput() { + super.handleMouseInput(); + if(isMouseOverScrollBox(mouseX, mouseY)){ + int delta = Mouse.getEventDWheel(); + if (delta != 0) { + scrollY -= delta / 7; + if (scrollY < 0) scrollY = 0; + if (scrollY > maxScrollY) scrollY = maxScrollY; + } + } + + if (draggingScrollbar) { + if (Mouse.isButtonDown(0)) { + int mouseDiff = mouseY - lastMouseY; + scrollY += (mouseDiff * maxScrollY) / (ySize - 70); + scrollY = Math.max(0, Math.min(scrollY, maxScrollY)); + lastMouseY = mouseY; + } else { + draggingScrollbar = false; + } + } + } + + /** + * Draws the vertical scrollbar (only if needed). + */ + private void drawScrollbar() { + int scrollbarX = guiLeft + xSize + 30; + int scrollbarY = guiTop + 27; + int scrollbarHeight = ySize - 18; + int thumbHeight = Math.max(10, (scrollbarHeight * scrollbarHeight) / (maxScrollY + scrollbarHeight)); + int thumbY = scrollbarY + ((scrollY * (scrollbarHeight - thumbHeight)) / maxScrollY); + + drawRect(scrollbarX, scrollbarY, scrollbarX + 5, scrollbarY + scrollbarHeight, 0xFF333333); + drawRect(scrollbarX, thumbY, scrollbarX + 5, thumbY + thumbHeight, 0xFFAAAAAA); + } + + /** + * Checks if the mouse is inside the background box for scrolling. + */ + private boolean isMouseOverScrollBox(int mouseX, int mouseY) { + return (mouseX >= guiLeft + 133 && mouseX <= guiLeft + xSize + 33 && + mouseY >= guiTop + 27 && mouseY <= guiTop + ySize + 9); } @Override @@ -239,7 +304,7 @@ public class GuiProfiles extends GuiCNPCInventory implements ISubGuiListener, IC } } else { // If not a color code, calculate the width - width += fontRendererObj.getCharWidth(c); + width += Minecraft.getMinecraft().fontRenderer.getCharWidth(c); } } return width; @@ -261,10 +326,15 @@ public class GuiProfiles extends GuiCNPCInventory implements ISubGuiListener, IC @Override public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) { if (guiCustomScroll.id == 0) { + slot = null; selected = scroll.getSelected(); - if (selected != null && !selected.isEmpty()){ - // LOAD SLOT INFORMATION - + if (profile != null && selected != null && !selected.isEmpty()){ + for(Slot checkSlot : profile.slots.values()){ + if(checkSlot.getId() == data.get(selected)){ + slot = checkSlot; + break; + } + } } } } @@ -273,18 +343,79 @@ public class GuiProfiles extends GuiCNPCInventory implements ISubGuiListener, IC public void customScrollDoubleClicked(String selection, GuiCustomScroll scroll) {} public void setGuiData(NBTTagCompound compound) { - this.profile = null; if(compound.hasKey("PROFILE")){ // Load Profile this.profile = new Profile(mc.thePlayer, compound); this.data = new HashMap<>(); + String colorCode = "\u00A7e"; for(Slot slot1 : profile.slots.values()){ - this.data.put(slot1.getName(), slot1.getId()); + String name = slot1.getId() + " - " + slot1.getName(); + if(profile.currentID == slot1.getId()) + name = colorCode + name; + this.data.put(name, slot1.getId()); } } else if(compound.hasKey("PROFILE_INFO")){ - + slotInfoMap = ProfileGetInfoPacket.readSlotsFromNBT(compound); } initGui(); } + /** + * Handles mouse clicks for the scrollbar. + */ + @Override + public void mouseClicked(int mouseX, int mouseY, int button) { + super.mouseClicked(mouseX, mouseY, button); + + if (button == 0) { // Left mouse button + if (isMouseOverScrollbar(mouseX, mouseY)) { + int scrollbarY = guiTop + 27; + int scrollbarHeight = ySize - 35; + int thumbHeight = Math.max(10, (scrollbarHeight * scrollbarHeight) / (maxScrollY + scrollbarHeight)); + int thumbY = scrollbarY + ((scrollY * (scrollbarHeight - thumbHeight)) / maxScrollY); + + // If clicking on scrollbar thumb, start dragging + if (mouseY >= thumbY && mouseY <= thumbY + thumbHeight) { + draggingScrollbar = true; + lastMouseY = mouseY; + } else { + // Click anywhere on scrollbar background to jump position + scrollY = ((mouseY - scrollbarY) * maxScrollY) / scrollbarHeight; + scrollY = Math.max(0, Math.min(scrollY, maxScrollY)); + } + } + } + } + + /** + * Handles mouse dragging while clicking. + */ + @Override + protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { + if (draggingScrollbar) { + int mouseDiff = mouseY - lastMouseY; + scrollY += (mouseDiff * maxScrollY) / (ySize - 70); + scrollY = Math.max(0, Math.min(scrollY, maxScrollY)); + lastMouseY = mouseY; + } + } + + /** + * Handles mouse release. + */ + @Override + protected void mouseMovedOrUp(int mouseX, int mouseY, int state) { + super.mouseMovedOrUp(mouseX, mouseY, state); + if (state == 0) { + draggingScrollbar = false; + } + } + + private boolean isMouseOverScrollbar(int mouseX, int mouseY) { + int scrollbarX = guiLeft + xSize + 30; + int scrollbarY = guiTop + 27; + int scrollbarHeight = ySize - 35; + return (mouseX >= scrollbarX && mouseX <= scrollbarX + 5 && + mouseY >= scrollbarY && mouseY <= scrollbarY + scrollbarHeight); + } } diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiEffectBar.java b/src/main/java/noppes/npcs/client/gui/util/GuiEffectBar.java new file mode 100644 index 000000000..262ff9d0a --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/util/GuiEffectBar.java @@ -0,0 +1,195 @@ +package noppes.npcs.client.gui.util; + +import java.util.ArrayList; +import java.util.List; +import noppes.npcs.client.ClientCacheHandler; +import noppes.npcs.client.renderer.ImageData; +import noppes.npcs.controllers.data.CustomEffect; +import noppes.npcs.controllers.data.PlayerEffect; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.ScaledResolution; +import org.lwjgl.opengl.GL11; + +public class GuiEffectBar extends GuiScreen { + public int x, y, width, height; + public int scrollY = 0; // Tracks vertical scrolling + public int entryHeight = 20; // Height for each effect slot + public List entries = new ArrayList<>(); + public FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer; + + public GuiEffectBar(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public void initGui() { + this.fontRenderer = Minecraft.getMinecraft().fontRenderer; + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + // Draw background of effect bar + drawRect(x, y, x + width, y + height, 0x90000000); + + int totalHeight = entries.size() * (entryHeight + 2); // +2 for padding + if (scrollY < 0) scrollY = 0; + if (scrollY > totalHeight - height) scrollY = Math.max(totalHeight - height, 0); + + // Enable scissoring for vertical clipping + GL11.glEnable(GL11.GL_SCISSOR_TEST); + Minecraft minecraft = Minecraft.getMinecraft(); + ScaledResolution sr = new ScaledResolution(minecraft, minecraft.displayWidth, minecraft.displayHeight); + int scaleFactor = sr.getScaleFactor(); + int scissorX = x * scaleFactor; + int scissorY = (sr.getScaledHeight() - (y + height)) * scaleFactor; + int scissorW = width * scaleFactor; + int scissorH = height * scaleFactor; + GL11.glScissor(scissorX, scissorY, scissorW, scissorH); + + int iconRenderSize = 16; + int padding = 1; + + int startIndex = scrollY / (entryHeight + padding); + int endIndex = Math.min(entries.size(), startIndex + height / (entryHeight + padding) + 1); + + List hoveredTooltip = null; + int tooltipX = 0, tooltipY = 0; + CustomEffect toolTipEffect = null; + + for (int i = startIndex; i < endIndex; i++) { + int drawY = y + (i - startIndex) * (entryHeight + padding) - (scrollY % (entryHeight + padding)); + EffectEntry entry = entries.get(i); + ImageData imageData = ClientCacheHandler.getImageData(entry.effect.getIcon()); + + if (imageData != null && imageData.imageLoaded()) { + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + imageData.bindTexture(); + int iconU = entry.effect.iconX; + int iconV = entry.effect.iconY; + int iconWidth = entry.effect.getWidth(); + int iconHeight = entry.effect.getHeight(); + int texWidth = imageData.getTotalWidth(); + + func_152125_a(x + 2, drawY + 2, iconU, iconV, iconWidth, iconHeight, iconRenderSize, iconRenderSize, texWidth, texWidth); + } + + // If mouse hovers over this effect, save tooltip info + if (mouseX >= x && mouseX < x + width && + mouseY >= drawY && mouseY < drawY + entryHeight) { + int seconds = entry.playerEffect.duration; + hoveredTooltip = new ArrayList<>(); + hoveredTooltip.add(entry.effect.getMenuName()); + hoveredTooltip.add("Time: " + seconds + "s"); + + tooltipX = mouseX; + tooltipY = mouseY; + toolTipEffect = entry.effect; + } + } + + // Disable scissoring for tooltip rendering + GL11.glDisable(GL11.GL_SCISSOR_TEST); + + // Draw vertical scrollbar + if (totalHeight > height) { + int scrollBarWidth = 4; + int scrollBarX = x + width - scrollBarWidth - 2 + 10 ; + drawRect(scrollBarX, y, scrollBarX + scrollBarWidth, y + height, 0xFF333333); + int thumbHeight = Math.max(20, height * height / totalHeight); + int thumbY = y + (scrollY * (height - thumbHeight)) / (totalHeight - height); + drawRect(scrollBarX, thumbY, scrollBarX + scrollBarWidth, thumbY + thumbHeight, 0xFFAAAAAA); + } + + super.drawScreen(mouseX, mouseY, partialTicks); + + // Draw tooltip after all icons + if (hoveredTooltip != null && fontRenderer != null && toolTipEffect != null) { + drawCustomHoveringText(hoveredTooltip, tooltipX, tooltipY, fontRenderer, toolTipEffect); + } + } + + /** + * Adjusted scrolling to move up and down instead of left and right. + */ + public void mouseScrolled(int delta) { + int totalHeight = entries.size() * entryHeight; + scrollY -= delta * 10; // Adjust scroll speed as needed. + if (scrollY < 0) scrollY = 0; + if (scrollY > totalHeight - height) scrollY = Math.max(totalHeight - height, 0); + } + + /** + * Custom tooltip with enlarged effect icon. + */ + public void drawCustomHoveringText(List textLines, int x, int y, FontRenderer font, CustomEffect hoveredEffect) { + if (textLines == null || textLines.isEmpty()) return; + + int maxWidth = 0; + for (String s : textLines) { + int lineWidth = font.getStringWidth(s); + if (lineWidth > maxWidth) { + maxWidth = lineWidth; + } + } + + int iconSize = 32; + + int tooltipHeight = 8 + iconSize + 8; // Reserve space for enlarged icon + if (textLines.size() > 1) { + tooltipHeight += 2 + (textLines.size() - 1) * 10; + } + + int screenWidth = Minecraft.getMinecraft().currentScreen.width; + int screenHeight = Minecraft.getMinecraft().currentScreen.height; + + int tooltipX = x + 12; + int tooltipY = y - 12; + if (tooltipX + maxWidth > screenWidth) { + tooltipX = x - 28 - maxWidth; + } + if (tooltipY + tooltipHeight + 6 > screenHeight) { + tooltipY = screenHeight - tooltipHeight - 6; + } + + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glPushMatrix(); + this.zLevel = 1000.0F; + + // Draw tooltip background + drawRect(tooltipX - 3, tooltipY - 4, tooltipX + maxWidth + 3, tooltipY + tooltipHeight, 0xF0100010); + + // Draw text + int textY = tooltipY; + for (String s : textLines) { + font.drawStringWithShadow(s, tooltipX, textY, 0xFFFFFF); + textY += 10; + } + + // Draw enlarged icon + ImageData imageData = ClientCacheHandler.getImageData(hoveredEffect.getIcon()); + if (imageData != null && imageData.imageLoaded()) { + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + imageData.bindTexture(); + func_152125_a(tooltipX + (maxWidth - iconSize) / 2, textY + 4, hoveredEffect.iconX, hoveredEffect.iconY, hoveredEffect.getWidth(), hoveredEffect.getHeight(), iconSize, iconSize, imageData.getTotalWidth(), imageData.getTotalWidth()); + } + + GL11.glEnable(GL11.GL_DEPTH_TEST); + GL11.glPopMatrix(); + GL11.glPopAttrib(); + } + + public static class EffectEntry { + public CustomEffect effect; + public PlayerEffect playerEffect; + public EffectEntry(CustomEffect effect, PlayerEffect playerEffect) { + this.effect = effect; + this.playerEffect = playerEffect; + } + } +} diff --git a/src/main/java/noppes/npcs/controllers/data/CustomEffect.java b/src/main/java/noppes/npcs/controllers/data/CustomEffect.java index 03520eee8..75591a8c5 100644 --- a/src/main/java/noppes/npcs/controllers/data/CustomEffect.java +++ b/src/main/java/noppes/npcs/controllers/data/CustomEffect.java @@ -22,7 +22,6 @@ public class CustomEffect implements ICustomEffect { public int everyXTick = 20; public String icon = ""; - // CustomNpcPlusDBC.ID + ":textures/gui/icons.png"; public int iconX = 0, iconY = 0; /** diff --git a/src/main/java/tconstruct/client/tabs/InventoryTabCustomNpc.java b/src/main/java/tconstruct/client/tabs/InventoryTabCustomNpc.java index 39f9f1761..9b845c5f5 100644 --- a/src/main/java/tconstruct/client/tabs/InventoryTabCustomNpc.java +++ b/src/main/java/tconstruct/client/tabs/InventoryTabCustomNpc.java @@ -52,7 +52,7 @@ public class InventoryTabCustomNpc extends AbstractTab { mc.displayGuiScreen(new GuiSettings()); } if(tab == -104){ - // Profile Tab + mc.displayGuiScreen(new GuiProfiles()); } } } diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index b7723ab04..95a469c4f 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -1303,3 +1303,7 @@ effect.editor.xPos=X Pos effect.editor.yPos=Y Pos effect.editor.lossOnDeath=Loss on Death +profile.lastLoaded=Last Loaded +profile.info.quest.active=Quests Active +profile.info.quest.finished=Quests Finished +profile.info.dialog.read=Dialogs Read