aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/thatgravyboat/skyblockhud/core
diff options
context:
space:
mode:
authorThatGravyBoat <thatgravyboat@gmail.com>2021-07-06 15:10:29 -0230
committerThatGravyBoat <thatgravyboat@gmail.com>2021-07-06 15:10:29 -0230
commit6d8e1e5659f64a4f9ba86d6ab5bbc8e688faf22a (patch)
tree7451e53ceeae3c324d83a7faba83ce80005e6f23 /src/main/java/com/thatgravyboat/skyblockhud/core
downloadskyblockhud-6d8e1e5659f64a4f9ba86d6ab5bbc8e688faf22a.tar.gz
skyblockhud-6d8e1e5659f64a4f9ba86d6ab5bbc8e688faf22a.tar.bz2
skyblockhud-6d8e1e5659f64a4f9ba86d6ab5bbc8e688faf22a.zip
Initial Commit
Diffstat (limited to 'src/main/java/com/thatgravyboat/skyblockhud/core')
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/BackgroundBlur.java231
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/ChromaColour.java95
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GlScissorStack.java87
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiElement.java8
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementBoolean.java121
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementColour.java368
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementTextField.java534
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/GuiScreenElementWrapper.java35
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/Config.java8
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/Position.java196
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/Category.java15
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigAccordionId.java14
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorAccordion.java14
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorBoolean.java12
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorButton.java15
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorColour.java13
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDraggableList.java14
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDropdown.java16
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorSlider.java18
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorText.java13
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigOption.java17
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditor.java62
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorAccordion.java82
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorBoolean.java38
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorButton.java64
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorColour.java71
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDraggableList.java284
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDropdown.java152
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorSlider.java132
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorText.java84
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiPositionEditor.java179
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/config/struct/ConfigProcessor.java176
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/GuiElementSlider.java123
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/MiscUtils.java104
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/Splitters.java10
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/StringUtils.java39
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpUtils.java26
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingFloat.java68
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingInteger.java76
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/render/RenderUtils.java165
-rw-r--r--src/main/java/com/thatgravyboat/skyblockhud/core/util/render/TextRenderUtils.java215
41 files changed, 3994 insertions, 0 deletions
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/BackgroundBlur.java b/src/main/java/com/thatgravyboat/skyblockhud/core/BackgroundBlur.java
new file mode 100644
index 0000000..6782052
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/BackgroundBlur.java
@@ -0,0 +1,231 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.OpenGlHelper;
+import net.minecraft.client.shader.Framebuffer;
+import net.minecraft.client.shader.Shader;
+import net.minecraft.util.Matrix4f;
+import net.minecraftforge.client.event.EntityViewRenderEvent;
+import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import org.lwjgl.opengl.GL11;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class BackgroundBlur {
+
+ private static HashMap<Float, Framebuffer> blurOutput = new HashMap<>();
+ private static HashMap<Float, Long> lastBlurUse = new HashMap<>();
+ private static long lastBlur = 0;
+ private static HashSet<Float> requestedBlurs = new HashSet<>();
+
+ private static int fogColour = 0;
+ private static boolean registered = false;
+ public static void registerListener() {
+ if(!registered) {
+ registered = true;
+ MinecraftForge.EVENT_BUS.register(new BackgroundBlur());
+ }
+ }
+
+ private static boolean shouldBlur = true;
+
+ public static void markDirty() {
+ if(Minecraft.getMinecraft().theWorld != null) {
+ shouldBlur = true;
+ }
+ }
+
+ public static void processBlurs() {
+ if(shouldBlur) {
+ shouldBlur = false;
+
+ long currentTime = System.currentTimeMillis();
+
+ for(float blur : requestedBlurs) {
+ lastBlur = currentTime;
+ lastBlurUse.put(blur, currentTime);
+
+ int width = Minecraft.getMinecraft().displayWidth;
+ int height = Minecraft.getMinecraft().displayHeight;
+
+ Framebuffer output = blurOutput.computeIfAbsent(blur, k -> {
+ Framebuffer fb = new Framebuffer(width, height, false);
+ fb.setFramebufferFilter(GL11.GL_NEAREST);
+ return fb;
+ });
+
+ output.framebufferWidth = output.framebufferTextureWidth = width;
+ output.framebufferHeight = output.framebufferTextureHeight = height;
+
+ blurBackground(output, blur);
+ }
+
+ Set<Float> remove = new HashSet<>();
+ for(Map.Entry<Float, Long> entry : lastBlurUse.entrySet()) {
+ if(currentTime - entry.getValue() > 30*1000) {
+ remove.add(entry.getKey());
+ }
+ }
+ remove.remove(5f);
+
+ lastBlurUse.keySet().removeAll(remove);
+ blurOutput.keySet().removeAll(remove);
+
+ requestedBlurs.clear();
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ public void onScreenRender(RenderGameOverlayEvent.Pre event) {
+ if(event.type == RenderGameOverlayEvent.ElementType.ALL) {
+ processBlurs();
+ }
+ }
+
+ @SubscribeEvent
+ public void onFogColour(EntityViewRenderEvent.FogColors event) {
+ fogColour = 0xff000000;
+ fogColour |= ((int)(event.red*255) & 0xFF) << 16;
+ fogColour |= ((int)(event.green*255) & 0xFF) << 8;
+ fogColour |= (int)(event.blue*255) & 0xFF;
+ }
+
+ private static Shader blurShaderHorz = null;
+ private static Shader blurShaderVert = null;
+ private static Framebuffer blurOutputHorz = null;
+
+ /**
+ * Creates a projection matrix that projects from our coordinate space [0->width; 0->height] to OpenGL coordinate
+ * space [-1 -> 1; 1 -> -1] (Note: flipped y-axis).
+ *
+ * This is so that we can render to and from the framebuffer in a way that is familiar to us, instead of needing to
+ * apply scales and translations manually.
+ */
+ private static Matrix4f createProjectionMatrix(int width, int height) {
+ Matrix4f projMatrix = new Matrix4f();
+ projMatrix.setIdentity();
+ projMatrix.m00 = 2.0F / (float)width;
+ projMatrix.m11 = 2.0F / (float)(-height);
+ projMatrix.m22 = -0.0020001999F;
+ projMatrix.m33 = 1.0F;
+ projMatrix.m03 = -1.0F;
+ projMatrix.m13 = 1.0F;
+ projMatrix.m23 = -1.0001999F;
+ return projMatrix;
+ }
+
+ private static void blurBackground(Framebuffer output, float blurFactor) {
+ if(!OpenGlHelper.isFramebufferEnabled() || !OpenGlHelper.areShadersSupported()) return;
+
+ int width = Minecraft.getMinecraft().displayWidth;
+ int height = Minecraft.getMinecraft().displayHeight;
+
+ GlStateManager.matrixMode(GL11.GL_PROJECTION);
+ GlStateManager.loadIdentity();
+ GlStateManager.ortho(0.0D, width, height, 0.0D, 1000.0D, 3000.0D);
+ GlStateManager.matrixMode(GL11.GL_MODELVIEW);
+ GlStateManager.loadIdentity();
+ GlStateManager.translate(0.0F, 0.0F, -2000.0F);
+
+ if(blurOutputHorz == null) {
+ blurOutputHorz = new Framebuffer(width, height, false);
+ blurOutputHorz.setFramebufferFilter(GL11.GL_NEAREST);
+ }
+ if(blurOutputHorz == null || output == null) {
+ return;
+ }
+ if(blurOutputHorz.framebufferWidth != width || blurOutputHorz.framebufferHeight != height) {
+ blurOutputHorz.createBindFramebuffer(width, height);
+ blurShaderHorz.setProjectionMatrix(createProjectionMatrix(width, height));
+ Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(false);
+ }
+
+ try {
+ blurShaderHorz = new Shader(Minecraft.getMinecraft().getResourceManager(), "blur",
+ Minecraft.getMinecraft().getFramebuffer(), blurOutputHorz);
+ blurShaderHorz.getShaderManager().getShaderUniform("BlurDir").set(1, 0);
+ blurShaderHorz.setProjectionMatrix(createProjectionMatrix(width, height));
+ } catch(Exception ignored) { }
+ try {
+ blurShaderVert = new Shader(Minecraft.getMinecraft().getResourceManager(), "blur",
+ blurOutputHorz, output);
+ blurShaderVert.getShaderManager().getShaderUniform("BlurDir").set(0, 1);
+ blurShaderVert.setProjectionMatrix(createProjectionMatrix(width, height));
+ } catch(Exception ignored) { }
+ if(blurShaderHorz != null && blurShaderVert != null) {
+ if(blurShaderHorz.getShaderManager().getShaderUniform("Radius") == null) {
+ //Corrupted shader?
+ return;
+ }
+
+ blurShaderHorz.getShaderManager().getShaderUniform("Radius").set(blurFactor);
+ blurShaderVert.getShaderManager().getShaderUniform("Radius").set(blurFactor);
+
+ GL11.glPushMatrix();
+ /*GL30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, Minecraft.getMinecraft().getFramebuffer().framebufferObject);
+ GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, output.framebufferObject);
+ GL30.glBlitFramebuffer(0, 0, width, height,
+ 0, 0, output.framebufferWidth, output.framebufferHeight,
+ GL11.GL_COLOR_BUFFER_BIT, GL11.GL_NEAREST);*/
+
+ blurShaderHorz.loadShader(0);
+ blurShaderVert.loadShader(0);
+ GlStateManager.enableDepth();
+ GL11.glPopMatrix();
+
+ Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(false);
+ }
+ }
+
+ public static void renderBlurredBackground(float blurStrength, int screenWidth, int screenHeight,
+ int x, int y, int blurWidth, int blurHeight) {
+ renderBlurredBackground(blurStrength, screenWidth, screenHeight, x, y, blurWidth, blurHeight, false);
+ }
+
+ /**
+ * Renders a subsection of the blurred framebuffer on to the corresponding section of the screen.
+ * Essentially, this method will "blur" the background inside the bounds specified by [x->x+blurWidth, y->y+blurHeight]
+ */
+ public static void renderBlurredBackground(float blurStrength, int screenWidth, int screenHeight,
+ int x, int y, int blurWidth, int blurHeight, boolean forcedUpdate) {
+ if(!OpenGlHelper.isFramebufferEnabled() || !OpenGlHelper.areShadersSupported()) return;
+ if(blurStrength < 0.5) return;
+ requestedBlurs.add(blurStrength);
+
+ long currentTime = System.currentTimeMillis();
+ if(currentTime - lastBlur > 300) {
+ shouldBlur = true;
+ if(currentTime - lastBlur > 400 && forcedUpdate) return;
+ }
+
+ if(blurOutput.isEmpty()) return;
+
+ Framebuffer fb = blurOutput.get(blurStrength);
+ if(fb == null) {
+ fb = blurOutput.values().iterator().next();
+ }
+
+ float uMin = x/(float)screenWidth;
+ float uMax = (x+blurWidth)/(float)screenWidth;
+ float vMin = (screenHeight-y)/(float)screenHeight;
+ float vMax = (screenHeight-y-blurHeight)/(float)screenHeight;
+
+ GlStateManager.depthMask(false);
+ Gui.drawRect(x, y, x+blurWidth, y+blurHeight, fogColour);
+ fb.bindFramebufferTexture();
+ GlStateManager.color(1f, 1f, 1f, 1f);
+ RenderUtils.drawTexturedRect(x, y, blurWidth, blurHeight, uMin, uMax, vMin, vMax);
+ fb.unbindFramebufferTexture();
+ GlStateManager.depthMask(true);
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/ChromaColour.java b/src/main/java/com/thatgravyboat/skyblockhud/core/ChromaColour.java
new file mode 100644
index 0000000..b9f4e79
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/ChromaColour.java
@@ -0,0 +1,95 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import java.awt.*;
+
+public class ChromaColour {
+
+ public static String special(int chromaSpeed, int alpha, int rgb) {
+ return special(chromaSpeed, alpha, (rgb & 0xFF0000) >> 16, (rgb & 0x00FF00) >> 8, (rgb & 0x0000FF));
+ }
+
+ private static final int RADIX = 10;
+
+ public static String special(int chromaSpeed, int alpha, int r, int g, int b) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Integer.toString(chromaSpeed, RADIX)).append(":");
+ sb.append(Integer.toString(alpha, RADIX)).append(":");
+ sb.append(Integer.toString(r, RADIX)).append(":");
+ sb.append(Integer.toString(g, RADIX)).append(":");
+ sb.append(Integer.toString(b, RADIX));
+ return sb.toString();
+ }
+
+ private static int[] decompose(String csv) {
+ String[] split = csv.split(":");
+
+ int[] arr = new int[split.length];
+
+
+ for(int i=0; i<split.length; i++) {
+ arr[i] = Integer.parseInt(split[split.length-1-i], RADIX);
+ }
+ return arr;
+ }
+
+ public static int specialToSimpleRGB(String special) {
+ int[] d = decompose(special);
+ int r = d[2];
+ int g = d[1];
+ int b = d[0];
+ int a = d[3];
+ int chr = d[4];
+
+ return (a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF);
+ }
+
+ public static int getSpeed(String special) {
+ return decompose(special)[4];
+ }
+
+ public static float getSecondsForSpeed(int speed) {
+ return (255-speed)/254f*(MAX_CHROMA_SECS-MIN_CHROMA_SECS)+MIN_CHROMA_SECS;
+ }
+
+ private static final int MIN_CHROMA_SECS = 1;
+ private static final int MAX_CHROMA_SECS = 60;
+
+ public static long startTime = -1;
+ public static int specialToChromaRGB(String special) {
+ if(startTime < 0) startTime = System.currentTimeMillis();
+
+ int[] d = decompose(special);
+ int chr = d[4];
+ int a = d[3];
+ int r = d[2];
+ int g = d[1];
+ int b = d[0];
+
+ float[] hsv = Color.RGBtoHSB(r, g, b, null);
+
+ if(chr > 0) {
+ float seconds = getSecondsForSpeed(chr);
+ hsv[0] += (System.currentTimeMillis()-startTime)/1000f/seconds;
+ hsv[0] %= 1;
+ if(hsv[0] < 0) hsv[0] += 1;
+ }
+
+ return (a & 0xFF) << 24 | (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) & 0x00FFFFFF);
+ }
+
+ public static int rotateHue(int argb, int degrees) {
+ int a = (argb >> 24) & 0xFF;
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = (argb) & 0xFF;
+
+ float[] hsv = Color.RGBtoHSB(r, g, b, null);
+
+ hsv[0] += degrees/360f;
+ hsv[0] %= 1;
+
+ return (a & 0xFF) << 24 | (Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) & 0x00FFFFFF);
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GlScissorStack.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GlScissorStack.java
new file mode 100644
index 0000000..6f21e9a
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GlScissorStack.java
@@ -0,0 +1,87 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.ScaledResolution;
+import org.lwjgl.opengl.GL11;
+
+import java.util.LinkedList;
+
+public class GlScissorStack {
+
+ private static class Bounds {
+ int left;
+ int top;
+ int right;
+ int bottom;
+
+ public Bounds(int left, int top, int right, int bottom) {
+ this.left = left;
+ this.top = top;
+ this.right = right;
+ this.bottom = bottom;
+ }
+
+ public Bounds createSubBound(int left, int top, int right, int bottom) {
+ left = Math.max(left, this.left);
+ top = Math.max(top, this.top);
+ right = Math.min(right, this.right);
+ bottom = Math.min(bottom, this.bottom);
+
+ if(top > bottom) {
+ top = bottom;
+ }
+ if(left > right) {
+ left = right;
+ }
+
+ return new Bounds(left, top, right, bottom);
+ }
+
+ public void set(ScaledResolution scaledResolution) {
+ int height = Minecraft.getMinecraft().displayHeight;
+ int scale = scaledResolution.getScaleFactor();
+ GL11.glScissor(left*scale, height-bottom*scale, (right-left)*scale, (bottom-top)*scale);
+ }
+ }
+
+ private static final LinkedList<Bounds> boundsStack = new LinkedList<>();
+
+ public static void push(int left, int top, int right, int bottom, ScaledResolution scaledResolution) {
+ if(right < left) {
+ int temp = right;
+ right = left;
+ left = temp;
+ }
+ if(bottom < top) {
+ int temp = bottom;
+ bottom = top;
+ top = temp;
+ }
+ if(boundsStack.isEmpty()) {
+ boundsStack.push(new Bounds(left, top, right, bottom));
+ } else {
+ boundsStack.push(boundsStack.peek().createSubBound(left, top, right, bottom));
+ }
+ if(!boundsStack.isEmpty()) {
+ boundsStack.peek().set(scaledResolution);
+ }
+ GL11.glEnable(GL11.GL_SCISSOR_TEST);
+ }
+
+ public static void pop(ScaledResolution scaledResolution) {
+ if(!boundsStack.isEmpty()) {
+ boundsStack.pop();
+ }
+ if(boundsStack.isEmpty()) {
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+ } else {
+ boundsStack.peek().set(scaledResolution);
+ }
+ }
+
+ public static void clear() {
+ boundsStack.clear();
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElement.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElement.java
new file mode 100644
index 0000000..94d2375
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElement.java
@@ -0,0 +1,8 @@
+package com.thatgravyboat.skyblockhud.core;
+
+public abstract class GuiElement {
+
+ public abstract void render();
+ public abstract boolean mouseInput(int mouseX, int mouseY);
+ public abstract boolean keyboardInput();
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementBoolean.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementBoolean.java
new file mode 100644
index 0000000..d403bef
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementBoolean.java
@@ -0,0 +1,121 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import com.thatgravyboat.skyblockhud.core.util.lerp.LerpUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.GuiTextures;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.ResourceLocation;
+import org.lwjgl.input.Mouse;
+
+import java.util.function.Consumer;
+
+public class GuiElementBoolean extends GuiElement {
+
+ public int x;
+ public int y;
+ private boolean value;
+ private int clickRadius;
+ private Consumer<Boolean> toggleCallback;
+
+ private boolean previewValue;
+ private int animation = 0;
+ private long lastMillis = 0;
+
+ private static final int xSize = 48;
+ private static final int ySize = 14;
+
+ public GuiElementBoolean(int x, int y, boolean value, Consumer<Boolean> toggleCallback) {
+ this(x, y, value, 0, toggleCallback);
+ }
+
+ public GuiElementBoolean(int x, int y, boolean value, int clickRadius, Consumer<Boolean> toggleCallback) {
+ this.x = x;
+ this.y = y;
+ this.value = value;
+ this.previewValue = value;
+ this.clickRadius = clickRadius;
+ this.toggleCallback = toggleCallback;
+ this.lastMillis = System.currentTimeMillis();
+
+ if(value) animation = 36;
+ }
+
+ @Override
+ public void render() {
+ GlStateManager.color(1, 1, 1, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(GuiTextures.BAR);
+ RenderUtils.drawTexturedRect(x, y, xSize, ySize);
+
+ ResourceLocation buttonLoc = GuiTextures.ON;
+ long currentMillis = System.currentTimeMillis();
+ long deltaMillis = currentMillis - lastMillis;
+ lastMillis = currentMillis;
+ boolean passedLimit = false;
+ if(previewValue != value) {
+ if((previewValue && animation > 12) ||
+ (!previewValue && animation < 24)) {
+ passedLimit = true;
+ }
+ }
+ if(previewValue != passedLimit) {
+ animation += deltaMillis/10;
+ } else {
+ animation -= deltaMillis/10;
+ }
+ lastMillis -= deltaMillis%10;
+
+ if(previewValue == value) {
+ animation = Math.max(0, Math.min(36, animation));
+ } else if(!passedLimit) {
+ if(previewValue) {
+ animation = Math.max(0, Math.min(12, animation));
+ } else {
+ animation = Math.max(24, Math.min(36, animation));
+ }
+ } else {
+ if(previewValue) {
+ animation = Math.max(12, animation);
+ } else {
+ animation = Math.min(24, animation);
+ }
+ }
+
+ int animation = (int)(LerpUtils.sigmoidZeroOne(this.animation/36f)*36);
+ if(animation < 3) {
+ buttonLoc = GuiTextures.OFF;
+ } else if(animation < 13) {
+ buttonLoc = GuiTextures.ONE;
+ } else if(animation < 23) {
+ buttonLoc = GuiTextures.TWO;
+ } else if(animation < 33) {
+ buttonLoc = GuiTextures.THREE;
+ }
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(buttonLoc);
+ RenderUtils.drawTexturedRect(x+animation, y, 12, 14);
+ }
+
+ @Override
+ public boolean mouseInput(int mouseX, int mouseY) {
+ if(mouseX > x-clickRadius && mouseX < x+xSize+clickRadius &&
+ mouseY > y-clickRadius && mouseY < y+ySize+clickRadius) {
+ if(Mouse.getEventButton() == 0) {
+ if(Mouse.getEventButtonState()) {
+ previewValue = !value;
+ } else if(previewValue == !value) {
+ value = !value;
+ toggleCallback.accept(value);
+ }
+ }
+ } else {
+ previewValue = value;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementColour.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementColour.java
new file mode 100644
index 0000000..8774595
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementColour.java
@@ -0,0 +1,368 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.texture.DynamicTexture;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.function.Consumer;
+
+public class GuiElementColour extends GuiElement {
+
+ public static final ResourceLocation colour_selector_dot = new ResourceLocation("skyblockhud:core/colour_selector_dot.png");
+ public static final ResourceLocation colour_selector_bar = new ResourceLocation("skyblockhud:core/colour_selector_bar.png");
+ public static final ResourceLocation colour_selector_bar_alpha = new ResourceLocation("skyblockhud:core/colour_selector_bar_alpha.png");
+ public static final ResourceLocation colour_selector_chroma = new ResourceLocation("skyblockhud:core/colour_selector_chroma.png");
+
+ private static final ResourceLocation colourPickerLocation = new ResourceLocation("mbcore:dynamic/colourpicker");
+ private static final ResourceLocation colourPickerBarValueLocation = new ResourceLocation("mbcore:dynamic/colourpickervalue");
+ private static final ResourceLocation colourPickerBarOpacityLocation = new ResourceLocation("mbcore:dynamic/colourpickeropacity");
+ private final GuiElementTextField hexField = new GuiElementTextField("",
+ GuiElementTextField.SCALE_TEXT | GuiElementTextField.FORCE_CAPS | GuiElementTextField.NO_SPACE);
+
+ private int x;
+ private int y;
+ private final int xSize = 119;
+ private final int ySize = 89;
+
+ private float wheelAngle = 0;
+ private float wheelRadius = 0;
+
+ private int clickedComponent = -1;
+
+ private Consumer<String> colourChangedCallback;
+ private Runnable closeCallback;
+ private String colour;
+
+ public GuiElementColour(int x, int y, String initialColour, Consumer<String> colourChangedCallback,
+ Runnable closeCallback) {
+
+ final ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+
+ this.y = Math.max(10, Math.min(scaledResolution.getScaledHeight()-ySize-10, y));
+ this.x = Math.max(10, Math.min(scaledResolution.getScaledWidth()-xSize-10, x));
+
+ this.colour = initialColour;
+ this.colourChangedCallback = colourChangedCallback;
+ this.closeCallback = closeCallback;
+
+ int colour = ChromaColour.specialToSimpleRGB(initialColour);
+ Color c = new Color(colour);
+ float[] hsv = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+ updateAngleAndRadius(hsv);
+ }
+
+ public void updateAngleAndRadius(float[] hsv) {
+ this.wheelRadius = hsv[1];
+ this.wheelAngle = hsv[0]*360;
+ }
+
+ public void render() {
+ RenderUtils.drawFloatingRectDark(x, y, xSize, ySize);
+
+ int currentColour = ChromaColour.specialToSimpleRGB(colour);
+ Color c = new Color(currentColour, true);
+ float[] hsv = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+
+ BufferedImage bufferedImage = new BufferedImage(288, 288, BufferedImage.TYPE_INT_ARGB);
+ float borderRadius = 0.05f;
+ if(Keyboard.isKeyDown(Keyboard.KEY_N)) borderRadius = 0;
+ for(int x=-16; x<272; x++) {
+ for(int y=-16; y<272; y++) {
+ float radius = (float) Math.sqrt(((x-128)*(x-128)+(y-128)*(y-128))/16384f);
+ float angle = (float) Math.toDegrees(Math.atan((128-x)/(y-128+1E-5))+Math.PI/2);
+ if(y < 128) angle += 180;
+ if(radius <= 1) {
+ int rgb = Color.getHSBColor(angle/360f, (float)Math.pow(radius, 1.5f), hsv[2]).getRGB();
+ bufferedImage.setRGB(x+16, y+16, rgb);
+ } else if(radius <= 1+borderRadius) {
+ float invBlackAlpha = Math.abs(radius-1-borderRadius/2)/borderRadius*2;
+ float blackAlpha = 1-invBlackAlpha;
+
+ if(radius > 1+borderRadius/2) {
+ bufferedImage.setRGB(x+16, y+16, (int)(blackAlpha*255) << 24);
+ } else {
+ Color col = Color.getHSBColor(angle/360f, 1, hsv[2]);
+ int rgb = (int)(col.getRed()*invBlackAlpha) << 16 |
+ (int)(col.getGreen()*invBlackAlpha) << 8 |
+ (int)(col.getBlue()*invBlackAlpha);
+ bufferedImage.setRGB(x+16, y+16, 0xff000000 | rgb);
+ }
+
+ }
+ }
+ }
+
+ BufferedImage bufferedImageValue = new BufferedImage(10, 64, BufferedImage.TYPE_INT_ARGB);
+ for(int x=0; x<10; x++) {
+ for(int y=0; y<64; y++) {
+ if((x == 0 || x == 9) && (y == 0 || y == 63)) continue;
+
+ int rgb = Color.getHSBColor(wheelAngle/360, wheelRadius, (64-y)/64f).getRGB();
+ bufferedImageValue.setRGB(x, y, rgb);
+ }
+ }
+
+ BufferedImage bufferedImageOpacity = new BufferedImage(10, 64, BufferedImage.TYPE_INT_ARGB);
+ for(int x=0; x<10; x++) {
+ for(int y=0; y<64; y++) {
+ if((x == 0 || x == 9) && (y == 0 || y == 63)) continue;
+
+ int rgb = (currentColour & 0x00FFFFFF) | (Math.min(255, (64-y)*4) << 24);
+ bufferedImageOpacity.setRGB(x, y, rgb);
+ }
+ }
+
+ float selradius = (float)Math.pow(wheelRadius, 1/1.5f)*32;
+ int selx = (int)(Math.cos(Math.toRadians(wheelAngle))*selradius);
+ int sely = (int)(Math.sin(Math.toRadians(wheelAngle))*selradius);
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colour_selector_bar_alpha);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5, y+5, 10, 64, GL11.GL_NEAREST);
+
+ Minecraft.getMinecraft().getTextureManager().loadTexture(colourPickerBarValueLocation, new DynamicTexture(bufferedImageValue));
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colourPickerBarValueLocation);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+64+5, y+5, 10, 64, GL11.GL_NEAREST);
+
+ Minecraft.getMinecraft().getTextureManager().loadTexture(colourPickerBarOpacityLocation, new DynamicTexture(bufferedImageOpacity));
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colourPickerBarOpacityLocation);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5, y+5, 10, 64, GL11.GL_NEAREST);
+
+ int chromaSpeed = ChromaColour.getSpeed(colour);
+ int currentColourChroma = ChromaColour.specialToChromaRGB(colour);
+ Color cChroma = new Color(currentColourChroma, true);
+ float hsvChroma[] = Color.RGBtoHSB(cChroma.getRed(), cChroma.getGreen(), cChroma.getBlue(), null);
+
+ if(chromaSpeed > 0) {
+ Gui.drawRect(x+5+64+5+10+5+10+5+1, y+5+1,
+ x+5+64+5+10+5+10+5+10-1, y+5+64-1,
+ Color.HSBtoRGB(hsvChroma[0], 0.8f, 0.8f));
+ } else {
+ Gui.drawRect(x+5+64+5+10+5+10+5+1, y+5+27+1,
+ x+5+64+5+10+5+10+5+10-1, y+5+37-1,
+ Color.HSBtoRGB((hsvChroma[0]+(System.currentTimeMillis()-ChromaColour.startTime)/1000f)%1, 0.8f, 0.8f));
+ }
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colour_selector_bar);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+64+5, y+5, 10, 64, GL11.GL_NEAREST);
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5, y+5, 10, 64, GL11.GL_NEAREST);
+
+ if(chromaSpeed > 0) {
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5+10+5, y+5, 10, 64, GL11.GL_NEAREST);
+ } else {
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colour_selector_chroma);
+ RenderUtils.drawTexturedRect(x+5+64+5+10+5+10+5, y+5+27, 10, 10, GL11.GL_NEAREST);
+ }
+
+ Gui.drawRect(x+5+64+5, y+5+64-(int)(64*hsv[2]),
+ x+5+64+5+10, y+5+64-(int)(64*hsv[2])+1, 0xFF000000);
+ Gui.drawRect(x+5+64+5+10+5, y+5+64-c.getAlpha()/4,
+ x+5+64+5+10+5+10, y+5+64-c.getAlpha()/4-1, 0xFF000000);
+ if(chromaSpeed > 0) {
+ Gui.drawRect(x+5+64+5+10+5+10+5,
+ y+5+64-(int)(chromaSpeed/255f*64),
+ x+5+64+5+10+5+10+5+10,
+ y+5+64-(int)(chromaSpeed/255f*64)+1, 0xFF000000);
+ }
+
+ Minecraft.getMinecraft().getTextureManager().loadTexture(colourPickerLocation, new DynamicTexture(bufferedImage));
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colourPickerLocation);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+1, y+1, 72, 72, GL11.GL_LINEAR);
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(colour_selector_dot);
+ GlStateManager.color(1, 1, 1, 1);
+ RenderUtils.drawTexturedRect(x+5+32+selx-4, y+5+32+sely-4, 8, 8, GL11.GL_NEAREST);
+
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(EnumChatFormatting.GRAY.toString()+Math.round(hsv[2]*100)+"",
+ Minecraft.getMinecraft().fontRendererObj,
+ x+5+64+5+5-(Math.round(hsv[2]*100)==100?1:0), y+5+64+5+5, true, 13, -1);
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(EnumChatFormatting.GRAY.toString()+Math.round(c.getAlpha()/255f*100)+"",
+ Minecraft.getMinecraft().fontRendererObj,
+ x+5+64+5+15+5, y+5+64+5+5, true, 13, -1);
+ if(chromaSpeed > 0) {
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(EnumChatFormatting.GRAY.toString()+
+ (int)ChromaColour.getSecondsForSpeed(chromaSpeed)+"s",
+ Minecraft.getMinecraft().fontRendererObj,
+ x+5+64+5+30+6, y+5+64+5+5, true, 13, -1);
+ }
+
+ hexField.setSize(48, 10);
+ if(!hexField.getFocus()) hexField.setText(Integer.toHexString(c.getRGB() & 0xFFFFFF).toUpperCase());
+
+ StringBuilder sb = new StringBuilder(EnumChatFormatting.GRAY+"#");
+ for(int i=0; i<6-hexField.getText().length(); i++) {
+ sb.append("0");
+ }
+ sb.append(EnumChatFormatting.WHITE);
+
+ hexField.setPrependText(sb.toString());
+ hexField.render(x+5+8, y+5+64+5);
+ }
+
+ public boolean mouseInput(int mouseX, int mouseY) {
+ ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ float mouseXF = (float)(Mouse.getX() * scaledResolution.getScaledWidth_double() /
+ Minecraft.getMinecraft().displayWidth);
+ float mouseYF = (float)(scaledResolution.getScaledHeight_double() - Mouse.getY() *
+ scaledResolution.getScaledHeight_double() / Minecraft.getMinecraft().displayHeight - 1);
+
+ if((Mouse.getEventButton() == 0 || Mouse.getEventButton() == 1) && Mouse.getEventButtonState()) {
+ if(mouseX > x+5+8 && mouseX < x+5+8+48) {
+ if(mouseY > y+5+64+5 && mouseY < y+5+64+5+10) {
+ hexField.mouseClicked(mouseX, mouseY, Mouse.getEventButton());
+ clickedComponent = -1;
+ return true;
+ }
+ }
+ }
+ if(!Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
+ clickedComponent = -1;
+ }
+ if(Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
+ if(mouseX >= x && mouseX <= x+119 &&
+ mouseY >= y && mouseY <= y+89) {
+ hexField.unfocus();
+
+ int xWheel = mouseX - x - 5;
+ int yWheel = mouseY - y - 5;
+
+ if(xWheel > 0 && xWheel < 64) {
+ if(yWheel > 0 && yWheel < 64) {
+ clickedComponent = 0;
+ }
+ }
+
+ int xValue = mouseX - (x+5+64+5);
+ int y = mouseY - this.y - 5;
+
+ if(y > -5 && y <= 69) {
+ if(xValue > 0 && xValue < 10) {
+ clickedComponent = 1;
+ }
+
+ int xOpacity = mouseX - (x+5+64+5+10+5);
+
+ if(xOpacity > 0 && xOpacity < 10) {
+ clickedComponent = 2;
+ }
+ }
+
+ int chromaSpeed = ChromaColour.getSpeed(colour);
+ int xChroma = mouseX - (x+5+64+5+10+5+10+5);
+ if(xChroma > 0 && xChroma < 10) {
+ if(chromaSpeed > 0) {
+ if(y > -5 && y <= 69) {
+ clickedComponent = 3;
+ }
+ } else if(mouseY > this.y+5+27 && mouseY < this.y+5+37) {
+ int currentColour = ChromaColour.specialToSimpleRGB(colour);
+ Color c = new Color(currentColour, true);
+ colour = ChromaColour.special(200, c.getAlpha(), currentColour);
+ colourChangedCallback.accept(colour);
+ }
+ }
+ } else {
+ hexField.unfocus();
+ closeCallback.run();
+ return false;
+ }
+ }
+ if(Mouse.isButtonDown(0) && clickedComponent >= 0) {
+ int currentColour = ChromaColour.specialToSimpleRGB(colour);
+ Color c = new Color(currentColour, true);
+ float[] hsv = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+
+ float xWheel = mouseXF - x - 5;
+ float yWheel = mouseYF - y - 5;
+
+ if(clickedComponent == 0) {
+ float angle = (float) Math.toDegrees(Math.atan((32-xWheel)/(yWheel-32+1E-5))+Math.PI/2);
+ xWheel = Math.max(0, Math.min(64, xWheel));
+ yWheel = Math.max(0, Math.min(64, yWheel));
+ float radius = (float) Math.sqrt(((xWheel-32)*(xWheel-32)+(yWheel-32)*(yWheel-32))/1024f);
+ if(yWheel < 32) angle += 180;
+
+ this.wheelAngle = angle;
+ this.wheelRadius = (float)Math.pow(Math.min(1, radius), 1.5f);
+ int rgb = Color.getHSBColor(angle/360f, wheelRadius, hsv[2]).getRGB();
+ colour = ChromaColour.special(ChromaColour.getSpeed(colour), c.getAlpha(), rgb);
+ colourChangedCallback.accept(colour);
+ return true;
+ }
+
+ float y = mouseYF - this.y - 5;
+ y = Math.max(0, Math.min(64, y));
+ System.out.println(y);
+
+ if(clickedComponent == 1) {
+ int rgb = Color.getHSBColor(wheelAngle/360, wheelRadius, 1-y/64f).getRGB();
+ colour = ChromaColour.special(ChromaColour.getSpeed(colour), c.getAlpha(), rgb);
+ colourChangedCallback.accept(colour);
+ return true;
+ }
+
+ if(clickedComponent == 2) {
+ colour = ChromaColour.special(ChromaColour.getSpeed(colour),
+ 255-Math.round(y/64f*255), currentColour);
+ colourChangedCallback.accept(colour);
+ return true;
+ }
+
+ if(clickedComponent == 3) {
+ colour = ChromaColour.special(255-Math.round(y/64f*255), c.getAlpha(), currentColour);
+ colourChangedCallback.accept(colour);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean keyboardInput() {
+ if(Keyboard.getEventKeyState() && hexField.getFocus()) {
+ if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
+ hexField.unfocus();
+ return true;
+ }
+ String old = hexField.getText();
+
+ hexField.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey());
+
+ if(hexField.getText().length() > 6) {
+ hexField.setText(old);
+ } else {
+ try {
+ String text = hexField.getText().toLowerCase();
+
+ int rgb = Integer.parseInt(text, 16);
+ int alpha = (ChromaColour.specialToSimpleRGB(colour) >> 24) & 0xFF;
+ colour = ChromaColour.special(ChromaColour.getSpeed(colour), alpha, rgb);
+ colourChangedCallback.accept(colour);
+
+ Color c = new Color(rgb);
+ float[] hsv = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+ updateAngleAndRadius(hsv);
+ } catch(Exception e) {};
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementTextField.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementTextField.java
new file mode 100644
index 0000000..e2d6557
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiElementTextField.java
@@ -0,0 +1,534 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import com.thatgravyboat.skyblockhud.core.util.StringUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.GuiTextField;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+
+import java.awt.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class GuiElementTextField {
+
+ public static final int SCALE_TEXT = 0b100000;
+ public static final int NUM_ONLY = 0b10000;
+ public static final int NO_SPACE = 0b01000;
+ public static final int FORCE_CAPS = 0b00100;
+ public static final int COLOUR = 0b00010;
+ public static final int MULTILINE = 0b00001;
+
+ private int searchBarYSize;
+ private int searchBarXSize;
+ private static final int searchBarPadding = 2;
+
+ private int options;
+
+ private boolean focus = false;
+
+ private int x;
+ private int y;
+
+ private String prependText = "";
+
+ private final GuiTextField textField = new GuiTextField(0, Minecraft.getMinecraft().fontRendererObj,
+ 0 , 0, 0, 0);
+
+ private int customBorderColour = -1;
+
+ public GuiElementTextField(String initialText, int options) {
+ this(initialText, 100, 20, options);
+ }
+
+ public GuiElementTextField(String initialText, int sizeX, int sizeY, int options) {
+ textField.setFocused(true);
+ textField.setCanLoseFocus(false);
+ textField.setMaxStringLength(999);
+ textField.setText(initialText);
+ this.searchBarXSize = sizeX;
+ this.searchBarYSize = sizeY;
+ this.options = options;
+ }
+
+ public void setMaxStringLength(int len) {
+ textField.setMaxStringLength(len);
+ }
+
+ public void setCustomBorderColour(int colour) {
+ this.customBorderColour = colour;
+ }
+
+ public String getText() {
+ return textField.getText();
+ }
+
+ public void setPrependText(String text) {
+ this.prependText = text;
+ }
+
+ public void setText(String text) {
+ if(textField.getText() == null || !textField.getText().equals(text)) {
+ textField.setText(text);
+ }
+ }
+
+ public void setSize(int searchBarXSize, int searchBarYSize) {
+ this.searchBarXSize = searchBarXSize;
+ this.searchBarYSize = searchBarYSize;
+ }
+
+ public void setOptions(int options) {
+ this.options = options;
+ }
+
+ @Override
+ public String toString() {
+ return textField.getText();
+ }
+
+ public void setFocus(boolean focus) {
+ this.focus = focus;
+ }
+ public boolean getFocus() {
+ return focus;
+ }
+
+ public int getHeight() {
+ ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor();
+
+ int numLines = org.apache.commons.lang3.StringUtils.countMatches(textField.getText(), "\n")+1;
+ int extraSize = (searchBarYSize-8)/2+8;
+ int bottomTextBox = searchBarYSize + extraSize*(numLines-1);
+
+ return bottomTextBox + paddingUnscaled*2;
+ }
+
+ public int getWidth() {
+ ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor();
+
+ return searchBarXSize + paddingUnscaled*2;
+ }
+
+ private float getScaleFactor(String str) {
+ return Math.min(1, (searchBarXSize-2)/(float)Minecraft.getMinecraft().fontRendererObj.getStringWidth(str));
+ }
+
+ private boolean isScaling() {
+ return (options & SCALE_TEXT) != 0;
+ }
+
+ private float getStringWidth(String str) {
+ if(isScaling()) {
+ return Minecraft.getMinecraft().fontRendererObj.getStringWidth(str)*getScaleFactor(str);
+ } else {
+ return Minecraft.getMinecraft().fontRendererObj.getStringWidth(str);
+ }
+ }
+
+ public int getCursorPos(int mouseX, int mouseY) {
+ int xComp = mouseX - x;
+ int yComp = mouseY - y;
+
+ int extraSize = (searchBarYSize-8)/2+8;
+
+ String renderText = prependText + textField.getText();
+
+ int lineNum = Math.round(((yComp - (searchBarYSize-8)/2))/extraSize);
+
+ Pattern patternControlCode = Pattern.compile("(?i)\\u00A7([^\\u00B6])(?!\\u00B6)");
+ String text = renderText;
+ String textNoColour = renderText;
+ if((options & COLOUR) != 0) {
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(text);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ text = matcher.replaceFirst("\u00A7"+code+"\u00B6"+code);
+ }
+ }
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(textNoColour);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ textNoColour = matcher.replaceFirst("\u00B6"+code);
+ }
+
+ int currentLine = 0;
+ int cursorIndex = 0;
+ for(; cursorIndex<textNoColour.length(); cursorIndex++) {
+ if(currentLine == lineNum) break;
+ if(textNoColour.charAt(cursorIndex) == '\n') {
+ currentLine++;
+ }
+ }
+
+
+ String textNC = textNoColour.substring(0, cursorIndex);
+ int colorCodes = org.apache.commons.lang3.StringUtils.countMatches(textNC, "\u00B6");
+ String line = text.substring(cursorIndex+(((options & COLOUR) != 0)?colorCodes*2:0)).split("\n")[0];
+ int padding = Math.min(5, searchBarXSize-strLenNoColor(line))/2;
+ String trimmed = Minecraft.getMinecraft().fontRendererObj.trimStringToWidth(line, xComp-padding);
+ int linePos = strLenNoColor(trimmed);
+ if(linePos != strLenNoColor(line)) {
+ char after = line.charAt(linePos);
+ int trimmedWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(trimmed);
+ int charWidth = Minecraft.getMinecraft().fontRendererObj.getCharWidth(after);
+ if(trimmedWidth + charWidth/2 < xComp-padding) {
+ linePos++;
+ }
+ }
+ cursorIndex += linePos;
+
+ int pre = StringUtils.cleanColour(prependText).length();
+ if(cursorIndex < pre) {
+ cursorIndex = 0;
+ } else {
+ cursorIndex -= pre;
+ }
+
+ return cursorIndex;
+ }
+
+ public void mouseClicked(int mouseX, int mouseY, int mouseButton) {
+ if(mouseButton == 1) {
+ textField.setText("");
+ } else {
+ textField.setCursorPosition(getCursorPos(mouseX, mouseY));
+ }
+ focus = true;
+ }
+
+ public void unfocus() {
+ focus = false;
+ textField.setSelectionPos(textField.getCursorPosition());
+ }
+
+ public int strLenNoColor(String str) {
+ return str.replaceAll("(?i)\\u00A7.", "").length();
+ }
+
+ public void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
+ if(focus) {
+ textField.setSelectionPos(getCursorPos(mouseX, mouseY));
+ }
+ }
+
+ public void keyTyped(char typedChar, int keyCode) {
+ if(focus) {
+ if((options & MULTILINE) != 0) { //Carriage return
+ Pattern patternControlCode = Pattern.compile("(?i)\\u00A7([^\\u00B6\n])(?!\\u00B6)");
+
+ String text = textField.getText();
+ String textNoColour = textField.getText();
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(text);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ text = matcher.replaceFirst("\u00A7"+code+"\u00B6"+code);
+ }
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(textNoColour);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ textNoColour = matcher.replaceFirst("\u00B6"+code);
+ }
+
+ if(keyCode == 28) {
+ String before = textField.getText().substring(0, textField.getCursorPosition());
+ String after = textField.getText().substring(textField.getCursorPosition());
+ int pos = textField.getCursorPosition();
+ textField.setText(before + "\n" + after);
+ textField.setCursorPosition(pos+1);
+ return;
+ } else if(keyCode == 200) { //Up
+ String textNCBeforeCursor = textNoColour.substring(0, textField.getSelectionEnd());
+ int colorCodes = org.apache.commons.lang3.StringUtils.countMatches(textNCBeforeCursor, "\u00B6");
+ String textBeforeCursor = text.substring(0, textField.getSelectionEnd()+colorCodes*2);
+
+ int numLinesBeforeCursor = org.apache.commons.lang3.StringUtils.countMatches(textBeforeCursor, "\n");
+
+ String[] split = textBeforeCursor.split("\n");
+ int textBeforeCursorWidth;
+ String lineBefore;
+ String thisLineBeforeCursor;
+ if(split.length == numLinesBeforeCursor && split.length > 0) {
+ textBeforeCursorWidth = 0;
+ lineBefore = split[split.length-1];
+ thisLineBeforeCursor = "";
+ } else if(split.length > 1) {
+ thisLineBeforeCursor = split[split.length-1];
+ lineBefore = split[split.length-2];
+ textBeforeCursorWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(thisLineBeforeCursor);
+ } else {
+ return;
+ }
+ String trimmed = Minecraft.getMinecraft().fontRendererObj
+ .trimStringToWidth(lineBefore, textBeforeCursorWidth);
+ int linePos = strLenNoColor(trimmed);
+ if(linePos != strLenNoColor(lineBefore)) {
+ char after = lineBefore.charAt(linePos);
+ int trimmedWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(trimmed);
+ int charWidth = Minecraft.getMinecraft().fontRendererObj.getCharWidth(after);
+ if(trimmedWidth + charWidth/2 < textBeforeCursorWidth) {
+ linePos++;
+ }
+ }
+ int newPos = textField.getSelectionEnd()-strLenNoColor(thisLineBeforeCursor)
+ -strLenNoColor(lineBefore)-1+linePos;
+
+ if(GuiScreen.isShiftKeyDown()) {
+ textField.setSelectionPos(newPos);
+ } else {
+ textField.setCursorPosition(newPos);
+ }
+ } else if(keyCode == 208) { //Down
+ String textNCBeforeCursor = textNoColour.substring(0, textField.getSelectionEnd());
+ int colorCodes = org.apache.commons.lang3.StringUtils.countMatches(textNCBeforeCursor, "\u00B6");
+ String textBeforeCursor = text.substring(0, textField.getSelectionEnd()+colorCodes*2);
+
+ int numLinesBeforeCursor = org.apache.commons.lang3.StringUtils.countMatches(textBeforeCursor, "\n");
+
+ String[] split = textBeforeCursor.split("\n");
+ String thisLineBeforeCursor;
+ int textBeforeCursorWidth;
+ if(split.length == numLinesBeforeCursor) {
+ thisLineBeforeCursor = "";
+ textBeforeCursorWidth = 0;
+ } else if(split.length > 0) {
+ thisLineBeforeCursor = split[split.length-1];
+ textBeforeCursorWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(thisLineBeforeCursor);
+ } else {
+ return;
+ }
+
+ String[] split2 = textNoColour.split("\n");
+ if(split2.length > numLinesBeforeCursor+1) {
+ String lineAfter = split2[numLinesBeforeCursor+1];
+ String trimmed = Minecraft.getMinecraft().fontRendererObj
+ .trimStringToWidth(lineAfter, textBeforeCursorWidth);
+ int linePos = strLenNoColor(trimmed);
+ if(linePos != strLenNoColor(lineAfter)) {
+ char after = lineAfter.charAt(linePos);
+ int trimmedWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(trimmed);
+ int charWidth = Minecraft.getMinecraft().fontRendererObj.getCharWidth(after);
+ if(trimmedWidth + charWidth/2 < textBeforeCursorWidth) {
+ linePos++;
+ }
+ }
+ int newPos = textField.getSelectionEnd()-strLenNoColor(thisLineBeforeCursor)
+ +strLenNoColor(split2[numLinesBeforeCursor])+1+linePos;
+
+ if(GuiScreen.isShiftKeyDown()) {
+ textField.setSelectionPos(newPos);
+ } else {
+ textField.setCursorPosition(newPos);
+ }
+ }
+ }
+ }
+
+ String old = textField.getText();
+ if((options & FORCE_CAPS) != 0) typedChar = Character.toUpperCase(typedChar);
+ if((options & NO_SPACE) != 0 && typedChar == ' ') return;
+
+ textField.setFocused(true);
+ textField.textboxKeyTyped(typedChar, keyCode);
+
+ if((options & COLOUR) != 0) {
+ if(typedChar == '&') {
+ int pos = textField.getCursorPosition()-2;
+ if(pos >= 0 && pos < textField.getText().length()) {
+ if(textField.getText().charAt(pos) == '&') {
+ String before = textField.getText().substring(0, pos);
+ String after = "";
+ if(pos+2 < textField.getText().length()) {
+ after = textField.getText().substring(pos+2);
+ }
+ textField.setText(before + "\u00A7" + after);
+ textField.setCursorPosition(pos+1);
+ }
+ }
+ }
+ }
+
+ if((options & NUM_ONLY) != 0 && textField.getText().matches("[^0-9.]")) textField.setText(old);
+ }
+ }
+
+ public void render(int x, int y) {
+ this.x = x;
+ this.y = y;
+ drawTextbox(x, y, searchBarXSize, searchBarYSize, searchBarPadding, textField, focus);
+ }
+
+ private void drawTextbox(int x, int y, int searchBarXSize, int searchBarYSize, int searchBarPadding,
+ GuiTextField textField, boolean focus) {
+ ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ String renderText = prependText + textField.getText();
+
+ GlStateManager.disableLighting();
+
+ /**
+ * Search bar
+ */
+ int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor();
+ if(paddingUnscaled < 1) paddingUnscaled = 1;
+
+ int numLines = org.apache.commons.lang3.StringUtils.countMatches(renderText, "\n")+1;
+ int extraSize = (searchBarYSize-8)/2+8;
+ int bottomTextBox = y + searchBarYSize + extraSize*(numLines-1);
+
+ int borderColour = focus ? Color.GREEN.getRGB() : Color.WHITE.getRGB();
+ if(customBorderColour != -1) {
+ borderColour = customBorderColour;
+ }
+ //bar background
+ Gui.drawRect(x - paddingUnscaled,
+ y - paddingUnscaled,
+ x + searchBarXSize + paddingUnscaled,
+ bottomTextBox + paddingUnscaled, borderColour);
+ Gui.drawRect(x,
+ y,
+ x + searchBarXSize,
+ bottomTextBox, Color.BLACK.getRGB());
+
+ //bar text
+ Pattern patternControlCode = Pattern.compile("(?i)\\u00A7([^\\u00B6\n])(?!\\u00B6)");
+
+ String text = renderText;
+ String textNoColor = renderText;
+ if((options & COLOUR) != 0) {
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(text);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ text = matcher.replaceFirst("\u00A7"+code+"\u00B6"+code);
+ }
+ }
+ while(true) {
+ Matcher matcher = patternControlCode.matcher(textNoColor);
+ if(!matcher.find() || matcher.groupCount() < 1) break;
+ String code = matcher.group(1);
+ textNoColor = matcher.replaceFirst("\u00B6"+code);
+ }
+
+ int xStartOffset = 5;
+ float scale = 1;
+ String[] texts = text.split("\n");
+ for(int yOffI = 0; yOffI < texts.length; yOffI++) {
+ int yOff = yOffI*extraSize;
+
+ if(isScaling() && Minecraft.getMinecraft().fontRendererObj.getStringWidth(texts[yOffI])>searchBarXSize-10) {
+ scale = (searchBarXSize-2)/(float)Minecraft.getMinecraft().fontRendererObj.getStringWidth(texts[yOffI]);
+ if(scale > 1) scale=1;
+ float newLen = Minecraft.getMinecraft().fontRendererObj.getStringWidth(texts[yOffI])*scale;
+ xStartOffset = (int)((searchBarXSize-newLen)/2f);
+
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(texts[yOffI], Minecraft.getMinecraft().fontRendererObj, x+searchBarXSize/2f,
+ y+searchBarYSize/2f+yOff, false,
+ searchBarXSize-2, Color.WHITE.getRGB());
+ } else {
+ Minecraft.getMinecraft().fontRendererObj.drawString(StringUtils.trimToWidth(texts[yOffI], searchBarXSize-10), x + 5,
+ y+(searchBarYSize-8)/2+yOff, Color.WHITE.getRGB());
+ }
+ }
+
+ if(focus && System.currentTimeMillis()%1000>500) {
+ String textNCBeforeCursor = textNoColor.substring(0, textField.getCursorPosition()+prependText.length());
+ int colorCodes = org.apache.commons.lang3.StringUtils.countMatches(textNCBeforeCursor, "\u00B6");
+ String textBeforeCursor = text.substring(0, textField.getCursorPosition()+prependText.length()+(((options & COLOUR) != 0) ? colorCodes*2 : 0));
+
+ int numLinesBeforeCursor = org.apache.commons.lang3.StringUtils.countMatches(textBeforeCursor, "\n");
+ int yOff = numLinesBeforeCursor*extraSize;
+
+ String[] split = textBeforeCursor.split("\n");
+ int textBeforeCursorWidth;
+ if(split.length <= numLinesBeforeCursor || split.length == 0) {
+ textBeforeCursorWidth = 0;
+ } else {
+ textBeforeCursorWidth = (int)(Minecraft.getMinecraft().fontRendererObj.getStringWidth(split[split.length-1])*scale);
+ }
+ Gui.drawRect(x + xStartOffset + textBeforeCursorWidth,
+ y+(searchBarYSize-8)/2-1 + yOff,
+ x + xStartOffset + textBeforeCursorWidth+1,
+ y+(searchBarYSize-8)/2+9 + yOff, Color.WHITE.getRGB());
+ }
+
+ String selectedText = textField.getSelectedText();
+ if(!selectedText.isEmpty()) {
+ System.out.println("Start");
+ int leftIndex = Math.min(textField.getCursorPosition()+prependText.length(), textField.getSelectionEnd()+prependText.length());
+ int rightIndex = Math.max(textField.getCursorPosition()+prependText.length(), textField.getSelectionEnd()+prependText.length());
+
+ float texX = 0;
+ int texY = 0;
+ boolean sectionSignPrev = false;
+ boolean ignoreNext = false;
+ boolean bold = false;
+ for(int i=0; i<textNoColor.length(); i++) {
+ if(ignoreNext) {
+ ignoreNext = false;
+ continue;
+ }
+
+ char c = textNoColor.charAt(i);
+ if(sectionSignPrev) {
+ if(c != 'k' && c != 'K'
+ && c != 'm' && c != 'M'
+ && c != 'n' && c != 'N'
+ && c != 'o' && c != 'O') {
+ bold = c == 'l' || c == 'L';
+ }
+ sectionSignPrev = false;
+ if(i < prependText.length()) continue;
+ }
+ if(c == '\u00B6') {
+ sectionSignPrev = true;
+ if(i < prependText.length()) continue;
+ }
+
+ if(c == '\n') {
+ if(i >= leftIndex && i < rightIndex) {
+ Gui.drawRect(x + xStartOffset + (int)texX,
+ y+(searchBarYSize-8)/2-1 + texY,
+ x + xStartOffset + (int)texX + 3,
+ y+(searchBarYSize-8)/2+9 + texY, Color.LIGHT_GRAY.getRGB());
+ }
+
+ texX = 0;
+ texY += extraSize;
+ continue;
+ }
+
+ //String c2 = bold ? EnumChatFormatting.BOLD.toString() : "" + c;
+
+ System.out.println("Adding len for char:"+c+":"+Integer.toHexString(c));
+ int len = Minecraft.getMinecraft().fontRendererObj.getStringWidth(String.valueOf(c));
+ if(bold) len++;
+ if(i >= leftIndex && i < rightIndex) {
+ Gui.drawRect(x + xStartOffset + (int)texX,
+ y+(searchBarYSize-8)/2-1 + texY,
+ x + xStartOffset + (int)(texX + len*scale),
+ y+(searchBarYSize-8)/2+9 + texY, Color.LIGHT_GRAY.getRGB());
+
+ TextRenderUtils.drawStringScaled(String.valueOf(c), Minecraft.getMinecraft().fontRendererObj,
+ x + xStartOffset + texX,
+ y+searchBarYSize/2f-scale*8/2f + texY, false, Color.BLACK.getRGB(), scale);
+ if(bold) {
+ TextRenderUtils.drawStringScaled(String.valueOf(c), Minecraft.getMinecraft().fontRendererObj,
+ x + xStartOffset + texX + 1,
+ y+searchBarYSize/2f-scale*8/2f + texY, false, Color.BLACK.getRGB(), scale);
+ }
+ }
+
+ texX += len*scale;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/GuiScreenElementWrapper.java b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiScreenElementWrapper.java
new file mode 100644
index 0000000..234cc2d
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/GuiScreenElementWrapper.java
@@ -0,0 +1,35 @@
+package com.thatgravyboat.skyblockhud.core;
+
+import net.minecraft.client.gui.GuiScreen;
+import org.lwjgl.input.Mouse;
+
+import java.io.IOException;
+
+public class GuiScreenElementWrapper extends GuiScreen {
+
+ public final GuiElement element;
+
+ public GuiScreenElementWrapper(GuiElement element) {
+ this.element = element;
+ }
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ super.drawScreen(mouseX, mouseY, partialTicks);
+ element.render();
+ }
+
+ @Override
+ public void handleMouseInput() throws IOException {
+ super.handleMouseInput();
+ int i = Mouse.getEventX() * this.width / this.mc.displayWidth;
+ int j = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1;
+ element.mouseInput(i, j);
+ }
+
+ @Override
+ public void handleKeyboardInput() throws IOException {
+ super.handleKeyboardInput();
+ element.keyboardInput();
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/Config.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/Config.java
new file mode 100644
index 0000000..40f8c0e
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/Config.java
@@ -0,0 +1,8 @@
+package com.thatgravyboat.skyblockhud.core.config;
+
+public class Config {
+
+ public void executeRunnable(String runnableId) {
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/Position.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/Position.java
new file mode 100644
index 0000000..1c2de86
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/Position.java
@@ -0,0 +1,196 @@
+package com.thatgravyboat.skyblockhud.core.config;
+
+import com.google.gson.annotations.Expose;
+import net.minecraft.client.gui.ScaledResolution;
+
+public class Position {
+
+ @Expose
+ private int x;
+ @Expose
+ private int y;
+ @Expose
+ private boolean centerX;
+ @Expose
+ private boolean centerY;
+
+
+ private static final int EDGE_OFFSET = 0;
+
+ public Position(int x, int y) {
+ this(x, y, false, false);
+ }
+
+ public Position(int x, int y, boolean centerX, boolean centerY) {
+ this.x = x;
+ this.y = y;
+ this.centerX = centerX;
+ this.centerY = centerY;
+ }
+
+ public void set(Position other) {
+ this.x = other.x;
+ this.y = other.y;
+ this.centerX = other.centerX;
+ this.centerY = other.centerY;
+ }
+
+ public Position clone() {
+ return new Position(x, y, centerX, centerY);
+ }
+
+ public boolean isCenterX() {
+ return centerX;
+ }
+
+ public boolean isCenterY() {
+ return centerY;
+ }
+
+ public int getRawX() {
+ return x;
+ }
+
+ public int getRawY() {
+ return y;
+ }
+
+ public int getAbsX(ScaledResolution scaledResolution, int objWidth) {
+ int width = scaledResolution.getScaledWidth();
+
+ if(centerX) {
+ return width/2 + x;
+ }
+
+ int ret = x;
+ if(x < 0) {
+ ret = width + x - objWidth;
+ }
+
+ if(ret < 0) ret = 0;
+ if(ret > width - objWidth) ret = width - objWidth;
+
+ return ret;
+ }
+
+ public int getAbsY(ScaledResolution scaledResolution, int objHeight) {
+ int height = scaledResolution.getScaledHeight();
+
+ if(centerY) {
+ return height/2 + y;
+ }
+
+ int ret = y;
+ if(y < 0) {
+ ret = height + y - objHeight;
+ }
+
+ if(ret < 0) ret = 0;
+ if(ret > height - objHeight) ret = height - objHeight;
+
+ return ret;
+ }
+
+ public int moveX(int deltaX, int objWidth, ScaledResolution scaledResolution) {
+ int screenWidth = scaledResolution.getScaledWidth();
+ boolean wasPositiveX = this.x >= 0;
+ this.x += deltaX;
+
+ if(centerX) {
+ if(wasPositiveX) {
+ if(this.x > screenWidth/2-objWidth/2) {
+ deltaX += screenWidth/2-objWidth/2-this.x;
+ this.x = screenWidth/2-objWidth/2;
+ }
+ } else {
+ if(this.x < -screenWidth/2+objWidth/2) {
+ deltaX += -screenWidth/2+objWidth/2-this.x;
+ this.x = -screenWidth/2+objWidth/2;
+ }
+ }
+ return deltaX;
+ }
+
+ if(wasPositiveX) {
+ if(this.x < EDGE_OFFSET) {
+ deltaX += EDGE_OFFSET-this.x;
+ this.x = EDGE_OFFSET;
+ }
+ if(this.x > screenWidth-EDGE_OFFSET) {
+ deltaX += screenWidth-EDGE_OFFSET-this.x;
+ this.x = screenWidth-EDGE_OFFSET;
+ }
+ } else {
+ if(this.x+1 > -EDGE_OFFSET) {
+ deltaX += -EDGE_OFFSET-1-this.x;
+ this.x = -EDGE_OFFSET-1;
+ }
+ if(this.x+screenWidth < EDGE_OFFSET) {
+ deltaX += EDGE_OFFSET-screenWidth-this.x;
+ this.x = EDGE_OFFSET-screenWidth;
+ }
+ }
+
+ if(this.x >= 0 && this.x+objWidth/2 > screenWidth/2) {
+ this.x -= screenWidth - objWidth;
+ }
+ if(this.x < 0 && this.x+objWidth/2 <= -screenWidth/2) {
+ this.x += screenWidth - objWidth;
+ }
+ return deltaX;
+ }
+
+ public int moveY(int deltaY, int objHeight, ScaledResolution scaledResolution) {
+ int screenHeight = scaledResolution.getScaledHeight();
+ boolean wasPositiveY = this.y >= 0;
+ this.y += deltaY;
+
+ if(centerY) {
+ if(wasPositiveY) {
+ if(this.y > screenHeight/2-objHeight/2) {
+ deltaY += screenHeight/2-objHeight/2-this.y;
+ this.y = screenHeight/2-objHeight/2;
+ }
+ } else {
+ if(this.y < -screenHeight/2+objHeight/2) {
+ deltaY += -screenHeight/2+objHeight/2-this.y;
+ this.y = -screenHeight/2+objHeight/2;
+ }
+ }
+ return deltaY;
+ }
+
+ if(wasPositiveY) {
+ if(this.y < EDGE_OFFSET) {
+ deltaY += EDGE_OFFSET-this.y;
+ this.y = EDGE_OFFSET;
+ }
+ if(this.y > screenHeight-EDGE_OFFSET) {
+ deltaY += screenHeight-EDGE_OFFSET-this.y;
+ this.y = screenHeight-EDGE_OFFSET;
+ }
+ } else {
+ if(this.y+1 > -EDGE_OFFSET) {
+ deltaY += -EDGE_OFFSET-1-this.y;
+ this.y = -EDGE_OFFSET-1;
+ }
+ if(this.y+screenHeight < EDGE_OFFSET) {
+ deltaY += EDGE_OFFSET-screenHeight-this.y;
+ this.y = EDGE_OFFSET-screenHeight;
+ }
+ }
+
+ if(this.y >= 0 && this.y-objHeight/2 > screenHeight/2) {
+ this.y -= screenHeight - objHeight;
+ }
+ if(this.y < 0 && this.y-objHeight/2 <= -screenHeight/2) {
+ this.y += screenHeight - objHeight;
+ }
+ return deltaY;
+ }
+
+ public boolean rightAligned(ScaledResolution scaledResolution, int objWidth){
+ return this.getAbsX(scaledResolution, objWidth) > (scaledResolution.getScaledWidth() / 2);
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/Category.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/Category.java
new file mode 100644
index 0000000..e1d51ac
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/Category.java
@@ -0,0 +1,15 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Category {
+
+ String name();
+ String desc();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigAccordionId.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigAccordionId.java
new file mode 100644
index 0000000..8e2836f
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigAccordionId.java
@@ -0,0 +1,14 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigAccordionId {
+
+ int id();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorAccordion.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorAccordion.java
new file mode 100644
index 0000000..acb60af
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorAccordion.java
@@ -0,0 +1,14 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorAccordion {
+
+ int id();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorBoolean.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorBoolean.java
new file mode 100644
index 0000000..b1846d3
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorBoolean.java
@@ -0,0 +1,12 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorBoolean {
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorButton.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorButton.java
new file mode 100644
index 0000000..32425d1
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorButton.java
@@ -0,0 +1,15 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorButton {
+
+ String runnableId();
+ String buttonText() default "";
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorColour.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorColour.java
new file mode 100644
index 0000000..1e5713c
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorColour.java
@@ -0,0 +1,13 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorColour {
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDraggableList.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDraggableList.java
new file mode 100644
index 0000000..963c305
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDraggableList.java
@@ -0,0 +1,14 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorDraggableList {
+
+ String[] exampleText();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDropdown.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDropdown.java
new file mode 100644
index 0000000..5e48e25
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorDropdown.java
@@ -0,0 +1,16 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorDropdown {
+
+ String[] values();
+ int initialIndex() default 0;
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorSlider.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorSlider.java
new file mode 100644
index 0000000..a0fea03
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorSlider.java
@@ -0,0 +1,18 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorSlider {
+
+ float minValue();
+ float maxValue();
+
+ float minStep();
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorText.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorText.java
new file mode 100644
index 0000000..0c00561
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigEditorText.java
@@ -0,0 +1,13 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigEditorText {
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigOption.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigOption.java
new file mode 100644
index 0000000..845d01f
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/annotations/ConfigOption.java
@@ -0,0 +1,17 @@
+package com.thatgravyboat.skyblockhud.core.config.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ConfigOption {
+
+ String name();
+ String desc();
+ int subcategoryId() default -1;
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditor.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditor.java
new file mode 100644
index 0000000..e978420
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditor.java
@@ -0,0 +1,62 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.GlStateManager;
+
+public abstract class GuiOptionEditor {
+
+ protected final ConfigProcessor.ProcessedOption option;
+ private static final int HEIGHT = 45;
+
+ public GuiOptionEditor(ConfigProcessor.ProcessedOption option) {
+ this.option = option;
+ }
+
+ public void render(int x, int y, int width) {
+ int height = getHeight();
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ RenderUtils.drawFloatingRectDark(x, y, width, height, true);
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(option.name,
+ fr, x+width/6f, y+13, true, width/3-10, 0xc0c0c0);
+
+ int maxLines = 5;
+ float scale = 1;
+ int lineCount = fr.listFormattedStringToWidth(option.desc, width*2/3-10).size();
+
+ if(lineCount <= 0) return;
+
+ float paraHeight = 9 * lineCount - 1;
+
+ while(paraHeight >= HEIGHT-10) {
+ scale -= 1/8f;
+ lineCount = fr.listFormattedStringToWidth(option.desc, (int)(width*2/3/scale-10)).size();
+ paraHeight = (int)(9*scale * lineCount - 1*scale);
+ }
+
+ GlStateManager.pushMatrix();
+ GlStateManager.translate(x+5+width/3f, y+HEIGHT/2f-paraHeight/2, 0);
+ GlStateManager.scale(scale, scale, 1);
+
+ fr.drawSplitString(option.desc, 0, 0, (int)(width*2/3/scale-10), 0xc0c0c0);
+
+ GlStateManager.popMatrix();
+ }
+
+ public int getHeight() {
+ return HEIGHT;
+ }
+
+ public abstract boolean mouseInput(int x, int y, int width, int mouseX, int mouseY);
+ public abstract boolean keyboardInput();
+
+ public boolean mouseInputOverlay(int x, int y, int width, int mouseX, int mouseY) {
+ return false;
+ }
+ public void renderOverlay(int x, int y, int width) {}
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorAccordion.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorAccordion.java
new file mode 100644
index 0000000..c2b129d
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorAccordion.java
@@ -0,0 +1,82 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+public class GuiOptionEditorAccordion extends GuiOptionEditor {
+
+ private int accordionId;
+ private boolean accordionToggled;
+
+ public GuiOptionEditorAccordion(ConfigProcessor.ProcessedOption option, int accordionId) {
+ super(option);
+ this.accordionToggled = (boolean) option.get();
+ this.accordionId = accordionId;
+ }
+
+ @Override
+ public int getHeight() {
+ return 20;
+ }
+
+ public int getAccordionId() {
+ return accordionId;
+ }
+
+ public boolean getToggled() {
+ return accordionToggled;
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ int height = getHeight();
+ RenderUtils.drawFloatingRectDark(x, y, width, height, true);
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ GlStateManager.enableBlend();
+ GlStateManager.disableTexture2D();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.color(1, 1, 1, 1);
+ worldrenderer.begin(GL11.GL_TRIANGLES, DefaultVertexFormats.POSITION);
+ if(accordionToggled) {
+ worldrenderer.pos((double)x+6, (double)y+6, 0.0D).endVertex();
+ worldrenderer.pos((double)x+9.75f, (double)y+13.5f, 0.0D).endVertex();
+ worldrenderer.pos((double)x+13.5f, (double)y+6, 0.0D).endVertex();
+ } else {
+ worldrenderer.pos((double)x+6, (double)y+13.5f, 0.0D).endVertex();
+ worldrenderer.pos((double)x+13.5f, (double)y+9.75f, 0.0D).endVertex();
+ worldrenderer.pos((double)x+6, (double)y+6, 0.0D).endVertex();
+ }
+ tessellator.draw();
+ GlStateManager.enableTexture2D();
+ GlStateManager.disableBlend();
+
+ TextRenderUtils.drawStringScaledMaxWidth(option.name, Minecraft.getMinecraft().fontRendererObj,
+ x+18, y+6, false, width-10, 0xc0c0c0);
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ if(Mouse.getEventButtonState() && mouseX > x && mouseX < x+ width &&
+ mouseY > y && mouseY < y+getHeight()) {
+ accordionToggled = !accordionToggled;
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorBoolean.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorBoolean.java
new file mode 100644
index 0000000..3ea70c0
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorBoolean.java
@@ -0,0 +1,38 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.GuiElementBoolean;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+
+public class GuiOptionEditorBoolean extends GuiOptionEditor {
+
+ private final GuiElementBoolean bool;
+
+ public GuiOptionEditorBoolean(ConfigProcessor.ProcessedOption option) {
+ super(option);
+
+ bool = new GuiElementBoolean(0, 0, (boolean)option.get(), 10, option::set);
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+ int height = getHeight();
+
+ bool.x = x+width/6-24;
+ bool.y = y+height-7-14;
+ bool.render();
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+ bool.x = x+width/6-24;
+ bool.y = y+height-7-14;
+ return bool.mouseInput(mouseX, mouseY);
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorButton.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorButton.java
new file mode 100644
index 0000000..752b4bf
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorButton.java
@@ -0,0 +1,64 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.ChromaColour;
+import com.thatgravyboat.skyblockhud.core.config.Config;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Mouse;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.button_tex;
+
+public class GuiOptionEditorButton extends GuiOptionEditor {
+
+ private final String runnableId;
+ private String buttonText;
+ private Config config;
+
+ public GuiOptionEditorButton(ConfigProcessor.ProcessedOption option, String runnableId, String buttonText, Config config) {
+ super(option);
+ this.runnableId = runnableId;
+ this.config = config;
+
+ this.buttonText = buttonText;
+ if(this.buttonText != null && this.buttonText.isEmpty()) this.buttonText = null;
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+
+ int height = getHeight();
+
+ GlStateManager.color(1, 1, 1, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(button_tex);
+ RenderUtils.drawTexturedRect(x+width/6-24, y+height-7-14, 48, 16);
+
+ if(buttonText != null) {
+ TextRenderUtils.drawStringCenteredScaledMaxWidth(buttonText, Minecraft.getMinecraft().fontRendererObj,
+ x+width/6, y+height-7-6,
+ false, 44, 0xFF303030);
+ }
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ if(Mouse.getEventButtonState()) {
+ int height = getHeight();
+ if(mouseX > x+width/6-24 && mouseX < x+width/6+24 &&
+ mouseY > y+height-7-14 && mouseY < y+height-7+2) {
+ config.executeRunnable(runnableId);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorColour.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorColour.java
new file mode 100644
index 0000000..1f03954
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorColour.java
@@ -0,0 +1,71 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.ChromaColour;
+import com.thatgravyboat.skyblockhud.core.GuiElementColour;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Mouse;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.*;
+
+public class GuiOptionEditorColour extends GuiOptionEditor {
+
+ private String chromaColour;
+ private GuiElementColour colourElement = null;
+
+ public GuiOptionEditorColour(ConfigProcessor.ProcessedOption option) {
+ super(option);
+
+ this.chromaColour = (String)option.get();
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+ int height = getHeight();
+
+ int argb = ChromaColour.specialToChromaRGB(chromaColour);
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = argb & 0xFF;
+ GlStateManager.color(r/255f, g/255f, b/255f, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(button_white);
+ RenderUtils.drawTexturedRect(x+width/6f-24, y+height-7-14, 48, 16);
+ }
+
+ @Override
+ public void renderOverlay(int x, int y, int width) {
+ if(colourElement != null) {
+ colourElement.render();
+ }
+ }
+
+ @Override
+ public boolean mouseInputOverlay(int x, int y, int width, int mouseX, int mouseY) {
+ return colourElement != null && colourElement.mouseInput(mouseX, mouseY);
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ if(Mouse.getEventButtonState() && Mouse.getEventButton() == 0 &&
+ mouseX > x+width/6-24 && mouseX < x+width/6+24 &&
+ mouseY > y+height-7-14 && mouseY < y+height-7+2) {
+ colourElement = new GuiElementColour(mouseX, mouseY, (String) option.get(), (val) -> {
+ option.set(val);
+ chromaColour = val;
+ }, () -> colourElement = null);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return colourElement != null && colourElement.keyboardInput();
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDraggableList.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDraggableList.java
new file mode 100644
index 0000000..59f9e82
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDraggableList.java
@@ -0,0 +1,284 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.lerp.LerpUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.button_tex;
+
+public class GuiOptionEditorDraggableList extends GuiOptionEditor {
+
+ private static final ResourceLocation DELETE = new ResourceLocation("notenoughupdates:core/delete.png");
+
+ private String[] exampleText;
+ private List<Integer> activeText;
+ private int currentDragging = -1;
+ private int dragStartIndex = -1;
+
+ private long trashHoverTime = -1;
+
+ private int dragOffsetX = -1;
+ private int dragOffsetY = -1;
+
+ private boolean dropdownOpen = false;
+
+ public GuiOptionEditorDraggableList(ConfigProcessor.ProcessedOption option, String[] exampleText) {
+ super(option);
+
+ this.exampleText = exampleText;
+ this.activeText = (List<Integer>) option.get();
+ }
+
+ @Override
+ public int getHeight() {
+ int height = super.getHeight() + 13;
+
+ for(int strIndex : activeText) {
+ String str = exampleText[strIndex];
+ height += 10 * str.split("\n").length;
+ }
+
+ return height;
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+
+ int height = getHeight();
+
+ GlStateManager.color(1, 1, 1, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(button_tex);
+ RenderUtils.drawTexturedRect(x+width/6f-24, y+45-7-14, 48, 16);
+
+ TextRenderUtils.drawStringCenteredScaledMaxWidth("Add", Minecraft.getMinecraft().fontRendererObj,
+ x+width/6f, y+45-7-6,
+ false, 44, 0xFF303030);
+
+ long currentTime = System.currentTimeMillis();
+ float greenBlue = LerpUtils.clampZeroOne(((trashHoverTime < 0 ? 250 : 0) + trashHoverTime - currentTime)/250f);
+ GlStateManager.color(1, greenBlue, greenBlue, 1);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(DELETE);
+ Utils.drawTexturedRect(x+width/6f+27, y+45-7-13, 11, 14, GL11.GL_NEAREST);
+
+ Gui.drawRect(x+5, y+45, x+width-5, y+height-5, 0xffdddddd);
+ Gui.drawRect(x+6, y+46, x+width-6, y+height-6, 0xff000000);
+
+ int i = 0;
+ int yOff=0;
+ for(int strIndex : activeText) {
+ String str = exampleText[strIndex];
+
+ String[] multilines = str.split("\n");
+
+ int ySize = multilines.length * 10;
+
+ if(i++ != dragStartIndex) {
+ for(int multilineIndex=0; multilineIndex<multilines.length; multilineIndex++) {
+ String line = multilines[multilineIndex];
+ Utils.drawStringScaledMaxWidth(line+EnumChatFormatting.RESET, Minecraft.getMinecraft().fontRendererObj,
+ x+20, y+50+yOff+multilineIndex*10, true, width-20, 0xffffffff);
+ }
+ Minecraft.getMinecraft().fontRendererObj.drawString("\u2261", x+10, y+50+yOff + ySize/2f - 4, 0xffffff, true);
+ }
+
+ yOff += ySize;
+ }
+ }
+
+ @Override
+ public void renderOverlay(int x, int y, int width) {
+ super.renderOverlay(x, y, width);
+
+ if(dropdownOpen) {
+ List<Integer> remaining = new ArrayList<>();
+ for(int i=0; i<exampleText.length; i++) {
+ remaining.add(i);
+ }
+ remaining.removeAll(activeText);
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ int dropdownWidth = Math.min(width/2-10, 150);
+ int left = dragOffsetX;
+ int top = dragOffsetY;
+
+ int dropdownHeight = -1 + 12*remaining.size();
+
+ int main = 0xff202026;
+ int outline = 0xff404046;
+ Gui.drawRect(left, top, left+1, top+dropdownHeight, outline); //Left
+ Gui.drawRect(left+1, top, left+dropdownWidth, top+1, outline); //Top
+ Gui.drawRect(left+dropdownWidth-1, top+1, left+dropdownWidth, top+dropdownHeight, outline); //Right
+ Gui.drawRect(left+1, top+dropdownHeight-1, left+dropdownWidth-1, top+dropdownHeight, outline); //Bottom
+ Gui.drawRect(left+1, top+1, left+dropdownWidth-1, top+dropdownHeight-1, main); //Middle
+
+ int dropdownY = -1;
+ for(int strIndex : remaining) {
+ String str = exampleText[strIndex];
+ if(str.isEmpty()) {
+ str = "<NONE>";
+ }
+ TextRenderUtils.drawStringScaledMaxWidth(str.replaceAll("(\n.*)+", " ..."),
+ fr, left+3, top+3+dropdownY, false, dropdownWidth-6, 0xffa0a0a0);
+ dropdownY += 12;
+ }
+ } else if(currentDragging >= 0) {
+ int opacity = 0x80;
+ long currentTime = System.currentTimeMillis();
+ if(trashHoverTime < 0) {
+ float greenBlue = LerpUtils.clampZeroOne((currentTime + trashHoverTime)/250f);
+ opacity = (int)(opacity * greenBlue);
+ } else {
+ float greenBlue = LerpUtils.clampZeroOne((250 + trashHoverTime - currentTime)/250f);
+ opacity = (int)(opacity * greenBlue);
+ }
+
+ if(opacity < 20) return;
+
+ ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ int mouseX = Mouse.getX() * scaledResolution.getScaledWidth() / Minecraft.getMinecraft().displayWidth;
+ int mouseY = scaledResolution.getScaledHeight() - Mouse.getY() * scaledResolution.getScaledHeight() / Minecraft.getMinecraft().displayHeight - 1;
+
+ String str = exampleText[currentDragging];
+
+ String[] multilines = str.split("\n");
+
+ GlStateManager.enableBlend();
+ for(int multilineIndex=0; multilineIndex<multilines.length; multilineIndex++) {
+ String line = multilines[multilineIndex];
+ Utils.drawStringScaledMaxWidth(line+EnumChatFormatting.RESET, Minecraft.getMinecraft().fontRendererObj,
+ dragOffsetX + mouseX + 10, dragOffsetY + mouseY + multilineIndex*10, true, width-20, 0xffffff | (opacity << 24));
+ }
+
+ int ySize = multilines.length * 10;
+
+ Minecraft.getMinecraft().fontRendererObj.drawString("\u2261",
+ dragOffsetX + mouseX,
+ dragOffsetY + mouseY + ySize/2f - 4, 0xffffff, true);
+ }
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ if(!Mouse.getEventButtonState() && !dropdownOpen &&
+ dragStartIndex >= 0 && Mouse.getEventButton() == 0 &&
+ mouseX >= x+width/6+27-3 && mouseX <= x+width/6+27+11+3 &&
+ mouseY >= y+45-7-13-3 && mouseY <= y+45-7-13+14+3) {
+ activeText.remove(dragStartIndex);
+ currentDragging = -1;
+ dragStartIndex = -1;
+ return false;
+ }
+
+ if(!Mouse.isButtonDown(0) || dropdownOpen) {
+ currentDragging = -1;
+ dragStartIndex = -1;
+ if(trashHoverTime > 0) trashHoverTime = -System.currentTimeMillis();
+ } else if(currentDragging >= 0 &&
+ mouseX >= x+width/6+27-3 && mouseX <= x+width/6+27+11+3 &&
+ mouseY >= y+45-7-13-3 && mouseY <= y+45-7-13+14+3) {
+ if(trashHoverTime < 0) trashHoverTime = System.currentTimeMillis();
+ } else {
+ if(trashHoverTime > 0) trashHoverTime = -System.currentTimeMillis();
+ }
+
+ if(Mouse.getEventButtonState()) {
+ int height = getHeight();
+
+ if(dropdownOpen) {
+ List<Integer> remaining = new ArrayList<>();
+ for(int i=0; i<exampleText.length; i++) {
+ remaining.add(i);
+ }
+ remaining.removeAll(activeText);
+
+ int dropdownWidth = Math.min(width/2-10, 150);
+ int left = dragOffsetX;
+ int top = dragOffsetY;
+
+ int dropdownHeight = -1 + 12*remaining.size();
+
+ if(mouseX > left && mouseX < left+dropdownWidth &&
+ mouseY > top && mouseY < top + dropdownHeight) {
+ int dropdownY = -1;
+ for(int strIndex : remaining) {
+ if(mouseY < top+dropdownY+12) {
+ activeText.add(0, strIndex);
+ if(remaining.size() == 1) dropdownOpen = false;
+ return true;
+ }
+
+ dropdownY += 12;
+ }
+ }
+
+ dropdownOpen = false;
+ return true;
+ }
+
+ if(activeText.size() < exampleText.length &&
+ mouseX > x+width/6-24 && mouseX < x+width/6+24 &&
+ mouseY > y+45-7-14 && mouseY < y+45-7+2) {
+ dropdownOpen = !dropdownOpen;
+ dragOffsetX = mouseX;
+ dragOffsetY = mouseY;
+ return true;
+ }
+
+ if(Mouse.getEventButton() == 0 &&
+ mouseX > x+5 && mouseX < x+width-5 &&
+ mouseY > y+45 && mouseY < y+height-6) {
+ int yOff=0;
+ int i = 0;
+ for(int strIndex : activeText) {
+ int ySize = 10 * exampleText[strIndex].split("\n").length;
+ if(mouseY < y+50+yOff+ySize) {
+ dragOffsetX = x+10 - mouseX;
+ dragOffsetY = y+50+yOff - mouseY;
+
+ currentDragging = strIndex;
+ dragStartIndex = i;
+ break;
+ }
+ yOff += ySize;
+ i++;
+ }
+ }
+ } else if(Mouse.getEventButton() == -1 && currentDragging >= 0) {
+ int yOff=0;
+ int i = 0;
+ for(int strIndex : activeText) {
+ if(dragOffsetY + mouseY + 4 < y+50+yOff+10) {
+ activeText.remove(dragStartIndex);
+ activeText.add(i, currentDragging);
+
+ dragStartIndex = i;
+ break;
+ }
+ yOff += 10 * exampleText[strIndex].split("\n").length;
+ i++;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDropdown.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDropdown.java
new file mode 100644
index 0000000..5c797ad
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorDropdown.java
@@ -0,0 +1,152 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.render.RenderUtils;
+import com.thatgravyboat.skyblockhud.core.util.render.TextRenderUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+public class GuiOptionEditorDropdown extends GuiOptionEditor {
+
+ private final String[] values;
+ private final boolean useOrdinal;
+ private int selected;
+ private boolean open = false;
+
+ public GuiOptionEditorDropdown(ConfigProcessor.ProcessedOption option, String[] values, int selected, boolean useOrdinal) {
+ super(option);
+ if(selected >= values.length) selected = values.length;
+ this.values = values;
+ this.selected = selected;
+ this.useOrdinal = useOrdinal;
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+
+ if(!open) {
+ int height = getHeight();
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ int dropdownWidth = Math.min(width/3-10, 80);
+ int left = x+width/6-dropdownWidth/2;
+ int top = y+height-7-14;
+
+ String selectedString = " - Select - ";
+ if(selected >= 0 && selected < values.length) {
+ selectedString = values[selected];
+ }
+
+ RenderUtils.drawFloatingRectDark(left, top, dropdownWidth, 14, false);
+ TextRenderUtils.drawStringScaled("\u25BC", fr, left+dropdownWidth-10, y+height-7-15, false, 0xffa0a0a0, 2);
+
+ TextRenderUtils.drawStringScaledMaxWidth(selectedString, fr, left+3, top+3, false,
+ dropdownWidth-16, 0xffa0a0a0);
+ }
+ }
+
+ @Override
+ public void renderOverlay(int x, int y, int width) {
+ if(open) {
+ String selectedString = " - Select - ";
+ if(selected >= 0 && selected < values.length) {
+ selectedString = values[selected];
+ }
+
+ int height = getHeight();
+
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ int dropdownWidth = Math.min(width/3-10, 80);
+ int left = x+width/6-dropdownWidth/2;
+ int top = y+height-7-14;
+
+ int dropdownHeight = 13 + 12*values.length;
+
+ int main = 0xff202026;
+ int blue = 0xff2355ad;
+ Gui.drawRect(left, top, left+1, top+dropdownHeight, blue); //Left
+ Gui.drawRect(left+1, top, left+dropdownWidth, top+1, blue); //Top
+ Gui.drawRect(left+dropdownWidth-1, top+1, left+dropdownWidth, top+dropdownHeight, blue); //Right
+ Gui.drawRect(left+1, top+dropdownHeight-1, left+dropdownWidth-1, top+dropdownHeight, blue); //Bottom
+ Gui.drawRect(left+1, top+1, left+dropdownWidth-1, top+dropdownHeight-1, main); //Middle
+
+ Gui.drawRect(left+1, top+14-1, left+dropdownWidth-1, top+14, blue); //Bar
+
+ int dropdownY = 13;
+ for(String option : values) {
+ if(option.isEmpty()) {
+ option = "<NONE>";
+ }
+ TextRenderUtils.drawStringScaledMaxWidth(option, fr, left+3, top+3+dropdownY, false, dropdownWidth-6, 0xffa0a0a0);
+ dropdownY += 12;
+ }
+
+ TextRenderUtils.drawStringScaled("\u25B2", fr, left+dropdownWidth-10, y+height-7-15, false, 0xffa0a0a0, 2);
+
+
+ TextRenderUtils.drawStringScaledMaxWidth(selectedString, fr, left+3, top+3, false,
+ dropdownWidth-16, 0xffa0a0a0);
+ }
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ int left = x+width/6-40;
+ int top = y+height-7-14;
+
+ if(Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
+ if(mouseX >= left && mouseX <= left+80 &&
+ mouseY >= top && mouseY <= top+14) {
+ open = !open;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean mouseInputOverlay(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ int left = x+width/6-40;
+ int top = y+height-7-14;
+
+ if(Mouse.getEventButtonState() && Mouse.getEventButton() == 0) {
+ if(!(mouseX >= left && mouseX <= left+80 &&
+ mouseY >= top && mouseY <= top+14) && open) {
+ open = false;
+ if(mouseX >= left && mouseX <= left+80) {
+ int dropdownY = 13;
+ for(int ordinal=0; ordinal < values.length; ordinal++) {
+ if(mouseY >= top+3+dropdownY && mouseY <= top+3+dropdownY+12) {
+ selected = ordinal;
+ if(useOrdinal) {
+ option.set(selected);
+ } else {
+ option.set(values[selected]);
+ }
+ return true;
+ }
+ dropdownY += 12;
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorSlider.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorSlider.java
new file mode 100644
index 0000000..a37b952
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorSlider.java
@@ -0,0 +1,132 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.GuiElementBoolean;
+import com.thatgravyboat.skyblockhud.core.GuiElementTextField;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.GuiElementSlider;
+import net.minecraft.client.Minecraft;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+
+public class GuiOptionEditorSlider extends GuiOptionEditor {
+
+ private final GuiElementSlider slider;
+ private final GuiElementTextField textField;
+
+ public GuiOptionEditorSlider(ConfigProcessor.ProcessedOption option, float minValue, float maxValue, float minStep) {
+ super(option);
+ if(minStep < 0) minStep = 0.01f;
+
+ float floatVal = ((Number)option.get()).floatValue();
+ {
+ String strVal;
+ if(floatVal % 1 == 0) {
+ strVal = Integer.toString((int)floatVal);
+ } else {
+ strVal = Float.toString(floatVal);
+ }
+ textField = new GuiElementTextField(strVal,
+ GuiElementTextField.NO_SPACE | GuiElementTextField.NUM_ONLY | GuiElementTextField.SCALE_TEXT);
+ }
+
+ slider = new GuiElementSlider(0, 0, 80, minValue, maxValue, minStep, floatVal, (val) -> {
+ option.set(val);
+
+ String strVal;
+ if(val % 1 == 0) {
+ strVal = Integer.toString(val.intValue());
+ } else {
+ strVal = Float.toString(val);
+ strVal = strVal.replaceAll("(\\.\\d\\d\\d)(?:\\d)+", "$1");
+ strVal = strVal.replaceAll("0+$", "");
+ }
+ textField.setText(strVal);
+ });
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+ int height = getHeight();
+
+ int fullWidth = Math.min(width/3-10, 80);
+ int sliderWidth = (fullWidth-5)*3/4;
+ int textFieldWidth = (fullWidth-5)/4;
+
+ slider.x = x+width/6-fullWidth/2;
+ slider.y = y+height-7-14;
+ slider.width = sliderWidth;
+ slider.render();
+
+ if(textField.getFocus()) {
+ textField.setOptions(GuiElementTextField.NO_SPACE | GuiElementTextField.NUM_ONLY);
+ textField.setSize(Minecraft.getMinecraft().fontRendererObj.getStringWidth(textField.getText())+10,
+ 16);
+ } else {
+ textField.setSize(textFieldWidth, 16);
+ textField.setOptions(GuiElementTextField.NO_SPACE | GuiElementTextField.NUM_ONLY | GuiElementTextField.SCALE_TEXT);
+ }
+
+ textField.render(x+width/6-fullWidth/2+sliderWidth+5, y+height-7-14);
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ int fullWidth = Math.min(width/3-10, 80);
+ int sliderWidth = (fullWidth-5)*3/4;
+ int textFieldWidth = (fullWidth-5)/4;
+
+ slider.x = x+width/6-fullWidth/2;
+ slider.y = y+height-7-14;
+ slider.width = sliderWidth;
+ if(slider.mouseInput(mouseX, mouseY)) {
+ textField.unfocus();
+ return true;
+ }
+
+ if(textField.getFocus()) {
+ textFieldWidth = Minecraft.getMinecraft().fontRendererObj.getStringWidth(textField.getText())+10;
+ }
+
+ int textFieldX = x+width/6-fullWidth/2+sliderWidth+5;
+ int textFieldY = y+height-7-14;
+ textField.setSize(textFieldWidth, 16);
+
+ if(Mouse.getEventButtonState() && (Mouse.getEventButton() == 0 || Mouse.getEventButton() == 1)) {
+ if(mouseX > textFieldX && mouseX < textFieldX+textFieldWidth &&
+ mouseY > textFieldY && mouseY < textFieldY+16) {
+ textField.mouseClicked(mouseX, mouseY, Mouse.getEventButton());
+ return true;
+ }
+ textField.unfocus();
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ if(Keyboard.getEventKeyState() && textField.getFocus()) {
+ textField.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey());
+
+ try {
+ textField.setCustomBorderColour(0xffffffff);
+ float f = Float.parseFloat(textField.getText());
+ if(option.set(f)) {
+ slider.setValue(f);
+ } else {
+ textField.setCustomBorderColour(0xff0000ff);
+ }
+ } catch(Exception e) {
+ textField.setCustomBorderColour(0xffff0000);
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorText.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorText.java
new file mode 100644
index 0000000..e42d5b3
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiOptionEditorText.java
@@ -0,0 +1,84 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.core.GuiElementBoolean;
+import com.thatgravyboat.skyblockhud.core.GuiElementTextField;
+import com.thatgravyboat.skyblockhud.core.config.struct.ConfigProcessor;
+import com.thatgravyboat.skyblockhud.core.util.GuiElementSlider;
+import net.minecraft.client.Minecraft;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+
+public class GuiOptionEditorText extends GuiOptionEditor {
+
+ private final GuiElementTextField textField;
+
+ public GuiOptionEditorText(ConfigProcessor.ProcessedOption option) {
+ super(option);
+
+ textField = new GuiElementTextField((String)option.get(), 0);
+ }
+
+ @Override
+ public void render(int x, int y, int width) {
+ super.render(x, y, width);
+ int height = getHeight();
+
+ int fullWidth = Math.min(width/3-10, 80);
+
+ int textFieldX = x+width/6-fullWidth/2;
+ if(textField.getFocus()) {
+ fullWidth = Math.max(fullWidth, Minecraft.getMinecraft().fontRendererObj.getStringWidth(textField.getText())+10);
+ }
+
+ textField.setSize(fullWidth, 16);
+
+ textField.render(textFieldX, y+height-7-14);
+ }
+
+ @Override
+ public boolean mouseInput(int x, int y, int width, int mouseX, int mouseY) {
+ int height = getHeight();
+
+ int fullWidth = Math.min(width/3-10, 80);
+
+ int textFieldX = x+width/6-fullWidth/2;
+
+ if(textField.getFocus()) {
+ fullWidth = Math.max(fullWidth, Minecraft.getMinecraft().fontRendererObj.getStringWidth(textField.getText())+10);
+ }
+
+ int textFieldY = y+height-7-14;
+ textField.setSize(fullWidth, 16);
+
+ if(Mouse.getEventButtonState() && (Mouse.getEventButton() == 0 || Mouse.getEventButton() == 1)) {
+ if(mouseX > textFieldX && mouseX < textFieldX+fullWidth &&
+ mouseY > textFieldY && mouseY < textFieldY+16) {
+ textField.mouseClicked(mouseX, mouseY, Mouse.getEventButton());
+ return true;
+ }
+ textField.unfocus();
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ if(Keyboard.getEventKeyState() && textField.getFocus()) {
+ Keyboard.enableRepeatEvents(true);
+ textField.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey());
+
+ try {
+ textField.setCustomBorderColour(0xffffffff);
+ option.set(textField.getText());
+ } catch(Exception e) {
+ textField.setCustomBorderColour(0xffff0000);
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiPositionEditor.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiPositionEditor.java
new file mode 100644
index 0000000..e178843
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/gui/GuiPositionEditor.java
@@ -0,0 +1,179 @@
+package com.thatgravyboat.skyblockhud.core.config.gui;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.core.config.Position;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+
+import java.io.IOException;
+
+public class GuiPositionEditor extends GuiScreen {
+
+ private final Position position;
+ private Position originalPosition;
+ private final int elementWidth;
+ private final int elementHeight;
+ private final Runnable renderCallback;
+ private final Runnable positionChangedCallback;
+ private final Runnable closedCallback;
+ private boolean clicked = false;
+ private int grabbedX = 0;
+ private int grabbedY = 0;
+
+ private int guiScaleOverride = -1;
+
+ public GuiPositionEditor(Position position, int elementWidth, int elementHeight,
+ Runnable renderCallback,
+ Runnable positionChangedCallback,
+ Runnable closedCallback) {
+ this.position = position;
+ this.originalPosition = position.clone();
+ this.elementWidth = elementWidth == -1 ? this.width : elementWidth;
+ this.elementHeight = elementHeight;
+ this.renderCallback = renderCallback;
+ this.positionChangedCallback = positionChangedCallback;
+ this.closedCallback = closedCallback;
+ }
+
+ public GuiPositionEditor withScale(int scale) {
+ this.guiScaleOverride = scale;
+ return this;
+ }
+
+ @Override
+ public void onGuiClosed() {
+ super.onGuiClosed();
+ closedCallback.run();
+ }
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ super.drawScreen(mouseX, mouseY, partialTicks);
+ ScaledResolution scaledResolution;
+ if(guiScaleOverride >= 0) {
+ scaledResolution = Utils.pushGuiScale(guiScaleOverride);
+ } else {
+ scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ }
+
+ this.width = scaledResolution.getScaledWidth();
+ this.height = scaledResolution.getScaledHeight();
+ mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth;
+ mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1;
+
+ drawDefaultBackground();
+
+ if(clicked) {
+ grabbedX += position.moveX(mouseX - grabbedX, elementWidth, scaledResolution);
+ grabbedY += position.moveY(mouseY - grabbedY, elementHeight, scaledResolution);
+ }
+
+ renderCallback.run();
+
+ int x = position.getAbsX(scaledResolution, elementWidth);
+ int y = position.getAbsY(scaledResolution, elementHeight);
+
+ if(position.isCenterX()) x -= elementWidth/2;
+ if(position.isCenterY()) y -= elementHeight/2;
+ Gui.drawRect(x, y, x+elementWidth, y+elementHeight, 0x80404040);
+
+ if(guiScaleOverride >= 0) {
+ Utils.pushGuiScale(-1);
+ }
+
+ scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ Utils.drawStringCentered("Position Editor", Minecraft.getMinecraft().fontRendererObj,
+ scaledResolution.getScaledWidth()/2f, 8, true, 0xffffff);
+ Utils.drawStringCentered("R to Reset - Arrow keys/mouse to move", Minecraft.getMinecraft().fontRendererObj,
+ scaledResolution.getScaledWidth()/2f, 18, true, 0xffffff);
+ }
+
+ @Override
+ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
+ super.mouseClicked(mouseX, mouseY, mouseButton);
+
+ if(mouseButton == 0) {
+ ScaledResolution scaledResolution;
+ if(guiScaleOverride >= 0) {
+ scaledResolution = Utils.pushGuiScale(guiScaleOverride);
+ } else {
+ scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ }
+ mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth;
+ mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1;
+
+ int x = position.getAbsX(scaledResolution, elementWidth);
+ int y = position.getAbsY(scaledResolution, elementHeight);
+ if(position.isCenterX()) x -= elementWidth/2;
+ if(position.isCenterY()) y -= elementHeight/2;
+
+ if(mouseX >= x && mouseY >= y &&
+ mouseX <= x+elementWidth && mouseY <= y+elementHeight) {
+ clicked = true;
+ grabbedX = mouseX;
+ grabbedY = mouseY;
+ }
+
+ if(guiScaleOverride >= 0) {
+ Utils.pushGuiScale(-1);
+ }
+ }
+ }
+
+ @Override
+ protected void keyTyped(char typedChar, int keyCode) throws IOException {
+ Keyboard.enableRepeatEvents(true);
+
+ if(keyCode == Keyboard.KEY_R) {
+ position.set(originalPosition);
+ } else if(!clicked) {
+ boolean shiftHeld = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
+ int dist = shiftHeld ? 10 : 1;
+ if(keyCode == Keyboard.KEY_DOWN) {
+ position.moveY(dist, elementHeight, new ScaledResolution(Minecraft.getMinecraft()));
+ } else if(keyCode == Keyboard.KEY_UP) {
+ position.moveY(-dist, elementHeight, new ScaledResolution(Minecraft.getMinecraft()));
+ } else if(keyCode == Keyboard.KEY_LEFT) {
+ position.moveX(-dist, elementWidth, new ScaledResolution(Minecraft.getMinecraft()));
+ } else if(keyCode == Keyboard.KEY_RIGHT) {
+ position.moveX(dist, elementWidth, new ScaledResolution(Minecraft.getMinecraft()));
+ }
+ }
+ super.keyTyped(typedChar, keyCode);
+ }
+
+ @Override
+ protected void mouseReleased(int mouseX, int mouseY, int state) {
+ super.mouseReleased(mouseX, mouseY, state);
+ clicked = false;
+ }
+
+ @Override
+ protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
+ super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick);
+
+ if(clicked) {
+ ScaledResolution scaledResolution;
+ if(guiScaleOverride >= 0) {
+ scaledResolution = Utils.pushGuiScale(guiScaleOverride);
+ } else {
+ scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ }
+ mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth;
+ mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1;
+
+ grabbedX += position.moveX(mouseX - grabbedX, elementWidth, scaledResolution);
+ grabbedY += position.moveY(mouseY - grabbedY, elementHeight, scaledResolution);
+ positionChangedCallback.run();
+
+ if(guiScaleOverride >= 0) {
+ Utils.pushGuiScale(-1);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/config/struct/ConfigProcessor.java b/src/main/java/com/thatgravyboat/skyblockhud/core/config/struct/ConfigProcessor.java
new file mode 100644
index 0000000..aef92d8
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/config/struct/ConfigProcessor.java
@@ -0,0 +1,176 @@
+package com.thatgravyboat.skyblockhud.core.config.struct;
+
+import com.google.gson.annotations.Expose;
+import com.thatgravyboat.skyblockhud.core.config.Config;
+import com.thatgravyboat.skyblockhud.core.config.annotations.*;
+import com.thatgravyboat.skyblockhud.core.config.gui.*;
+import com.thatgravyboat.skyblockhud.core.config.Config;
+
+import java.lang.reflect.Field;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+public class ConfigProcessor {
+
+ public static class ProcessedCategory {
+ public final String name;
+ public final String desc;
+ public final LinkedHashMap<String, ProcessedOption> options = new LinkedHashMap<>();
+
+ public ProcessedCategory(String name, String desc) {
+ this.name = name;
+ this.desc = desc;
+ }
+ }
+
+ public static class ProcessedOption {
+ public final String name;
+ public final String desc;
+ public final int subcategoryId;
+ public GuiOptionEditor editor;
+
+ public int accordionId = -1;
+
+ private final Field field;
+ private final Object container;
+
+ public ProcessedOption(String name, String desc, int subcategoryId, Field field, Object container) {
+ this.name = name;
+ this.desc = desc;
+ this.subcategoryId = subcategoryId;
+
+ this.field = field;
+ this.container = container;
+ }
+
+ public Object get() {
+ try {
+ return field.get(container);
+ } catch(Exception e) {
+ return null;
+ }
+ }
+
+ public boolean set(Object value) {
+ try {
+ if(field.getType() == int.class && value instanceof Number) {
+ field.set(container, ((Number)value).intValue());
+ } else {
+ field.set(container, value);
+ }
+ return true;
+ } catch(Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ }
+
+ public static LinkedHashMap<String, ProcessedCategory> create(Config config) {
+ LinkedHashMap<String, ProcessedCategory> processedConfig = new LinkedHashMap<>();
+ for(Field categoryField : config.getClass().getDeclaredFields()) {
+ boolean exposePresent = categoryField.isAnnotationPresent(Expose.class);
+ boolean categoryPresent = categoryField.isAnnotationPresent(Category.class);
+
+ if(exposePresent && categoryPresent) {
+ Object categoryObj;
+ try {
+ categoryObj = categoryField.get(config);
+ } catch(Exception e) {
+ //System.err.printf("Failed to load config category %s. Field was not accessible.\n", categoryField.getName());
+ continue;
+ }
+
+ Category categoryAnnotation = categoryField.getAnnotation(Category.class);
+ ProcessedCategory cat = new ProcessedCategory(
+ categoryAnnotation.name(),
+ categoryAnnotation.desc()
+ );
+ processedConfig.put(categoryField.getName(), cat);
+
+ for(Field optionField : categoryObj.getClass().getDeclaredFields()) {
+ boolean optionPresent = optionField.isAnnotationPresent(ConfigOption.class);
+
+ if(optionPresent) {
+ ConfigOption optionAnnotation = optionField.getAnnotation(ConfigOption.class);
+ ProcessedOption option = new ProcessedOption(
+ optionAnnotation.name(),
+ optionAnnotation.desc(),
+ optionAnnotation.subcategoryId(),
+ optionField,
+ categoryObj
+ );
+ if(optionField.isAnnotationPresent(ConfigAccordionId.class)) {
+ ConfigAccordionId annotation = optionField.getAnnotation(ConfigAccordionId.class);
+ option.accordionId = annotation.id();
+ }
+
+ GuiOptionEditor editor = null;
+ Class<?> optionType = optionField.getType();
+ if(optionField.isAnnotationPresent(ConfigEditorButton.class)) {
+ ConfigEditorButton configEditorAnnotation = optionField.getAnnotation(ConfigEditorButton.class);
+ editor = new GuiOptionEditorButton(option, configEditorAnnotation.runnableId(), configEditorAnnotation.buttonText(), config);
+ }
+ if(optionType.isAssignableFrom(boolean.class) &&
+ optionField.isAnnotationPresent(ConfigEditorBoolean.class)) {
+ editor = new GuiOptionEditorBoolean(option);
+ }
+ if(optionType.isAssignableFrom(boolean.class) &&
+ optionField.isAnnotationPresent(ConfigEditorAccordion.class)) {
+ ConfigEditorAccordion configEditorAnnotation = optionField.getAnnotation(ConfigEditorAccordion.class);
+ editor = new GuiOptionEditorAccordion(option, configEditorAnnotation.id());
+ }
+ if(optionType.isAssignableFrom(int.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorDropdown.class)) {
+ ConfigEditorDropdown configEditorAnnotation = optionField.getAnnotation(ConfigEditorDropdown.class);
+ editor = new GuiOptionEditorDropdown(option, configEditorAnnotation.values(), (int)option.get(), true);
+ }
+ }
+ if(optionType.isAssignableFrom(List.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorDraggableList.class)) {
+ ConfigEditorDraggableList configEditorAnnotation = optionField.getAnnotation(ConfigEditorDraggableList.class);
+ editor = new GuiOptionEditorDraggableList(option, configEditorAnnotation.exampleText());
+ }
+ }
+ if(optionType.isAssignableFrom(String.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorDropdown.class)) {
+ ConfigEditorDropdown configEditorAnnotation = optionField.getAnnotation(ConfigEditorDropdown.class);
+ editor = new GuiOptionEditorDropdown(option, configEditorAnnotation.values(),
+ configEditorAnnotation.initialIndex(), false);
+ } else if(optionField.isAnnotationPresent(ConfigEditorColour.class)) {
+ editor = new GuiOptionEditorColour(option);
+ } else if(optionField.isAnnotationPresent(ConfigEditorText.class)) {
+ editor = new GuiOptionEditorText(option);
+ }
+ }
+ if(optionType.isAssignableFrom(int.class) ||
+ optionType.isAssignableFrom(float.class) ||
+ optionType.isAssignableFrom(double.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorSlider.class)) {
+ ConfigEditorSlider configEditorAnnotation = optionField.getAnnotation(ConfigEditorSlider.class);
+ editor = new GuiOptionEditorSlider(option, configEditorAnnotation.minValue(),
+ configEditorAnnotation.maxValue(), configEditorAnnotation.minStep());
+ }
+ }
+ if(optionType.isAssignableFrom(String.class)) {
+ if(optionField.isAnnotationPresent(ConfigEditorDropdown.class)) {
+ ConfigEditorDropdown configEditorAnnotation = optionField.getAnnotation(ConfigEditorDropdown.class);
+ editor = new GuiOptionEditorDropdown(option, configEditorAnnotation.values(), 0,false);
+ }
+ }
+ if(editor == null) {
+ //System.err.printf("Failed to load config option %s. Could not find suitable editor.\n", optionField.getName());
+ continue;
+ }
+ option.editor = editor;
+ cat.options.put(optionField.getName(), option);
+ }
+ }
+ } else if(exposePresent || categoryPresent) {
+ //System.err.printf("Failed to load config category %s. Both @Expose and @Category must be present.\n", categoryField.getName());
+ }
+ }
+ return processedConfig;
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/GuiElementSlider.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/GuiElementSlider.java
new file mode 100644
index 0000000..ee963c2
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/GuiElementSlider.java
@@ -0,0 +1,123 @@
+package com.thatgravyboat.skyblockhud.core.util;
+
+import com.thatgravyboat.skyblockhud.Utils;
+import com.thatgravyboat.skyblockhud.core.GuiElement;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+import java.util.function.Consumer;
+
+import static com.thatgravyboat.skyblockhud.GuiTextures.*;
+
+public class GuiElementSlider extends GuiElement {
+
+ public int x;
+ public int y;
+ public int width;
+ private static final int HEIGHT = 16;
+
+ private float minValue;
+ private float maxValue;
+ private float minStep;
+
+ private float value;
+ private Consumer<Float> setCallback;
+
+ private boolean clicked = false;
+
+ public GuiElementSlider(int x, int y, int width, float minValue, float maxValue, float minStep,
+ float value, Consumer<Float> setCallback) {
+ if(minStep < 0) minStep = 0.01f;
+
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+ this.minStep = minStep;
+ this.value = value;
+ this.setCallback = setCallback;
+ }
+
+ public void setValue(float value) {
+ this.value = value;
+ }
+
+ @Override
+ public void render() {
+ final ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ int mouseX = Mouse.getX() * scaledResolution.getScaledWidth() / Minecraft.getMinecraft().displayWidth;
+
+ float value = this.value;
+ if(clicked) {
+ value = (mouseX-x)*(maxValue-minValue)/width+minValue;
+ value = Math.max(minValue, Math.min(maxValue, value));
+ value = Math.round(value/minStep)*minStep;
+ }
+
+ float sliderAmount = Math.max(0, Math.min(1, (value-minValue)/(maxValue-minValue)));
+ int sliderAmountI = (int)(width*sliderAmount);
+
+ GlStateManager.color(1f, 1f, 1f, 1f);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_on_cap);
+ Utils.drawTexturedRect(x, y, 4, HEIGHT, GL11.GL_NEAREST);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_off_cap);
+ Utils.drawTexturedRect(x+width-4, y, 4, HEIGHT, GL11.GL_NEAREST);
+
+ if(sliderAmountI > 5) {
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_on_segment);
+ Utils.drawTexturedRect(x+4, y, sliderAmountI-4, HEIGHT, GL11.GL_NEAREST);
+ }
+
+ if(sliderAmountI < width-5) {
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_off_segment);
+ Utils.drawTexturedRect(x+sliderAmountI, y, width-4-sliderAmountI, HEIGHT, GL11.GL_NEAREST);
+ }
+
+ for(int i=1; i<4; i++) {
+ int notchX = x+width*i/4-1;
+ Minecraft.getMinecraft().getTextureManager().bindTexture(notchX > x+sliderAmountI ? slider_off_notch : slider_on_notch);
+ Utils.drawTexturedRect(notchX, y+(HEIGHT-4)/2f, 2, 4, GL11.GL_NEAREST);
+ }
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(slider_button_new);
+ Utils.drawTexturedRect(x+sliderAmountI-4, y, 8, HEIGHT, GL11.GL_NEAREST);
+ }
+
+ @Override
+ public boolean mouseInput(int mouseX, int mouseY) {
+ if(!Mouse.isButtonDown(0)) {
+ clicked = false;
+ }
+
+ if(Mouse.getEventButton() == 0) {
+ clicked = Mouse.getEventButtonState() && mouseX > x && mouseX < x+width && mouseY > y && mouseY < y+HEIGHT;
+ if(clicked) {
+ value = (mouseX-x)*(maxValue-minValue)/width+minValue;
+ value = Math.max(minValue, Math.min(maxValue, value));
+ value = (float)(Math.round(value/minStep)*(double)minStep);
+ setCallback.accept(value);
+ return true;
+ }
+ }
+
+ if(!Mouse.getEventButtonState() && Mouse.getEventButton() == -1 && clicked) {
+ value = (mouseX-x)*(maxValue-minValue)/width+minValue;
+ value = Math.max(minValue, Math.min(maxValue, value));
+ value = Math.round(value/minStep)*minStep;
+ setCallback.accept(value);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean keyboardInput() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/MiscUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/MiscUtils.java
new file mode 100644
index 0000000..da89e75
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/MiscUtils.java
@@ -0,0 +1,104 @@
+package com.thatgravyboat.skyblockhud.core.util;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.ResourceLocation;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.input.Cursor;
+import org.lwjgl.input.Mouse;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.datatransfer.StringSelection;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.IntBuffer;
+import java.nio.file.Files;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class MiscUtils {
+
+ public static void copyToClipboard(String str) {
+ Toolkit.getDefaultToolkit().getSystemClipboard()
+ .setContents(new StringSelection(str), null);
+ }
+ private static void unzip(InputStream src, File dest) {
+ //buffer for read and write data to file
+ byte[] buffer = new byte[1024];
+ try {
+ ZipInputStream zis = new ZipInputStream(src);
+ ZipEntry ze = zis.getNextEntry();
+ while(ze != null){
+ if(!ze.isDirectory()) {
+ String fileName = ze.getName();
+ File newFile = new File(dest, fileName);
+ //create directories for sub directories in zip
+ new File(newFile.getParent()).mkdirs();
+ FileOutputStream fos = new FileOutputStream(newFile);
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ fos.write(buffer, 0, len);
+ }
+ fos.close();
+ }
+ //close this ZipEntry
+ zis.closeEntry();
+ ze = zis.getNextEntry();
+ }
+ //close last ZipEntry
+ zis.closeEntry();
+ zis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void recursiveDelete(File file) {
+ if(file.isDirectory() && !Files.isSymbolicLink(file.toPath())) {
+ for(File child : file.listFiles()) {
+ recursiveDelete(child);
+ }
+ }
+ file.delete();
+ }
+
+ private static String currentCursor = null;
+
+ public static void resetCursor() {
+ if(currentCursor == null) {
+ return;
+ }
+ currentCursor = null;
+ try { Mouse.setNativeCursor(null); } catch(Exception ignored) {}
+ }
+
+ public static void setCursor(ResourceLocation loc, int hotspotX, int hotspotY) {
+ if(currentCursor != null && loc.getResourcePath().equals(currentCursor)) {
+ return;
+ }
+ currentCursor = loc.getResourcePath();
+ try {
+ BufferedImage image = ImageIO.read(Minecraft.getMinecraft()
+ .getResourceManager().getResource(loc).getInputStream());
+ int maxSize = Cursor.getMaxCursorSize();
+ IntBuffer buffer = BufferUtils.createIntBuffer(maxSize*maxSize);
+ for(int i=0; i<maxSize*maxSize; i++) {
+ int cursorX = i%maxSize;
+ int cursorY = i/maxSize;
+ if(cursorX >= image.getWidth() || cursorY >= image.getHeight()) {
+ buffer.put(0x00000000);
+ } else {
+ buffer.put(image.getRGB(cursorX, image.getHeight()-1-cursorY));
+ }
+ }
+ buffer.flip();
+ Mouse.setNativeCursor(new Cursor(maxSize, maxSize, hotspotX, hotspotY, 1,
+ buffer, null));
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/Splitters.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/Splitters.java
new file mode 100644
index 0000000..9736803
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/Splitters.java
@@ -0,0 +1,10 @@
+package com.thatgravyboat.skyblockhud.core.util;
+
+import com.google.common.base.Splitter;
+
+public class Splitters {
+
+ public static final Splitter NEWLINE_SPLITTER = Splitter.on('\n');
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/StringUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/StringUtils.java
new file mode 100644
index 0000000..d8fd0e6
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/StringUtils.java
@@ -0,0 +1,39 @@
+package com.thatgravyboat.skyblockhud.core.util;
+
+import com.google.common.collect.Sets;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+
+import java.util.Set;
+
+public class StringUtils {
+
+ public static final Set<String> PROTOCOLS = Sets.newHashSet("http", "https");
+
+ public static String cleanColour(String in) {
+ return in.replaceAll("(?i)\\u00A7.", "");
+ }
+
+ public static String cleanColourNotModifiers(String in) {
+ return in.replaceAll("(?i)\\u00A7[0-9a-f]", "\u00A7r");
+ }
+
+ public static String trimToWidth(String str, int len) {
+ FontRenderer fr = Minecraft.getMinecraft().fontRendererObj;
+ String trim = fr.trimStringToWidth(str, len);
+
+ if(str.length() != trim.length() && !trim.endsWith(" ")) {
+ char next = str.charAt(trim.length());
+ if(next != ' ') {
+ String[] split = trim.split(" ");
+ String last = split[split.length-1];
+ if(last.length() < 8) {
+ trim = trim.substring(0, trim.length()-last.length());
+ }
+ }
+ }
+
+ return trim;
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpUtils.java
new file mode 100644
index 0000000..e742ab1
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpUtils.java
@@ -0,0 +1,26 @@
+package com.thatgravyboat.skyblockhud.core.util.lerp;
+
+public class LerpUtils {
+
+ public static float clampZeroOne(float f) {
+ return Math.max(0, Math.min(1, f));
+ }
+
+ public static float sigmoid(float val) {
+ return (float)(1/(1 + Math.exp(-val)));
+ }
+
+ private static final float sigmoidStr = 8;
+ private static final float sigmoidA = -1/(sigmoid(-0.5f * sigmoidStr) - sigmoid(0.5f * sigmoidStr));
+ private static final float sigmoidB = sigmoidA*sigmoid(-0.5f * sigmoidStr);
+ public static float sigmoidZeroOne(float f) {
+ f = clampZeroOne(f);
+ return sigmoidA*sigmoid(sigmoidStr*(f-0.5f))-sigmoidB;
+ }
+
+ public static float lerp(float a, float b, float amount) {
+ return b + (a - b) * clampZeroOne(amount);
+ }
+
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingFloat.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingFloat.java
new file mode 100644
index 0000000..4831956
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingFloat.java
@@ -0,0 +1,68 @@
+package com.thatgravyboat.skyblockhud.core.util.lerp;
+
+public class LerpingFloat {
+
+ private int timeSpent;
+ private long lastMillis;
+ private int timeToReachTarget;
+
+ private float targetValue;
+ private float lerpValue;
+
+ public LerpingFloat(float initialValue, int timeToReachTarget) {
+ this.targetValue = this.lerpValue = initialValue;
+ this.timeToReachTarget = timeToReachTarget;
+ }
+
+ public LerpingFloat(int initialValue) {
+ this(initialValue, 200);
+ }
+
+ public void tick() {
+ int lastTimeSpent = timeSpent;
+ this.timeSpent += System.currentTimeMillis() - lastMillis;
+
+ float lastDistPercentToTarget = lastTimeSpent/(float)timeToReachTarget;
+ float distPercentToTarget = timeSpent/(float)timeToReachTarget;
+ float fac = (1-lastDistPercentToTarget)/lastDistPercentToTarget;
+
+ float startValue = lerpValue - (targetValue - lerpValue)/fac;
+
+ float dist = targetValue - startValue;
+ if(dist == 0) return;
+
+ float oldLerpValue = lerpValue;
+ if(distPercentToTarget >= 1) {
+ lerpValue = targetValue;
+ } else {
+ lerpValue = startValue + dist*distPercentToTarget;
+ }
+
+ if(lerpValue == oldLerpValue) {
+ timeSpent = lastTimeSpent;
+ } else {
+ this.lastMillis = System.currentTimeMillis();
+ }
+ }
+
+ public void resetTimer() {
+ this.timeSpent = 0;
+ this.lastMillis = System.currentTimeMillis();
+ }
+
+ public void setTarget(float targetValue) {
+ this.targetValue = targetValue;
+ }
+
+ public void setValue(float value) {
+ this.targetValue = this.lerpValue = value;
+ }
+
+ public float getValue() {
+ return lerpValue;
+ }
+
+ public float getTarget() {
+ return targetValue;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingInteger.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingInteger.java
new file mode 100644
index 0000000..48ae5ad
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/lerp/LerpingInteger.java
@@ -0,0 +1,76 @@
+package com.thatgravyboat.skyblockhud.core.util.lerp;
+
+public class LerpingInteger {
+
+ private int timeSpent;
+ private long lastMillis;
+ private int timeToReachTarget;
+
+ private int targetValue;
+ private int lerpValue;
+
+ public LerpingInteger(int initialValue, int timeToReachTarget) {
+ this.targetValue = this.lerpValue = initialValue;
+ this.timeToReachTarget = timeToReachTarget;
+ }
+
+ public LerpingInteger(int initialValue) {
+ this(initialValue, 200);
+ }
+
+ public void tick() {
+ int lastTimeSpent = timeSpent;
+ this.timeSpent += System.currentTimeMillis() - lastMillis;
+
+ float lastDistPercentToTarget = lastTimeSpent/(float)timeToReachTarget;
+ float distPercentToTarget = timeSpent/(float)timeToReachTarget;
+ float fac = (1-lastDistPercentToTarget)/lastDistPercentToTarget;
+
+ int startValue = lerpValue - (int)((targetValue - lerpValue)/fac);
+
+ int dist = targetValue - startValue;
+ if(dist == 0) return;
+
+ int oldLerpValue = lerpValue;
+ if(distPercentToTarget >= 1) {
+ lerpValue = targetValue;
+ } else {
+ lerpValue = startValue + (int)(dist*distPercentToTarget);
+ }
+
+ if(lerpValue == oldLerpValue) {
+ timeSpent = lastTimeSpent;
+ } else {
+ this.lastMillis = System.currentTimeMillis();
+ }
+ }
+
+ public int getTimeSpent() {
+ return timeSpent;
+ }
+
+ public void resetTimer() {
+ this.timeSpent = 0;
+ this.lastMillis = System.currentTimeMillis();
+ }
+
+ public void setTimeToReachTarget(int timeToReachTarget) {
+ this.timeToReachTarget = timeToReachTarget;
+ }
+
+ public void setTarget(int targetValue) {
+ this.targetValue = targetValue;
+ }
+
+ public void setValue(int value) {
+ this.targetValue = this.lerpValue = value;
+ }
+
+ public int getValue() {
+ return lerpValue;
+ }
+
+ public int getTarget() {
+ return targetValue;
+ }
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/RenderUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/RenderUtils.java
new file mode 100644
index 0000000..95d1b05
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/RenderUtils.java
@@ -0,0 +1,165 @@
+package com.thatgravyboat.skyblockhud.core.util.render;
+
+import com.thatgravyboat.skyblockhud.core.BackgroundBlur;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.OpenGlHelper;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL14;
+
+public class RenderUtils {
+
+ public static void drawFloatingRectDark(int x, int y, int width, int height) {
+ drawFloatingRectDark(x, y, width, height, true);
+ }
+
+ public static void drawFloatingRectDark(int x, int y, int width, int height, boolean shadow) {
+ int alpha = 0xf0000000;
+
+ if(OpenGlHelper.isFramebufferEnabled()) {
+ ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
+ BackgroundBlur.renderBlurredBackground(15, scaledResolution.getScaledWidth(),
+ scaledResolution.getScaledHeight(), x, y, width, height, true);
+ } else {
+ alpha = 0xff000000;
+ }
+
+ int main = alpha | 0x202026;
+ int light = 0xff303036;
+ int dark = 0xff101016;
+ Gui.drawRect(x, y, x+1, y+height, light); //Left
+ Gui.drawRect(x+1, y, x+width, y+1, light); //Top
+ Gui.drawRect(x+width-1, y+1, x+width, y+height, dark); //Right
+ Gui.drawRect(x+1, y+height-1, x+width-1, y+height, dark); //Bottom
+ Gui.drawRect(x+1, y+1, x+width-1, y+height-1, main); //Middle
+ if(shadow) {
+ Gui.drawRect(x+width, y+2, x+width+2, y+height+2, 0x70000000); //Right shadow
+ Gui.drawRect(x+2, y+height, x+width, y+height+2, 0x70000000); //Bottom shadow
+ }
+ }
+
+ public static void drawFloatingRect(int x, int y, int width, int height) {
+ drawFloatingRectWithAlpha(x, y, width, height, 0xFF, true);
+ }
+
+ public static void drawFloatingRectWithAlpha(int x, int y, int width, int height, int alpha, boolean shadow) {
+ int main = (alpha << 24) | 0xc0c0c0;
+ int light = (alpha << 24) | 0xf0f0f0;
+ int dark = (alpha << 24) | 0x909090;
+ Gui.drawRect(x, y, x+1, y+height, light); //Left
+ Gui.drawRect(x+1, y, x+width, y+1, light); //Top
+ Gui.drawRect(x+width-1, y+1, x+width, y+height, dark); //Right
+ Gui.drawRect(x+1, y+height-1, x+width-1, y+height, dark); //Bottom
+ Gui.drawRect(x+1, y+1, x+width-1, y+height-1, main); //Middle
+ if(shadow) {
+ Gui.drawRect(x+width, y+2, x+width+2, y+height+2, (alpha*3/5) << 24); //Right shadow
+ Gui.drawRect(x+2, y+height, x+width, y+height+2, (alpha*3/5) << 24); //Bottom shadow
+ }
+ }
+
+ public static void drawTexturedModalRect(int x, int y, int textureX, int textureY, int width, int height){
+ double f = 0.00390625;
+ double f1 = 0.00390625;
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
+ worldrenderer.pos(x + 0.0, y + height, 0.0).tex((textureX + 0.0) * f, (textureY + height) * f1).endVertex();
+ worldrenderer.pos(x + width, y + height, 0.0).tex((textureX + width) * f, (textureY + height) * f1).endVertex();
+ worldrenderer.pos(x + width, y + 0.0, 0.0).tex((textureX + width) * f, (textureY + 0.0) * f1).endVertex();
+ worldrenderer.pos(x + 0.0, y + 0.0, 0.0).tex((textureX + 0.0) * f, (textureY + 0.0) * f1).endVertex();
+ tessellator.draw();
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height) {
+ drawTexturedRect(x, y, width, height, 0, 1, 0 , 1);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, int filter) {
+ drawTexturedRect(x, y, width, height, 0, 1, 0 , 1, filter);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, float uMin, float uMax, float vMin, float vMax) {
+ drawTexturedRect(x, y, width, height, uMin, uMax, vMin , vMax, GL11.GL_NEAREST);
+ }
+
+ public static void drawTexturedRect(float x, float y, float width, float height, float uMin, float uMax, float vMin, float vMax, int filter) {
+ GlStateManager.enableBlend();
+ GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ drawTexturedRectNoBlend(x, y, width, height, uMin, uMax, vMin, vMax, filter);
+
+ GlStateManager.disableBlend();
+ }
+
+ public static void drawTexturedRectNoBlend(float x, float y, float width, float height, float uMin, float uMax, float vMin, float vMax, int filter) {
+ GlStateManager.enableTexture2D();
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter);
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
+ worldrenderer
+ .pos(x, y+height, 0.0D)
+ .tex(uMin, vMax).endVertex();
+ worldrenderer
+ .pos(x+width, y+height, 0.0D)
+ .tex(uMax, vMax).endVertex();
+ worldrenderer
+ .pos(x+width, y, 0.0D)
+ .tex(uMax, vMin).endVertex();
+ worldrenderer
+ .pos(x, y, 0.0D)
+ .tex(uMin, vMin).endVertex();
+ tessellator.draw();
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
+ }
+
+ public static void drawGradientRect(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) {
+ float startAlpha = (float)(startColor >> 24 & 255) / 255.0F;
+ float startRed = (float)(startColor >> 16 & 255) / 255.0F;
+ float startGreen = (float)(startColor >> 8 & 255) / 255.0F;
+ float startBlue = (float)(startColor & 255) / 255.0F;
+ float endAlpha = (float)(endColor >> 24 & 255) / 255.0F;
+ float endRed = (float)(endColor >> 16 & 255) / 255.0F;
+ float endGreen = (float)(endColor >> 8 & 255) / 255.0F;
+ float endBlue = (float)(endColor & 255) / 255.0F;
+
+ GlStateManager.disableTexture2D();
+ GlStateManager.enableBlend();
+ GlStateManager.disableAlpha();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.shadeModel(7425);
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_COLOR);
+ worldrenderer.pos(right, top, zLevel).color(startRed, startGreen, startBlue, startAlpha).endVertex();
+ worldrenderer.pos(left, top, zLevel).color(startRed, startGreen, startBlue, startAlpha).endVertex();
+ worldrenderer.pos(left, bottom, zLevel).color(endRed, endGreen, endBlue, endAlpha).endVertex();
+ worldrenderer.pos(right, bottom, zLevel).color(endRed, endGreen, endBlue, endAlpha).endVertex();
+ tessellator.draw();
+
+ GlStateManager.shadeModel(7424);
+ GlStateManager.disableBlend();
+ GlStateManager.enableAlpha();
+ GlStateManager.enableTexture2D();
+ }
+
+ public static void drawInnerBox(int left, int top, int width, int height){
+ Gui.drawRect(left, top, left+width, top+height, 0x6008080E); //Middle
+ Gui.drawRect(left, top, left+1, top+height, 0xff08080E); //Left
+ Gui.drawRect(left, top, left+width, top+1, 0xff08080E); //Top
+ Gui.drawRect(left+width-1, top, left+width, top+height, 0xff28282E); //Right
+ Gui.drawRect(left, top+height-1, left+width, top+height, 0xff28282E); //Bottom
+ }
+
+}
diff --git a/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/TextRenderUtils.java b/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/TextRenderUtils.java
new file mode 100644
index 0000000..fce2f82
--- /dev/null
+++ b/src/main/java/com/thatgravyboat/skyblockhud/core/util/render/TextRenderUtils.java
@@ -0,0 +1,215 @@
+package com.thatgravyboat.skyblockhud.core.util.render;
+
+import com.thatgravyboat.skyblockhud.core.util.StringUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.RenderHelper;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import org.lwjgl.opengl.GL11;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TextRenderUtils {
+
+ public static int getCharVertLen(char c) {
+ if("acegmnopqrsuvwxyz".indexOf(c) >= 0) {
+ return 5;
+ } else {
+ return 7;
+ }
+ }
+
+ public static float getVerticalHeight(String str) {
+ str = StringUtils.cleanColour(str);
+ float height = 0;
+ for(int i=0; i<str.length(); i++) {
+ char c = str.charAt(i);
+ int charHeight = getCharVertLen(c);
+ height += charHeight + 1.5f;
+ }
+ return height;
+ }
+
+ public static void drawStringVertical(String str, FontRenderer fr, float x, float y, boolean shadow, int colour) {
+ String format = FontRenderer.getFormatFromString(str);
+ str = StringUtils.cleanColour(str);
+ for(int i=0; i<str.length(); i++) {
+ char c = str.charAt(i);
+
+ int charHeight = getCharVertLen(c);
+ int charWidth = fr.getCharWidth(c);
+ fr.drawString(format+c, x+(5-charWidth)/2f, y-7+charHeight, colour, shadow);
+
+ y += charHeight + 1.5f;
+ }
+ }
+
+ public static void drawStringScaledMaxWidth(String str, FontRenderer fr, float x, float y, boolean shadow, int len, int colour) {
+ int strLen = fr.getStringWidth(str);
+ float factor = len/(float)strLen;
+ factor = Math.min(1, factor);
+
+ drawStringScaled(str, fr, x, y, shadow, colour, factor);
+ }
+
+ public static void drawStringCentered(String str, FontRenderer fr, float x, float y, boolean shadow, int colour) {
+ int strLen = fr.getStringWidth(str);
+
+ float x2 = x - strLen/2f;
+ float y2 = y - fr.FONT_HEIGHT/2f;
+
+ GL11.glTranslatef(x2, y2, 0);
+ fr.drawString(str, 0, 0, colour, shadow);
+ GL11.glTranslatef(-x2, -y2, 0);
+ }
+
+ public static void drawStringScaled(String str, FontRenderer fr, float x, float y, boolean shadow, int colour, float factor) {
+ GlStateManager.scale(factor, factor, 1);
+ fr.drawString(str, x/factor, y/factor, colour, shadow);
+ GlStateManager.scale(1/factor, 1/factor, 1);
+ }
+
+ public static void drawStringCenteredScaledMaxWidth(String str, FontRenderer fr, float x, float y, boolean shadow, int len, int colour) {
+ int strLen = fr.getStringWidth(str);
+ float factor = len/(float)strLen;
+ factor = Math.min(1, factor);
+ int newLen = Math.min(strLen, len);
+
+ float fontHeight = 8*factor;
+
+ drawStringScaled(str, fr, x-newLen/2, y-fontHeight/2, shadow, colour, factor);
+ }
+
+ public static void renderToolTip(ItemStack stack, int mouseX, int mouseY, int screenWidth, int screenHeight, FontRenderer fontStd) {
+ List<String> list = stack.getTooltip(Minecraft.getMinecraft().thePlayer,
+ Minecraft.getMinecraft().gameSettings.advancedItemTooltips);
+
+ for (int i = 0; i < list.size(); ++i) {
+ if (i == 0) {
+ list.set(i, stack.getRarity().rarityColor + list.get(i));
+ } else {
+ list.set(i, EnumChatFormatting.GRAY + list.get(i));
+ }
+ }
+
+ FontRenderer font = stack.getItem().getFontRenderer(stack);
+ drawHoveringText(list, mouseX, mouseY, screenWidth, screenHeight, -1, font == null ? fontStd : font);
+ }
+
+ public static void drawHoveringText(List<String> textLines, final int mouseX, final int mouseY, final int screenWidth, final int screenHeight, final int maxTextWidth, FontRenderer font) {
+ if (!textLines.isEmpty()) {
+ GlStateManager.disableRescaleNormal();
+ RenderHelper.disableStandardItemLighting();
+ GlStateManager.disableLighting();
+ GlStateManager.disableDepth();
+ int tooltipTextWidth = 0;
+
+ for (String textLine : textLines) {
+ int textLineWidth = font.getStringWidth(textLine);
+
+ if (textLineWidth > tooltipTextWidth) {
+ tooltipTextWidth = textLineWidth;
+ }
+ }
+
+ boolean needsWrap = false;
+
+ int titleLinesCount = 1;
+ int tooltipX = mouseX + 12;
+ if (tooltipX + tooltipTextWidth + 4 > screenWidth) {
+ tooltipX = mouseX - 16 - tooltipTextWidth;
+ if (tooltipX < 4) // if the tooltip doesn't fit on the screen
+ {
+ if (mouseX > screenWidth / 2) {
+ tooltipTextWidth = mouseX - 12 - 8;
+ } else {
+ tooltipTextWidth = screenWidth - 16 - mouseX;
+ }
+ needsWrap = true;
+ }
+ }
+
+ if (maxTextWidth > 0 && tooltipTextWidth > maxTextWidth) {
+ tooltipTextWidth = maxTextWidth;
+ needsWrap = true;
+ }
+
+ if (needsWrap) {
+ int wrappedTooltipWidth = 0;
+ List<String> wrappedTextLines = new ArrayList<String>();
+ for (int i = 0; i < textLines.size(); i++) {
+ String textLine = textLines.get(i);
+ List<String> wrappedLine = font.listFormattedStringToWidth(textLine, tooltipTextWidth);
+ if (i == 0) {
+ titleLinesCount = wrappedLine.size();
+ }
+
+ for (String line : wrappedLine) {
+ int lineWidth = font.getStringWidth(line);
+ if (lineWidth > wrappedTooltipWidth) {
+ wrappedTooltipWidth = lineWidth;
+ }
+ wrappedTextLines.add(line);
+ }
+ }
+ tooltipTextWidth = wrappedTooltipWidth;
+ textLines = wrappedTextLines;
+
+ if (mouseX > screenWidth / 2) {
+ tooltipX = mouseX - 16 - tooltipTextWidth;
+ } else {
+ tooltipX = mouseX + 12;
+ }
+ }
+
+ int tooltipY = mouseY - 12;
+ int tooltipHeight = 8;
+
+ if (textLines.size() > 1) {
+ tooltipHeight += (textLines.size() - 1) * 10;
+ if (textLines.size() > titleLinesCount) {
+ tooltipHeight += 2; // gap between title lines and next lines
+ }
+ }
+
+ if (tooltipY + tooltipHeight + 6 > screenHeight) {
+ tooltipY = screenHeight - tooltipHeight - 6;
+ }
+
+ final int zLevel = 300;
+ final int backgroundColor = 0xF0100010;
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 4, tooltipX + tooltipTextWidth + 3, tooltipY - 3, backgroundColor, backgroundColor);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY + tooltipHeight + 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 4, backgroundColor, backgroundColor);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 4, tooltipY - 3, tooltipX - 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor);
+ RenderUtils.drawGradientRect(zLevel, tooltipX + tooltipTextWidth + 3, tooltipY - 3, tooltipX + tooltipTextWidth + 4, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor);
+ final int borderColorStart = 0x505000FF;
+ final int borderColorEnd = (borderColorStart & 0xFEFEFE) >> 1 | borderColorStart & 0xFF000000;
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd);
+ RenderUtils.drawGradientRect(zLevel, tooltipX + tooltipTextWidth + 2, tooltipY - 3 + 1, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY - 3 + 1, borderColorStart, borderColorStart);
+ RenderUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY + tooltipHeight + 2, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, borderColorEnd, borderColorEnd);
+
+ for (int lineNumber = 0; lineNumber < textLines.size(); ++lineNumber) {
+ String line = textLines.get(lineNumber);
+ font.drawStringWithShadow(line, (float) tooltipX, (float) tooltipY, -1);
+
+ if (lineNumber + 1 == titleLinesCount) {
+ tooltipY += 2;
+ }
+
+ tooltipY += 10;
+ }
+
+ GlStateManager.enableLighting();
+ GlStateManager.enableDepth();
+ RenderHelper.enableStandardItemLighting();
+ GlStateManager.enableRescaleNormal();
+ }
+ GlStateManager.disableLighting();
+ }
+
+}