From fe9bb02aea73f6d50ba3e31cdf017a2cf9cc5225 Mon Sep 17 00:00:00 2001 From: Kam Date: Sun, 23 Feb 2025 04:58:41 -0500 Subject: [PATCH] Better Hud System CNPC+ --- .../data/OverlayQuestTrackingPacket.java | 12 +- .../npcs/client/ClientCacheHandler.java | 3 - .../npcs/client/ClientEventHandler.java | 13 +- .../npcs/client/gui/hud/ClientHudManager.java | 36 +++ .../npcs/client/gui/hud/EnumHudComponent.java | 5 + .../npcs/client/gui/hud/GuiHudEditor.java | 180 ++++++++++++ .../client/gui/hud/GuiOverlayQuestEditor.java | 199 ------------- .../npcs/client/gui/hud/HudComponent.java | 55 ++++ .../client/gui/hud/OverlayQuestTracking.java | 191 ------------ .../gui/hud/QuestTrackingComponent.java | 278 ++++++++++++++++++ .../gui/player/inventory/GuiSettings.java | 4 +- 11 files changed, 575 insertions(+), 401 deletions(-) create mode 100644 src/main/java/noppes/npcs/client/gui/hud/ClientHudManager.java create mode 100644 src/main/java/noppes/npcs/client/gui/hud/EnumHudComponent.java create mode 100644 src/main/java/noppes/npcs/client/gui/hud/GuiHudEditor.java delete mode 100644 src/main/java/noppes/npcs/client/gui/hud/GuiOverlayQuestEditor.java create mode 100644 src/main/java/noppes/npcs/client/gui/hud/HudComponent.java delete mode 100644 src/main/java/noppes/npcs/client/gui/hud/OverlayQuestTracking.java create mode 100644 src/main/java/noppes/npcs/client/gui/hud/QuestTrackingComponent.java diff --git a/src/main/java/kamkeel/npcs/network/packets/data/OverlayQuestTrackingPacket.java b/src/main/java/kamkeel/npcs/network/packets/data/OverlayQuestTrackingPacket.java index af999382a..f64756ba1 100644 --- a/src/main/java/kamkeel/npcs/network/packets/data/OverlayQuestTrackingPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/data/OverlayQuestTrackingPacket.java @@ -12,7 +12,9 @@ import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.client.ClientCacheHandler; -import noppes.npcs.client.gui.hud.OverlayQuestTracking; +import noppes.npcs.client.gui.hud.ClientHudManager; +import noppes.npcs.client.gui.hud.EnumHudComponent; +import noppes.npcs.client.gui.hud.HudComponent; import java.io.IOException; @@ -46,12 +48,14 @@ public final class OverlayQuestTrackingPacket extends AbstractPacket { @SideOnly(Side.CLIENT) @Override public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + HudComponent component = ClientHudManager.getInstance().getHudComponents().get(EnumHudComponent.QuestTracker); try { NBTTagCompound nbt = ByteBufUtils.readNBT(in); - ClientCacheHandler.questTrackingOverlay = new OverlayQuestTracking(Minecraft.getMinecraft()); - ClientCacheHandler.questTrackingOverlay.setOverlayData(nbt); + component.loadData(nbt); + if(nbt.hasNoTags()) + component.hasData = false; } catch (IOException e) { - ClientCacheHandler.questTrackingOverlay = null; + component.hasData = false; } } } diff --git a/src/main/java/noppes/npcs/client/ClientCacheHandler.java b/src/main/java/noppes/npcs/client/ClientCacheHandler.java index 16a81a286..abfc0993e 100644 --- a/src/main/java/noppes/npcs/client/ClientCacheHandler.java +++ b/src/main/java/noppes/npcs/client/ClientCacheHandler.java @@ -1,7 +1,6 @@ package noppes.npcs.client; import net.minecraft.util.ResourceLocation; -import noppes.npcs.client.gui.hud.OverlayQuestTracking; import noppes.npcs.client.gui.customoverlay.OverlayCustom; import noppes.npcs.client.gui.select.GuiSoundSelection; import noppes.npcs.client.gui.select.GuiTextureSelection; @@ -19,7 +18,6 @@ import java.util.UUID; public class ClientCacheHandler { private static final CacheHashMap> imageDataCache = new CacheHashMap<>((long) ConfigClient.CacheLife * 60 * 1000); public static PlayerData playerData = new PlayerData(); - public static OverlayQuestTracking questTrackingOverlay = null; public static HashMap customOverlays = new HashMap<>(); public static HashMap> skinOverlays = new HashMap<>(); public static HashMap playerAnimations = new HashMap<>(); @@ -56,7 +54,6 @@ public class ClientCacheHandler { public static void clearCache() { ClientCacheHandler.imageDataCache.clear(); - ClientCacheHandler.questTrackingOverlay = null; ClientCacheHandler.customOverlays.clear(); ClientCacheHandler.skinOverlays.clear(); ClientCacheHandler.playerAnimations.clear(); diff --git a/src/main/java/noppes/npcs/client/ClientEventHandler.java b/src/main/java/noppes/npcs/client/ClientEventHandler.java index eb9e4825a..6d5bfedd8 100644 --- a/src/main/java/noppes/npcs/client/ClientEventHandler.java +++ b/src/main/java/noppes/npcs/client/ClientEventHandler.java @@ -16,6 +16,9 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.*; import noppes.npcs.client.gui.customoverlay.OverlayCustom; +import noppes.npcs.client.gui.hud.ClientHudManager; +import noppes.npcs.client.gui.hud.EnumHudComponent; +import noppes.npcs.client.gui.hud.QuestTrackingComponent; import noppes.npcs.client.renderer.MarkRenderer; import noppes.npcs.client.renderer.RenderCNPCPlayer; import noppes.npcs.constants.EnumAnimationPart; @@ -90,9 +93,15 @@ public class ClientEventHandler { overlayCustom.renderGameOverlay(event.partialTicks); } - if (ClientCacheHandler.questTrackingOverlay != null) { - ClientCacheHandler.questTrackingOverlay.renderOverlay(event.partialTicks); + if(ClientHudManager.getInstance().getHudComponents().isEmpty()){ + ClientHudManager.getInstance().registerHud(EnumHudComponent.QuestTracker, new QuestTrackingComponent(Minecraft.getMinecraft())); } + + ClientHudManager.getInstance().renderAllHUDs(event.partialTicks); + +// if (ClientCacheHandler.questTrackingOverlay != null) { +// ClientCacheHandler.questTrackingOverlay.renderOverlay(event.partialTicks); +// } } } diff --git a/src/main/java/noppes/npcs/client/gui/hud/ClientHudManager.java b/src/main/java/noppes/npcs/client/gui/hud/ClientHudManager.java new file mode 100644 index 000000000..362773b63 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/hud/ClientHudManager.java @@ -0,0 +1,36 @@ +package noppes.npcs.client.gui.hud; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class ClientHudManager { + private static ClientHudManager instance; + private HashMap hudComponents = new HashMap<>(); + + private ClientHudManager() {} + + public static ClientHudManager getInstance() { + if(instance == null) + instance = new ClientHudManager(); + return instance; + } + + public void registerHud(EnumHudComponent enumHudComponent, HudComponent hud) { + hudComponents.put(enumHudComponent, hud); + } + + public HashMap getHudComponents() { + return hudComponents; + } + + /** + * Renders all enabled HUD components (normal mode). + */ + public void renderAllHUDs(float partialTicks) { + for (HudComponent hud : hudComponents.values()) { + if(hud.enabled) + hud.renderOnScreen(partialTicks); + } + } +} diff --git a/src/main/java/noppes/npcs/client/gui/hud/EnumHudComponent.java b/src/main/java/noppes/npcs/client/gui/hud/EnumHudComponent.java new file mode 100644 index 000000000..c1ac41239 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/hud/EnumHudComponent.java @@ -0,0 +1,5 @@ +package noppes.npcs.client.gui.hud; + +public enum EnumHudComponent { + QuestTracker +} diff --git a/src/main/java/noppes/npcs/client/gui/hud/GuiHudEditor.java b/src/main/java/noppes/npcs/client/gui/hud/GuiHudEditor.java new file mode 100644 index 000000000..f7752f0e8 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/hud/GuiHudEditor.java @@ -0,0 +1,180 @@ +package noppes.npcs.client.gui.hud; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.Minecraft; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; +import java.util.List; +import java.util.ArrayList; + +public class GuiHudEditor extends GuiScreen { + private GuiScreen parent; + // Currently selected HUD component for moving/resizing. + private HudComponent selectedComponent; + private int dragOffsetX, dragOffsetY; + private boolean dragging = false; + private boolean resizing = false; + private int initialScale, initialMouseX, initialMouseY; + private final int HANDLE_SIZE = 10; + private final int RESET_POSITION_ID = 100; + + public GuiHudEditor(GuiScreen parent) { + this.parent = parent; + selectedComponent = ClientHudManager.getInstance().getHudComponents().get(EnumHudComponent.QuestTracker); + } + + @Override + public void initGui() { + buttonList.clear(); + // Global "Save and Close" button always visible. + buttonList.add(new GuiButton(0, width / 2 - 60, height - 45, 120, 20, "Save and Close")); + updateCustomButtons(); + } + + /** + * Updates the custom buttons shown on screen. + * Removes all buttons except the global Save button (id 0) + * then adds the selected component's custom buttons plus a Reset Position button. + */ + private void updateCustomButtons() { + // Remove any buttons that are not the global Save (id 0) + for (int i = buttonList.size() - 1; i >= 0; i--) { + GuiButton btn = (GuiButton) buttonList.get(i); + if (btn.id != 0) { + buttonList.remove(i); + } + } + if (selectedComponent != null) { + int baseX = width / 2 - 60; + int baseY = height - 70; // Start above the Save button. + List customButtons = new ArrayList<>(); + selectedComponent.addEditorButtons(customButtons); + int offsetY = 0; + for (GuiButton btn : customButtons) { + btn.xPosition = baseX; + btn.yPosition = baseY - offsetY; + offsetY += btn.height + 5; + buttonList.add(btn); + } + // Always add the Reset Position button. + GuiButton resetPos = new GuiButton(RESET_POSITION_ID, baseX, baseY - offsetY, 120, 20, "Reset Position"); + buttonList.add(resetPos); + } + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + drawDefaultBackground(); + // Render each HUD in editing mode. + for (HudComponent hud : ClientHudManager.getInstance().getHudComponents().values()) { + hud.renderEditing(); + } + super.drawScreen(mouseX, mouseY, partialTicks); + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { + // Iterate over HUD components to check for selection. + for (HudComponent hud : ClientHudManager.getInstance().getHudComponents().values()) { + ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + // Use base scaling (scale/100.0F) + float effectiveScale = (hud.scale / 100.0F); + int actualX = (int)((float)hud.posX / 100F * res.getScaledWidth()); + int actualY = (int)((float)hud.posY / 100F * res.getScaledHeight()); + int absWidth = (int)(hud.overlayWidth * effectiveScale); + int absHeight = (int)(hud.overlayHeight * effectiveScale); + if (mouseX >= actualX && mouseX <= actualX + absWidth && + mouseY >= actualY && mouseY <= actualY + absHeight) { + // Check if clicking on the resize handle (bottom-right). + if (mouseX >= actualX + absWidth - HANDLE_SIZE && mouseY >= actualY + absHeight - HANDLE_SIZE) { + resizing = true; + selectedComponent = hud; + initialScale = hud.scale; + initialMouseX = mouseX; + initialMouseY = mouseY; + } else { + dragging = true; + selectedComponent = hud; + dragOffsetX = mouseX - actualX; + dragOffsetY = mouseY - actualY; + } + updateCustomButtons(); + break; + } + } + super.mouseClicked(mouseX, mouseY, mouseButton); + } + + @Override + protected void mouseMovedOrUp(int mouseX, int mouseY, int state) { + dragging = false; + resizing = false; + // Optionally keep selectedComponent so its buttons remain visible. + super.mouseMovedOrUp(mouseX, mouseY, state); + } + + @Override + public void updateScreen() { + if (selectedComponent != null) { + ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + int scaledWidth = res.getScaledWidth(); + int scaledHeight = res.getScaledHeight(); + float effectiveScale = (selectedComponent.scale / 100.0F); + if (dragging) { + int newX = Mouse.getX() * width / mc.displayWidth - dragOffsetX; + int newY = scaledHeight - (Mouse.getY() * scaledHeight / mc.displayHeight) - dragOffsetY; + if (newX < 0) newX = 0; + if (newY < 0) newY = 0; + if (newX + (int)(selectedComponent.overlayWidth * effectiveScale) > scaledWidth) + newX = scaledWidth - (int)(selectedComponent.overlayWidth * effectiveScale); + if (newY + (int)(selectedComponent.overlayHeight * effectiveScale) > scaledHeight) + newY = scaledHeight - (int)(selectedComponent.overlayHeight * effectiveScale); + selectedComponent.posX = (int)(100F * newX / scaledWidth); + selectedComponent.posY = (int)(100F * newY / scaledHeight); + } + if (resizing) { + int currentMouseX = Mouse.getX() * width / mc.displayWidth; + int deltaX = currentMouseX - initialMouseX; + selectedComponent.scale = initialScale + deltaX; // 1 pixel = 1% change. + if (selectedComponent.scale < 50) selectedComponent.scale = 50; + if (selectedComponent.scale > 300) selectedComponent.scale = 300; + } + } + super.updateScreen(); + } + + @Override + protected void actionPerformed(GuiButton button) { + if (button.id == 0) { + close(); + } else if (button.id == RESET_POSITION_ID) { + // Reset position for selected HUD. + if (selectedComponent != null) { + selectedComponent.posX = 50; + selectedComponent.posY = 50; + updateCustomButtons(); + } + } else { + // Dispatch other button actions to the selected component. + if (selectedComponent != null) { + selectedComponent.onEditorButtonPressed(button); + updateCustomButtons(); + } + } + } + protected void keyTyped(char typedChar, int keyCode) { + if (keyCode == 1) { + close(); + } + } + + public void close() { + for (HudComponent hud : ClientHudManager.getInstance().getHudComponents().values()) { + hud.isEditting = false; + hud.save(); + } + mc.displayGuiScreen(parent); + } +} diff --git a/src/main/java/noppes/npcs/client/gui/hud/GuiOverlayQuestEditor.java b/src/main/java/noppes/npcs/client/gui/hud/GuiOverlayQuestEditor.java deleted file mode 100644 index 4ce36b5aa..000000000 --- a/src/main/java/noppes/npcs/client/gui/hud/GuiOverlayQuestEditor.java +++ /dev/null @@ -1,199 +0,0 @@ -package noppes.npcs.client.gui.hud; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.ScaledResolution; -import noppes.npcs.config.ConfigClient; -import org.lwjgl.input.Mouse; -import org.lwjgl.opengl.GL11; - -public class GuiOverlayQuestEditor extends GuiScreen { - private GuiScreen parent; - // posX and posY are stored as percentages (0-100) of the scaled resolution. - private int posX, posY, scale, textAlign; - private final int overlayWidth = 200, overlayHeight = 120; - - private boolean dragging = false; - private int dragOffsetX, dragOffsetY; - private boolean resizing = false; - private int initialScale, initialMouseX, initialMouseY; - private final int HANDLE_SIZE = 10; - - private GuiButton saveButton, alignButton, resetButton; - - public GuiOverlayQuestEditor(GuiScreen parent) { - this.parent = parent; - // Load config values (stored as percentages) - posX = ConfigClient.QuestOverlayX; - posY = ConfigClient.QuestOverlayY; - scale = ConfigClient.QuestOverlayScale; - textAlign = ConfigClient.QuestOverlayTextAlign; - } - - @Override - public void initGui() { - buttonList.clear(); - saveButton = new GuiButton(0, width / 2 - 60, height - 45, 120, 20, "Save and Close"); - alignButton = new GuiButton(1, width / 2 - 60, height - 70, 120, 20, getAlignText()); - resetButton = new GuiButton(3, width / 2 - 60, height - 95, 120, 20, "Reset to Center"); - buttonList.add(saveButton); - buttonList.add(alignButton); - buttonList.add(resetButton); - } - - private String getAlignText() { - switch (textAlign) { - case 0: return "Align: Left"; - case 1: return "Align: Center"; - case 2: return "Align: Right"; - default: return "Align: Unknown"; - } - } - - @Override - public void drawScreen(int mouseX, int mouseY, float partialTicks) { - drawDefaultBackground(); - ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); - int actualX = (int) ((float) posX / 100F * res.getScaledWidth()); - int actualY = (int) ((float) posY / 100F * res.getScaledHeight()); - - GL11.glPushMatrix(); - GL11.glTranslatef(actualX, actualY, 0); - float s = scale / 100.0F; - GL11.glScalef(s, s, s); - - // Draw preview background - drawRect(0, 0, overlayWidth, overlayHeight, 0x40FFFFFF); - - int currentY = 5; - currentY = renderDemoTextBlock(new String[] { "Dummy Quest Title" }, currentY, textAlign, 0xFFFFFF); - drawDecorativeLine(currentY - 1); - currentY += 8; - - currentY = renderDemoTextBlock(new String[] { "Dummy Category" }, currentY, textAlign, 0xCCCCCC); - drawDecorativeLine(currentY - 1); - currentY += 8; - - currentY = renderDemoTextBlock(new String[] { "Objective: Kill 10 mobs", "Objective: Collect 5 items" }, currentY, textAlign, 0xAAAAAA); - drawDecorativeLine(currentY - 1); - currentY += 8; - - renderDemoTextBlock(new String[] { "Turn in with NPC" }, currentY, textAlign, 0xAAAAAA); - - // Draw resize handle at bottom-right - drawRect(overlayWidth - HANDLE_SIZE, overlayHeight - HANDLE_SIZE, overlayWidth, overlayHeight, 0xFFCCCCCC); - GL11.glPopMatrix(); - - super.drawScreen(mouseX, mouseY, partialTicks); - } - - private int renderDemoTextBlock(String[] lines, int startY, int align, int color) { - int y = startY; - FontRenderer font = Minecraft.getMinecraft().fontRenderer; - for (String line : lines) { - int strWidth = font.getStringWidth(line); - int xOffset; - if (align == 1) - xOffset = (overlayWidth - strWidth) / 2; - else if (align == 2) - xOffset = overlayWidth - strWidth - 5; - else - xOffset = 5; - font.drawStringWithShadow(line, xOffset, y, color); - y += font.FONT_HEIGHT + 4; - } - return y; - } - - private void drawDecorativeLine(int y) { - drawHorizontalLine(5, overlayWidth - 5, y, 0xFF777777); - drawHorizontalLine(5, overlayWidth - 5, y + 1, 0xFFA8A8A8); - drawHorizontalLine(5, overlayWidth - 5, y + 2, 0xFFFFFFFF); - } - - @Override - protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { - ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); - int actualX = (int) ((float) posX / 100F * res.getScaledWidth()); - int actualY = (int) ((float) posY / 100F * res.getScaledHeight()); - float s = scale / 100.0F; - int absWidth = (int) (overlayWidth * s); - int absHeight = (int) (overlayHeight * s); - if (mouseX >= actualX && mouseX <= actualX + absWidth && - mouseY >= actualY && mouseY <= actualY + absHeight) { - // Check if within resize handle - if (mouseX >= actualX + absWidth - HANDLE_SIZE && mouseY >= actualY + absHeight - HANDLE_SIZE) { - resizing = true; - initialScale = scale; - initialMouseX = mouseX; - initialMouseY = mouseY; - } else { - dragging = true; - dragOffsetX = mouseX - actualX; - dragOffsetY = mouseY - actualY; - } - } - super.mouseClicked(mouseX, mouseY, mouseButton); - } - - // For Minecraft 1.7.10, use mouseMovedOrUp for button release. - @Override - protected void mouseMovedOrUp(int mouseX, int mouseY, int state) { - dragging = false; - resizing = false; - super.mouseMovedOrUp(mouseX, mouseY, state); - } - - @Override - public void updateScreen() { - ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); - int scaledWidth = res.getScaledWidth(); - int scaledHeight = res.getScaledHeight(); - // Calculate absolute dimensions for the overlay preview - int absWidth = (int) (overlayWidth * (scale / 100.0F)); - int absHeight = (int) (overlayHeight * (scale / 100.0F)); - - if (dragging) { - // Compute new absolute position in pixels - int newX = Mouse.getX() * width / mc.displayWidth - dragOffsetX; - int newY = scaledHeight - (Mouse.getY() * scaledHeight / mc.displayHeight) - dragOffsetY; - // Clamp the absolute position so that the overlay stays fully on screen - if (newX < 0) newX = 0; - if (newY < 0) newY = 0; - if (newX + absWidth > scaledWidth) newX = scaledWidth - absWidth; - if (newY + absHeight > scaledHeight) newY = scaledHeight - absHeight; - // Convert back to percentage values - posX = (int) (100F * newX / scaledWidth); - posY = (int) (100F * newY / scaledHeight); - } - if (resizing) { - int currentMouseX = Mouse.getX() * width / mc.displayWidth; - int deltaX = currentMouseX - initialMouseX; - scale = initialScale + deltaX; // 1 pixel = 1% change - if (scale < 50) scale = 50; - if (scale > 300) scale = 300; - } - super.updateScreen(); - } - - @Override - protected void actionPerformed(GuiButton button) { - if (button.id == 0) { // Save and close - ConfigClient.QuestOverlayX = posX; - ConfigClient.QuestOverlayY = posY; - ConfigClient.QuestOverlayScale = scale; - ConfigClient.QuestOverlayTextAlign = textAlign; - if (ConfigClient.config.hasChanged()) - ConfigClient.config.save(); - mc.displayGuiScreen(parent); - } else if (button.id == 1) { // Cycle alignment - textAlign = (textAlign + 1) % 3; - button.displayString = getAlignText(); - } else if (button.id == 3) { // Reset to center - posX = 50; - posY = 50; - } - } -} diff --git a/src/main/java/noppes/npcs/client/gui/hud/HudComponent.java b/src/main/java/noppes/npcs/client/gui/hud/HudComponent.java new file mode 100644 index 000000000..b004ecfa1 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/hud/HudComponent.java @@ -0,0 +1,55 @@ +package noppes.npcs.client.gui.hud; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.client.gui.GuiButton; +import java.util.List; + +public abstract class HudComponent { + // Stored as percentages (0-100) of the current screen resolution. + protected int posX = 50, posY = 50, scale = 100, textAlign = 0; + + public boolean enabled = true; + public boolean hasData = false; + public boolean isEditting = false; + + protected final int overlayWidth = 200, overlayHeight = 120; + + /** + * Loads custom HUD data from an NBT compound. + */ + public abstract void loadData(NBTTagCompound compound); + + /** + * Loads HUD settings from the config. + */ + public abstract void load(); + + /** + * Saves HUD settings to the config. + */ + public abstract void save(); + + /** + * Renders the HUD as it appears during normal gameplay. + */ + public abstract void renderOnScreen(float partialTicks); + + /** + * Renders the HUD in edit mode (with handles, borders, dummy data, etc.). + */ + public abstract void renderEditing(); + + /** + * Allows the component to add its own editor buttons. + */ + public void addEditorButtons(List buttonList) { + // Default: do nothing. + } + + /** + * Handles editor button actions specific to this HUD component. + */ + public void onEditorButtonPressed(GuiButton button) { + // Default: do nothing. + } +} diff --git a/src/main/java/noppes/npcs/client/gui/hud/OverlayQuestTracking.java b/src/main/java/noppes/npcs/client/gui/hud/OverlayQuestTracking.java deleted file mode 100644 index a68cd746a..000000000 --- a/src/main/java/noppes/npcs/client/gui/hud/OverlayQuestTracking.java +++ /dev/null @@ -1,191 +0,0 @@ -package noppes.npcs.client.gui.hud; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import noppes.npcs.config.ConfigClient; -import noppes.npcs.controllers.data.Quest; -import org.lwjgl.opengl.GL11; - -import java.util.ArrayList; -import java.util.List; - -public class OverlayQuestTracking extends Gui { - private Minecraft mc; - - // Dimensions for overlay background - private int overlayWidth = 200; - private int overlayHeight = 120; - - // Text lists for each section - private ArrayList questTitleLines = new ArrayList<>(); - private ArrayList questCategoryLines = new ArrayList<>(); - private ArrayList objectiveLines = new ArrayList<>(); - private ArrayList turnInLines = new ArrayList<>(); - - public OverlayQuestTracking(Minecraft mc) { - this.mc = mc; - } - - /** - * Updates overlay text based on quest data. - * Converts "&" codes into Minecraft formatting codes. - */ - public void setQuestData(Quest quest, String category, List objectives, String turnIn) { - questTitleLines = splitLines(convertColorCodes(quest.getName()), overlayWidth - 10); - questCategoryLines = splitLines(convertColorCodes(category), overlayWidth - 10); - objectiveLines.clear(); - for (String obj : objectives) { - objectiveLines.add(convertColorCodes(obj)); - } - turnInLines.clear(); - if (turnIn != null && !turnIn.isEmpty()) { - turnInLines.add(convertColorCodes(turnIn)); - } - } - - /** - * Reads overlay data from an NBT compound and updates the overlay. - */ - public void setOverlayData(NBTTagCompound compound) { - Quest quest = new Quest(); - quest.readNBT(compound.getCompoundTag("Quest")); - String category = compound.getString("CategoryName"); - - ArrayList objectives = new ArrayList<>(); - NBTTagList objectiveList = compound.getTagList("ObjectiveList", 8); - for (int i = 0; i < objectiveList.tagCount(); i++) { - String objective = objectiveList.getStringTagAt(i); - String[] split = objective.split(":"); - split = split[split.length - 1].split("/"); - boolean completed = false; - try { - if (split.length < 2) - throw new NumberFormatException("Insufficient data"); - int killed = Integer.parseInt(split[0].trim()); - int total = Integer.parseInt(split[1].trim()); - if (killed / total == 1) - completed = true; - } catch (NumberFormatException e) { - if (objective.endsWith("(Done)") || objective.endsWith("(read)") || - (objective.endsWith("Found") && !objective.endsWith("Not Found"))) - completed = true; - } - if (completed) { - objective = "&a&o&m" + objective; - } else { - objective = "&o" + objective; - } - objectives.add(objective); - } - - String turnIn = ""; - boolean instantComplete = compound.getBoolean("Instant"); - if (instantComplete) { - turnIn = "Completed automatically"; - } else { - String npcName = compound.getString("TurnInNPC"); - if (!npcName.isEmpty()) { - turnIn = "Complete with " + npcName; - } - } - setQuestData(quest, category, objectives, turnIn); - } - - /** - * Renders the overlay using the current configuration settings. - * Here, posX and posY (from config, treated as percentages) are used relative to the current scaled resolution. - */ - public void renderOverlay(float partialTicks) { - ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); - int actualX = (int) ((float) ConfigClient.QuestOverlayX / 100F * res.getScaledWidth()); - int actualY = (int) ((float) ConfigClient.QuestOverlayY / 100F * res.getScaledHeight()); - float s = ConfigClient.QuestOverlayScale / 100.0F; - int textAlign = ConfigClient.QuestOverlayTextAlign; // 0: left, 1: center, 2: right - - GL11.glPushMatrix(); - GL11.glTranslatef(actualX, actualY, 0); - GL11.glScalef(s, s, s); - - // Draw a lighter, semi-transparent background - drawRect(0, 0, overlayWidth, overlayHeight, 0x40FFFFFF); - - int currentY = 5; - currentY = renderTextBlock(questTitleLines, currentY, textAlign, 0xFFFFFF); - drawDecorativeLine(currentY - 1); - currentY += 8; - - currentY = renderTextBlock(questCategoryLines, currentY, textAlign, 0xCCCCCC); - drawDecorativeLine(currentY - 1); - currentY += 8; - - currentY = renderTextBlock(objectiveLines, currentY, textAlign, 0xAAAAAA); - if (!turnInLines.isEmpty()) { - drawDecorativeLine(currentY - 1); - currentY += 8; - } - - renderTextBlock(turnInLines, currentY, textAlign, 0xAAAAAA); - - GL11.glPopMatrix(); - } - - private int renderTextBlock(List lines, int startY, int align, int color) { - int y = startY; - FontRenderer font = mc.fontRenderer; - for (String line : lines) { - int strWidth = font.getStringWidth(line); - int xOffset; - if (align == 1) - xOffset = (overlayWidth - strWidth) / 2; - else if (align == 2) - xOffset = overlayWidth - strWidth - 5; - else - xOffset = 5; - font.drawStringWithShadow(line, xOffset, y, color); - y += font.FONT_HEIGHT + 4; - } - return y; - } - - private void drawDecorativeLine(int y) { - int lineStart = 5; - int lineEnd = overlayWidth - 5; - drawHorizontalLine(lineStart, lineEnd, y, 0xFF777777); - drawHorizontalLine(lineStart, lineEnd, y + 1, 0xFFA8A8A8); - drawHorizontalLine(lineStart, lineEnd, y + 2, 0xFFFFFFFF); - } - - private ArrayList splitLines(String text, int maxWidth) { - ArrayList lines = new ArrayList<>(); - String[] words = text.split(" "); - StringBuilder current = new StringBuilder(); - FontRenderer font = mc.fontRenderer; - for (String word : words) { - String test = current.length() == 0 ? word : current.toString() + " " + word; - if (font.getStringWidth(test) > maxWidth) { - if (current.length() > 0) { - lines.add(current.toString()); - current = new StringBuilder(word); - } else { - lines.add(word); - } - } else { - if (current.length() > 0) - current.append(" ").append(word); - else - current.append(word); - } - } - if (current.length() > 0) - lines.add(current.toString()); - return lines; - } - - private String convertColorCodes(String text) { - return text.replaceAll("&([0-9a-fk-or])", "§$1"); - } -} diff --git a/src/main/java/noppes/npcs/client/gui/hud/QuestTrackingComponent.java b/src/main/java/noppes/npcs/client/gui/hud/QuestTrackingComponent.java new file mode 100644 index 000000000..5236c0c19 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/hud/QuestTrackingComponent.java @@ -0,0 +1,278 @@ +package noppes.npcs.client.gui.hud; + +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.gui.GuiButton; +import noppes.npcs.config.ConfigClient; +import org.lwjgl.opengl.GL11; +import noppes.npcs.controllers.data.Quest; +import java.util.ArrayList; +import java.util.List; + +public class QuestTrackingComponent extends HudComponent { + private Minecraft mc; + private ArrayList questTitleLines = new ArrayList<>(); + private ArrayList questCategoryLines = new ArrayList<>(); + private ArrayList objectiveLines = new ArrayList<>(); + private ArrayList turnInLines = new ArrayList<>(); + + public QuestTrackingComponent(Minecraft mc) { + this.mc = mc; + load(); + } + + /** + * Sets quest data similar to the original OverlayQuestTracking. + */ + public void setQuestData(Quest quest, String category, List objectives, String turnIn) { + questTitleLines = splitLines(convertColorCodes(quest.getName()), overlayWidth - 10); + questCategoryLines = splitLines(convertColorCodes(category), overlayWidth - 10); + objectiveLines.clear(); + for (String obj : objectives) { + objectiveLines.add(convertColorCodes(obj)); + } + turnInLines.clear(); + if (turnIn != null && !turnIn.isEmpty()) { + turnInLines.add(convertColorCodes(turnIn)); + } + } + + /** + * Loads overlay data from an NBT compound. + */ + public void setOverlayData(NBTTagCompound compound) { + Quest quest = new Quest(); + quest.readNBT(compound.getCompoundTag("Quest")); + String category = compound.getString("CategoryName"); + + ArrayList objectives = new ArrayList<>(); + NBTTagList objectiveList = compound.getTagList("ObjectiveList", 8); + for (int i = 0; i < objectiveList.tagCount(); i++) { + String objective = objectiveList.getStringTagAt(i); + // Process objective if needed. + objectives.add(objective); + } + String turnIn = ""; + boolean instantComplete = compound.getBoolean("Instant"); + if (instantComplete) { + turnIn = "Completed automatically"; + } else { + String npcName = compound.getString("TurnInNPC"); + if (!npcName.isEmpty()) { + turnIn = "Complete with " + npcName; + } + } + setQuestData(quest, category, objectives, turnIn); + hasData = true; + } + + @Override + public void loadData(NBTTagCompound compound) { + setOverlayData(compound); + } + + @Override + public void load() { + posX = ConfigClient.QuestOverlayX; + posY = ConfigClient.QuestOverlayY; + scale = ConfigClient.QuestOverlayScale; + textAlign = ConfigClient.QuestOverlayTextAlign; + } + + @Override + public void save() { + ConfigClient.QuestOverlayX = posX; + ConfigClient.QuestOverlayXProperty.set(ConfigClient.QuestOverlayX); + + ConfigClient.QuestOverlayY = posY; + ConfigClient.QuestOverlayYProperty.set(ConfigClient.QuestOverlayY); + + ConfigClient.QuestOverlayScale = scale; + ConfigClient.QuestOverlayScaleProperty.set(ConfigClient.QuestOverlayScale); + + ConfigClient.QuestOverlayTextAlign = textAlign; + ConfigClient.QuestOverlayTextAlignProperty.set(ConfigClient.QuestOverlayTextAlign); + + if (ConfigClient.config.hasChanged()) { + ConfigClient.config.save(); + } + } + + @Override + public void renderOnScreen(float partialTicks) { + if (!hasData || isEditting) + return; + + ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + int actualX = (int)((float) posX / 100F * res.getScaledWidth()); + int actualY = (int)((float) posY / 100F * res.getScaledHeight()); + // Use base scaling: effective scale = scale/100.0F + float s = (scale / 100.0F); + + GL11.glPushMatrix(); + GL11.glTranslatef(actualX, actualY, 0); + GL11.glScalef(s, s, s); + + // Draw background (semi-transparent). + drawRect(0, 0, overlayWidth, overlayHeight, 0x40FFFFFF); + + int currentY = 5; + currentY = renderTextBlock(questTitleLines, currentY, textAlign, 0xFFFFFF); + drawDecorativeLine(currentY - 1); + currentY += 8; + + currentY = renderTextBlock(questCategoryLines, currentY, textAlign, 0xCCCCCC); + drawDecorativeLine(currentY - 1); + currentY += 8; + + currentY = renderTextBlock(objectiveLines, currentY, textAlign, 0xAAAAAA); + if (!turnInLines.isEmpty()) { + drawDecorativeLine(currentY - 1); + currentY += 8; + } + renderTextBlock(turnInLines, currentY, textAlign, 0xAAAAAA); + + GL11.glPopMatrix(); + } + + @Override + public void renderEditing() { + isEditting = true; + ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + int actualX = (int)((float) posX / 100F * res.getScaledWidth()); + int actualY = (int)((float) posY / 100F * res.getScaledHeight()); + float s = (scale / 100.0F); + + GL11.glPushMatrix(); + GL11.glTranslatef(actualX, actualY, 0); + GL11.glScalef(s, s, s); + + // Draw editing background, border, and resize handle. + drawRect(0, 0, overlayWidth, overlayHeight, 0x40FFFFFF); + drawRectOutline(0, 0, overlayWidth, overlayHeight, 0xFFAAAAAA); + drawRect(overlayWidth - 10, overlayHeight - 10, overlayWidth, overlayHeight, 0xFFCCCCCC); + + int currentY = 5; + currentY = renderDemoTextBlock(new String[] { "Dummy Quest Title" }, currentY, textAlign, 0xFFFFFF); + drawDecorativeLine(currentY - 1); + currentY += 8; + currentY = renderDemoTextBlock(new String[] { "Dummy Category" }, currentY, textAlign, 0xCCCCCC); + drawDecorativeLine(currentY - 1); + currentY += 8; + currentY = renderDemoTextBlock(new String[] { "Objective: Kill 10 mobs", "Objective: Collect 5 items" }, currentY, textAlign, 0xAAAAAA); + drawDecorativeLine(currentY - 1); + currentY += 8; + renderDemoTextBlock(new String[] { "Turn in with NPC" }, currentY, textAlign, 0xAAAAAA); + + GL11.glPopMatrix(); + } + + @Override + public void addEditorButtons(List buttonList) { + // Add buttons similar to the original editor: ID 1 for alignment, ID 3 for reset. + buttonList.add(new GuiButton(1, 100, Minecraft.getMinecraft().displayHeight - 70, 120, 20, getAlignText())); + buttonList.add(new GuiButton(3, 100, Minecraft.getMinecraft().displayHeight - 95, 120, 20, "Reset to Center")); + } + + @Override + public void onEditorButtonPressed(GuiButton button) { + if (button.id == 1) { // Cycle alignment. + textAlign = (textAlign + 1) % 3; + button.displayString = getAlignText(); + } else if (button.id == 3) { // Reset to center. + posX = 50; + posY = 50; + } + } + + private String getAlignText() { + switch (textAlign) { + case 0: return "Align: Left"; + case 1: return "Align: Center"; + case 2: return "Align: Right"; + default: return "Align: Unknown"; + } + } + + // ---------- Helper rendering methods ----------- + private int renderTextBlock(List lines, int startY, int align, int color) { + int y = startY; + FontRenderer font = mc.fontRenderer; + for (String line : lines) { + int strWidth = font.getStringWidth(line); + int xOffset = (align == 1) ? (overlayWidth - strWidth) / 2 + : (align == 2) ? overlayWidth - strWidth - 5 : 5; + font.drawStringWithShadow(line, xOffset, y, color); + y += font.FONT_HEIGHT + 4; + } + return y; + } + + private int renderDemoTextBlock(String[] lines, int startY, int align, int color) { + int y = startY; + FontRenderer font = mc.fontRenderer; + for (String line : lines) { + int strWidth = font.getStringWidth(line); + int xOffset = (align == 1) ? (overlayWidth - strWidth) / 2 + : (align == 2) ? overlayWidth - strWidth - 5 : 5; + font.drawStringWithShadow(line, xOffset, y, color); + y += font.FONT_HEIGHT + 4; + } + return y; + } + + private void drawDecorativeLine(int y) { + drawHorizontalLine(5, overlayWidth - 5, y, 0xFF777777); + drawHorizontalLine(5, overlayWidth - 5, y + 1, 0xFFA8A8A8); + drawHorizontalLine(5, overlayWidth - 5, y + 2, 0xFFFFFFFF); + } + + // Simple wrappers for drawing. + private void drawRect(int left, int top, int right, int bottom, int color) { + net.minecraft.client.gui.Gui.drawRect(left, top, right, bottom, color); + } + + private void drawHorizontalLine(int left, int right, int y, int color) { + net.minecraft.client.gui.Gui.drawRect(left, y, right, y + 1, color); + } + + private void drawRectOutline(int left, int top, int right, int bottom, int color) { + drawHorizontalLine(left, right, top, color); + drawHorizontalLine(left, right, bottom - 1, color); + net.minecraft.client.gui.Gui.drawRect(left, top, left + 1, bottom, color); + net.minecraft.client.gui.Gui.drawRect(right - 1, top, right, bottom, color); + } + + private String convertColorCodes(String text) { + return text.replaceAll("&([0-9a-fk-or])", "§$1"); + } + + private ArrayList splitLines(String text, int maxWidth) { + ArrayList lines = new ArrayList<>(); + String[] words = text.split(" "); + StringBuilder current = new StringBuilder(); + FontRenderer font = mc.fontRenderer; + for (String word : words) { + String test = current.length() == 0 ? word : current.toString() + " " + word; + if (font.getStringWidth(test) > maxWidth) { + if (current.length() > 0) { + lines.add(current.toString()); + current = new StringBuilder(word); + } else { + lines.add(word); + } + } else { + if (current.length() > 0) + current.append(" ").append(word); + else + current.append(word); + } + } + if (current.length() > 0) + lines.add(current.toString()); + return lines; + } +} diff --git a/src/main/java/noppes/npcs/client/gui/player/inventory/GuiSettings.java b/src/main/java/noppes/npcs/client/gui/player/inventory/GuiSettings.java index 2cf7d1d09..4451ecb18 100644 --- a/src/main/java/noppes/npcs/client/gui/player/inventory/GuiSettings.java +++ b/src/main/java/noppes/npcs/client/gui/player/inventory/GuiSettings.java @@ -8,7 +8,7 @@ import net.minecraft.client.gui.GuiYesNoCallback; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector; import noppes.npcs.client.ClientCacheHandler; -import noppes.npcs.client.gui.hud.GuiOverlayQuestEditor; +import noppes.npcs.client.gui.hud.GuiHudEditor; import noppes.npcs.client.gui.util.GuiNpcButton; import noppes.npcs.client.gui.util.GuiNpcLabel; import noppes.npcs.client.gui.util.GuiNpcTextField; @@ -167,7 +167,7 @@ public class GuiSettings extends GuiCNPCInventory implements ITextfieldListener, ConfigClient.HideEffectsBarProperty.set(ConfigClient.HideEffectsBar); } if(button.id == 90){ - mc.displayGuiScreen(new GuiOverlayQuestEditor(this)); + mc.displayGuiScreen(new GuiHudEditor(this)); } ConfigClient.config.save();