Better Hud System CNPC+

This commit is contained in:
Kam committed 2025-02-23 04:58:41 -05:00
1 parent c023071ff0
commit fe9bb02aea
11 files changed
+575 -401

No files matched your search

@@ -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;
}
}
}
@@ -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<String, CacheHashMap.CachedObject<ImageData>> imageDataCache = new CacheHashMap<>((long) ConfigClient.CacheLife * 60 * 1000);
public static PlayerData playerData = new PlayerData();
public static OverlayQuestTracking questTrackingOverlay = null;
public static HashMap<Integer, OverlayCustom> customOverlays = new HashMap<>();
public static HashMap<UUID, HashMap<Integer, SkinOverlay>> skinOverlays = new HashMap<>();
public static HashMap<UUID, AnimationData> 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();
@@ -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);
// }
}
}
@@ -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<EnumHudComponent, HudComponent> 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<EnumHudComponent, HudComponent> 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);
}
}
}
@@ -0,0 +1,5 @@
package noppes.npcs.client.gui.hud;
public enum EnumHudComponent {
QuestTracker
}
@@ -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<GuiButton> 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);
}
}
@@ -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;
}
}
}
@@ -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<GuiButton> buttonList) {
// Default: do nothing.
}
/**
* Handles editor button actions specific to this HUD component.
*/
public void onEditorButtonPressed(GuiButton button) {
// Default: do nothing.
}
}
@@ -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<String> questTitleLines = new ArrayList<>();
private ArrayList<String> questCategoryLines = new ArrayList<>();
private ArrayList<String> objectiveLines = new ArrayList<>();
private ArrayList<String> 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<String> 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<String> 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<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;
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<String> splitLines(String text, int maxWidth) {
ArrayList<String> 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");
}
}
@@ -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<String> questTitleLines = new ArrayList<>();
private ArrayList<String> questCategoryLines = new ArrayList<>();
private ArrayList<String> objectiveLines = new ArrayList<>();
private ArrayList<String> 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<String> 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<String> 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<GuiButton> 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<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 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<String> splitLines(String text, int maxWidth) {
ArrayList<String> 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;
}
}
@@ -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();