From ba0a16458329fb9a5a8966b3c665ea72151a9538 Mon Sep 17 00:00:00 2001 From: charkholl Date: Sun, 25 Oct 2026 10:48:43 -0400 Subject: [PATCH 1/4] Fix ClickGUI alpha, scrolling labels, and numeric sliders --- .../java/dev/goldforge/ConfigManager.java | 7 + .../dev/goldforge/gui/BackspaceMainMenu.java | 4 +- .../dev/goldforge/gui/GuiPreferences.java | 8 +- .../dev/goldforge/gui/ModernClickGui.java | 4 +- .../java/dev/goldforge/gui/PixelClickGui.java | 123 ++++++++++++++++-- .../dev/goldforge/gui/PixelGuiEditor.java | 46 +++++-- .../java/dev/goldforge/module/Module.java | 7 + .../dev/goldforge/module/ModuleSetting.java | 55 ++++++-- .../goldforge/module/impl/AntiLevitate.java | 4 +- .../dev/goldforge/module/impl/ElytraFly.java | 16 ++- 10 files changed, 230 insertions(+), 44 deletions(-) diff --git a/src/main/java/dev/goldforge/ConfigManager.java b/src/main/java/dev/goldforge/ConfigManager.java index 85a82f7..2a43dc2 100644 --- a/src/main/java/dev/goldforge/ConfigManager.java +++ b/src/main/java/dev/goldforge/ConfigManager.java @@ -41,6 +41,8 @@ public static synchronized void load(){ String key="module."+module.name+"."+field.getName(); try{ if(field.getType()==int.class&&VALUES.containsKey(key))field.setInt(module,parseInt(key,field.getInt(module))); + else if(field.getType()==float.class&&VALUES.containsKey(key))field.setFloat(module,parseFloat(key,field.getFloat(module))); + else if(field.getType()==double.class&&VALUES.containsKey(key))field.setDouble(module,parseDouble(key,field.getDouble(module))); else if(field.getType()==boolean.class&&VALUES.containsKey(key))field.setBoolean(module,Boolean.parseBoolean(VALUES.getProperty(key))); else if(field.getType()==String.class&&VALUES.containsKey(key))field.set(module,VALUES.getProperty(key,"")); }catch(IllegalAccessException ignored){} @@ -74,6 +76,8 @@ public static synchronized void save(){ try{ String key="module."+module.name+"."+field.getName(); if(field.getType()==int.class)VALUES.setProperty(key,Integer.toString(field.getInt(module))); + else if(field.getType()==float.class)VALUES.setProperty(key,Float.toString(field.getFloat(module))); + else if(field.getType()==double.class)VALUES.setProperty(key,Double.toString(field.getDouble(module))); else if(field.getType()==boolean.class)VALUES.setProperty(key,Boolean.toString(field.getBoolean(module))); else if(field.getType()==String.class)VALUES.setProperty(key,(String)field.get(module)); }catch(IllegalAccessException ignored){} @@ -132,5 +136,8 @@ private static Path profilePath(String name){ private static String serverProfileName(){net.minecraft.client.multiplayer.ServerData server=Minecraft.getMinecraft().getCurrentServerData();if(server==null||server.serverIP==null||server.serverIP.trim().isEmpty())return null;return "server-"+String.format(java.util.Locale.ROOT,"%08x",server.serverIP.trim().toLowerCase(java.util.Locale.ROOT).hashCode());} private static int getInt(String key,int fallback){return parseInt(key,fallback);} private static int parseInt(String key,int fallback){try{return Integer.parseInt(VALUES.getProperty(key));}catch(Exception ignored){return fallback;}} + private static float parseFloat(String key,float fallback){try{return Float.parseFloat(VALUES.getProperty(key));}catch(Exception ignored){return fallback;}} + private static double parseDouble(String key,double fallback){try{return Double.parseDouble(VALUES.getProperty(key));}catch(Exception ignored){return fallback;}} private static boolean getBoolean(String key,boolean fallback){String value=VALUES.getProperty(key);return value==null?fallback:Boolean.parseBoolean(value);} } + diff --git a/src/main/java/dev/goldforge/gui/BackspaceMainMenu.java b/src/main/java/dev/goldforge/gui/BackspaceMainMenu.java index 0ca8594..3a10337 100644 --- a/src/main/java/dev/goldforge/gui/BackspaceMainMenu.java +++ b/src/main/java/dev/goldforge/gui/BackspaceMainMenu.java @@ -95,7 +95,8 @@ public final class BackspaceMainMenu extends GuiScreen { } GlStateManager.popMatrix(); if (height >= 300) { - label("DEV 3.5 BETA", left + 2, (int)(titleY + 34F), fadeAlpha(MUTED, menuFade), 9F); + label(GoldForge.VERSION.toUpperCase(java.util.Locale.ROOT).replace('-', ' '), + left + 2, (int)(titleY + 34F), fadeAlpha(MUTED, menuFade), 9F); label("A CLEANER WAY TO PLAY", left + 2, (int)(titleY + 54F), fadeAlpha(0xFF777A83, menuFade), 9F); } @@ -394,3 +395,4 @@ private static int blendColor(int from, int to, float amount) { } private static int fadeAlpha(int color, float alpha) { return (Math.max(0, Math.min(255, Math.round(((color >>> 24) & 255) * alpha))) << 24) | (color & 0x00FFFFFF); } } + diff --git a/src/main/java/dev/goldforge/gui/GuiPreferences.java b/src/main/java/dev/goldforge/gui/GuiPreferences.java index 854c059..422146d 100644 --- a/src/main/java/dev/goldforge/gui/GuiPreferences.java +++ b/src/main/java/dev/goldforge/gui/GuiPreferences.java @@ -7,7 +7,7 @@ public final class GuiPreferences { public static int layout = 1; public static int theme = 0; // gold, red, mint, blue - public static int pixelOpacity = 160; + public static int pixelTransparency = 37; // 0 is opaque; 100 reveals the world public static final int[] panelX = new int[Category.values().length]; public static final int[] panelY = new int[Category.values().length]; public static final boolean[] collapsed = new boolean[Category.values().length]; @@ -17,7 +17,9 @@ private GuiPreferences() {} public static void load(Properties values) { layout = clamp(read(values, "gui.layout", layout), 0, 1); theme = clamp(read(values, "gui.theme", theme), 0, 3); - pixelOpacity = clamp(read(values, "gui.pixel.opacity", pixelOpacity), 32, 255); + pixelTransparency = values.containsKey("gui.pixel.transparency") + ? clamp(read(values, "gui.pixel.transparency", pixelTransparency), 0, 100) + : clamp(100 - Math.round(read(values, "gui.pixel.opacity", 160) * 100F / 255F), 0, 100); for (Category category : Category.values()) { String prefix = "gui.panel." + category.name().toLowerCase(java.util.Locale.ROOT) + "."; int index = category.ordinal(); @@ -29,7 +31,7 @@ public static void load(Properties values) { public static void save(Properties values) { values.setProperty("gui.layout", Integer.toString(clamp(layout, 0, 1))); values.setProperty("gui.theme", Integer.toString(clamp(theme, 0, 3))); - values.setProperty("gui.pixel.opacity", Integer.toString(clamp(pixelOpacity, 32, 255))); + values.setProperty("gui.pixel.transparency", Integer.toString(clamp(pixelTransparency, 0, 100))); for (Category category : Category.values()) { String prefix = "gui.panel." + category.name().toLowerCase(java.util.Locale.ROOT) + "."; int index = category.ordinal(); diff --git a/src/main/java/dev/goldforge/gui/ModernClickGui.java b/src/main/java/dev/goldforge/gui/ModernClickGui.java index 605a81c..028f0eb 100644 --- a/src/main/java/dev/goldforge/gui/ModernClickGui.java +++ b/src/main/java/dev/goldforge/gui/ModernClickGui.java @@ -156,7 +156,8 @@ private void drawBrand(long vg, float x, float y) { float spaceX = titleX + nvg.getTextWidth(vg, "back", HEADER_TITLE_SIZE, Fonts.REGULAR); text(vg, "back", titleX, titleY, HEADER_TITLE_SIZE, TEXT); text(vg, "space", spaceX, titleY, HEADER_TITLE_SIZE, YELLOW); - text(vg, "DEV 3.5 BETA", titleX, titleY + HEADER_SUBTITLE_GAP, HEADER_SUBTITLE_SIZE, MUTED); + text(vg, GoldForge.VERSION.toUpperCase(java.util.Locale.ROOT).replace('-', ' '), + titleX, titleY + HEADER_SUBTITLE_GAP, HEADER_SUBTITLE_SIZE, MUTED); } private void drawHeader(long vg, InputHandler input, float x, float y, float width) { @@ -550,3 +551,4 @@ private void text(long vg, String value, float x, float y, float size, int color private void textCentered(long vg, String value, float x, float y, float size, int color) { NanoVGHelper.INSTANCE.drawCenteredText(vg, value, x, y, color, size, Fonts.REGULAR); } private void textRight(long vg, String value, float right, float y, float size, int color) { text(vg, value, right - NanoVGHelper.INSTANCE.getTextWidth(vg, value, size, Fonts.REGULAR), y, size, color); } } + diff --git a/src/main/java/dev/goldforge/gui/PixelClickGui.java b/src/main/java/dev/goldforge/gui/PixelClickGui.java index 45cb175..69b050b 100644 --- a/src/main/java/dev/goldforge/gui/PixelClickGui.java +++ b/src/main/java/dev/goldforge/gui/PixelClickGui.java @@ -9,11 +9,14 @@ 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.BufferUtils; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; import java.io.IOException; +import java.nio.IntBuffer; import java.util.Collections; import java.util.EnumMap; import java.util.IdentityHashMap; @@ -27,10 +30,21 @@ public final class PixelClickGui extends GuiScreen { private final EnumMap scroll = new EnumMap(Category.class); private final Set expanded = Collections.newSetFromMap(new IdentityHashMap()); private final IdentityHashMap animated = new IdentityHashMap(); + private final IdentityHashMap nameMarquees = new IdentityHashMap(); + private final IdentityHashMap descriptionMarquees = new IdentityHashMap(); + private final IdentityHashMap sliderHover = new IdentityHashMap(); private int page; private Module binding; + private ModuleSetting activeSlider; + private int activeSliderX, activeSliderWidth; + private float frameSeconds; private long lastFrame; + private static final class Marquee { + float offset, timer; + int phase; + } + public PixelClickGui() { this(null); } public PixelClickGui(GuiScreen parent) { this.parent = parent; } @@ -55,7 +69,10 @@ private int edge() { default: return 0xFF544916; } } - private int surface() { return (GuiPreferences.pixelOpacity << 24) | 0x141515; } + private int backgroundAlpha() { + return Math.round(255F * (100 - GuiPreferences.pixelTransparency) / 100F); + } + private int surface() { return (backgroundAlpha() << 24) | 0x141515; } private void box(int x, int y, int w, int h, int fill) { if (h <= 0) return; Gui.drawRect(x, y, x + w, y + h, edge()); @@ -64,7 +81,47 @@ private void box(int x, int y, int w, int h, int fill) { private void text(String value, int x, int y, int color) { fontRenderer.drawString(value, x, y, color); } - private String fit(String value, int max) { return fontRenderer.trimStringToWidth(value, max); } + private int[] clipText(int x, int y, int w, int h) { + IntBuffer old = BufferUtils.createIntBuffer(4); + GL11.glGetInteger(GL11.GL_SCISSOR_BOX, old); + int scale = new ScaledResolution(mc).getScaleFactor(); + int lx = x * scale, ly = mc.displayHeight - (y + h) * scale; + int rx = lx + w * scale, ty = ly + h * scale; + lx = Math.max(lx, old.get(0)); + ly = Math.max(ly, old.get(1)); + rx = Math.min(rx, old.get(0) + old.get(2)); + ty = Math.min(ty, old.get(1) + old.get(3)); + GL11.glScissor(lx, ly, Math.max(0, rx - lx), Math.max(0, ty - ly)); + return new int[] {old.get(0), old.get(1), old.get(2), old.get(3)}; + } + private void marquee(Object key, IdentityHashMap states, + String value, int x, int y, int available, boolean hovered, int color) { + int overflow = fontRenderer.getStringWidth(value) - available; + if (overflow <= 0) { text(value, x, y, color); return; } + Marquee state = states.get(key); + if (state == null) { state = new Marquee(); states.put(key, state); } + float dt = frameSeconds; + if (!hovered) { + state.phase = 0; state.timer = 0; + state.offset *= (float) Math.exp(-11F * dt); + if (state.offset < 0.5F) state.offset = 0; + } else if (state.phase == 0) { + state.timer += dt; + if (state.timer >= 0.45F) { state.phase = 1; state.timer = 0; } + } else if (state.phase == 1) { + state.offset += (overflow - state.offset) * (1F - (float) Math.exp(-2.8F * dt)); + if (overflow - state.offset < 0.5F) { state.offset = overflow; state.phase = 2; state.timer = 0; } + } else if (state.phase == 2) { + state.timer += dt; + if (state.timer >= 0.8F) state.phase = 3; + } else { + state.offset *= (float) Math.exp(-5F * dt); + if (state.offset < 0.5F) { state.offset = 0; state.phase = 0; state.timer = 0; } + } + int[] old = clipText(x, y, available, 10); + text(value, x - Math.round(state.offset), y, color); + GL11.glScissor(old[0], old[1], old[2], old[3]); + } private int scroll(Category category) { Integer value = scroll.get(category); return value == null ? 0 : value; @@ -90,6 +147,7 @@ private int maxScroll(List modules) { private void updateAnimations(float partial) { long now = System.nanoTime(); float dt = lastFrame == 0 ? 0 : Math.min(0.05F, (now - lastFrame) / 1000000000F); + frameSeconds = dt; lastFrame = now; float ease = 1F - (float) Math.exp(-14F * dt); for (Category category : Category.values()) { @@ -111,6 +169,9 @@ private void clip(int x, int y, int w, int h) { @Override public void drawScreen(int mx, int my, float partial) { updateAnimations(partial); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, + GL11.GL_ONE, GL11.GL_ZERO); // The world is left untouched; the opacity preference controls the actual window fill. text("BACKSPACE", 8, 8, accent()); text("< CATEGORIES >", Math.max(8, width - 100), 8, 0xFFE0E0D0); @@ -135,22 +196,25 @@ private void drawColumn(Category category, int x, int mx, int my) { text(category.name(), x + 5, TOP + 5, 0xFF101010); text(Integer.toString(modules.size()), x + W - 18, TOP + 5, 0xFF101010); if (body <= 0) return; + GlStateManager.disableAlpha(); box(x, bodyTop(), W, body, surface()); + GlStateManager.enableAlpha(); clip(x + 1, bodyTop() + 1, W - 2, Math.max(0, body - 2)); int y = bodyTop() + 1 - offset; for (Module module : modules) { if (y + ROW > bodyTop() && y < bodyTop() + body) { boolean hover = mx >= x && mx < x + W && my >= y && my < y + ROW; - if (hover) Gui.drawRect(x + 1, y, x + W - 1, y + ROW, 0x556B591C); + if (hover) Gui.drawRect(x + 1, y, x + W - 1, y + ROW, + (backgroundAlpha() * 0x55 / 255 << 24) | 0x6B591C); if (module.enabled) Gui.drawRect(x + 1, y, x + 3, y + ROW, accent()); - text(fit(module.name, W - 27), x + 6, y + 4, + marquee(module, nameMarquees, module.name, x + 6, y + 4, W - 27, hover, module.enabled ? accent() : 0xFFE5E5D6); circle(x + W - 12, y + 8, module.enabled); } y += ROW; int h = panelHeight(module); if (h > 0 && y + h > bodyTop() && y < bodyTop() + body) - drawSettings(module, x, y, h, body); + drawSettings(module, x, y, h, body, mx, my); y += h; } GL11.glDisable(GL11.GL_SCISSOR_TEST); @@ -170,7 +234,7 @@ private void circle(int cx, int cy, boolean enabled) { if (enabled) Gui.drawRect(cx - 2, cy - 2, cx + 2, cy + 1, accent()); } - private void drawSettings(Module module, int x, int y, int h, int body) { + private void drawSettings(Module module, int x, int y, int h, int body, int mx, int my) { int top = Math.max(y, bodyTop() + 1); int end = Math.min(y + h, bodyTop() + body - 1); if (end <= top) return; @@ -178,7 +242,8 @@ private void drawSettings(Module module, int x, int y, int h, int body) { // The category body supplies the only translucent fill. A second fill here // would compound its alpha and make open settings appear more opaque. Gui.drawRect(x + 4, y + 1, x + 6, y + h - 1, accent()); - text(fit(module.description, W - 19), x + 9, y + 4, 0xFFE4E4D8); + marquee(module, descriptionMarquees, module.description, x + 9, y + 4, W - 19, + mx >= x + 9 && mx < x + W - 9 && my >= y && my < y + 15, 0xFFE4E4D8); Gui.drawRect(x + 9, y + 16, x + W - 9, y + 30, module.enabled ? accent() : 0xB0494944); text(module.enabled ? "ENABLED" : "DISABLED", x + 13, y + 19, @@ -187,11 +252,27 @@ private void drawSettings(Module module, int x, int y, int h, int body) { for (int i = 0; i < options.size(); i++) { ModuleSetting option = options.get(i); int sy = y + 34 + i * 18; - Gui.drawRect(x + 9, sy, x + W - 9, sy + 17, 0xA02A2A28); - text(fit(option.name, 64), x + 12, sy + 4, 0xFFE8E7D9); - text("-", x + W - 57, sy + 4, accent()); - text(Integer.toString(option.get()), x + W - 43, sy + 4, accent()); - text("+", x + W - 18, sy + 4, accent()); + boolean hover = mx >= x + 9 && mx < x + W - 9 && my >= sy && my < sy + 17; + Float oldHover = sliderHover.get(option); + float glow = (oldHover == null ? 0 : oldHover); + glow += ((hover || activeSlider == option ? 1F : 0F) - glow) + * (1F - (float) Math.exp(-12F * frameSeconds)); + sliderHover.put(option, glow); + String value = option.formattedValue(); + int valueWidth = fontRenderer.getStringWidth(value); + int nameWidth = Math.max(18, W - 33 - valueWidth); + marquee(option, nameMarquees, option.name, x + 12, sy + 1, nameWidth, + hover, 0xFFE8E7D9); + text(value, x + W - 12 - valueWidth, sy + 1, accent()); + int trackX = x + 12, trackWidth = W - 24; + Gui.drawRect(trackX, sy + 13, trackX + trackWidth, sy + 17, 0xFF292923); + int filled = Math.round(trackWidth * (float) option.fraction()); + Gui.drawRect(trackX, sy + 13, trackX + filled, sy + 17, accent()); + int knob = trackX + filled; + Gui.drawRect(Math.max(trackX, knob - 2), sy + 11, + Math.min(trackX + trackWidth, knob + 2), sy + 18, accent()); + if (glow > 0.02F) Gui.drawRect(trackX, sy + 10, trackX + trackWidth, sy + 11, + (Math.round(glow * 120F) << 24) | (accent() & 0xFFFFFF)); } int keyY = y + 34 + options.size() * 18; Gui.drawRect(x + 9, keyY, x + W - 9, keyY + 16, 0xA0423C23); @@ -218,8 +299,11 @@ private void settingsClick(Module module, int x, int y, int h, int mx, int my) { List options = module.getSettings(); for (int i = 0; i < options.size(); i++) { int sy = y + 34 + i * 18; - if (my >= sy && my < sy + 17 && mx >= x + W - 62 && mx < x + W - 9) { - options.get(i).adjust(mx < x + W - 31 ? -1 : 1); + if (my >= sy + 10 && my < sy + 18 && mx >= x + 12 && mx <= x + W - 12) { + activeSlider = options.get(i); + activeSliderX = x + 12; + activeSliderWidth = W - 24; + activeSlider.setFraction((mx - activeSliderX) / (double) activeSliderWidth); return; } } @@ -228,6 +312,17 @@ private void settingsClick(Module module, int x, int y, int h, int mx, int my) { if (special(module) && my >= keyY + 17 && my < keyY + 34) openEditor(module); } + @Override protected void mouseClickMove(int mx, int my, int button, long time) { + if (button == 0 && activeSlider != null) + activeSlider.setFraction((mx - activeSliderX) / (double) activeSliderWidth); + } + @Override protected void mouseReleased(int mx, int my, int button) { + if (button == 0 && activeSlider != null) { + activeSlider = null; + ConfigManager.save(); + } + } + @Override protected void mouseClicked(int mx, int my, int button) throws IOException { if (my >= height - 19 && my < height - 3) { if (mx >= width - 182 && mx < width - 98) { diff --git a/src/main/java/dev/goldforge/gui/PixelGuiEditor.java b/src/main/java/dev/goldforge/gui/PixelGuiEditor.java index d9f7059..74b1cbe 100644 --- a/src/main/java/dev/goldforge/gui/PixelGuiEditor.java +++ b/src/main/java/dev/goldforge/gui/PixelGuiEditor.java @@ -3,7 +3,9 @@ import dev.goldforge.ConfigManager; import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; import java.io.IOException; @@ -12,6 +14,7 @@ public final class PixelGuiEditor extends GuiScreen { private static final String[] THEMES = {"GOLD", "RED", "MINT", "BLUE"}; private static final int[] ACCENTS = {0xFFF4CE38, 0xFFE35D65, 0xFF6DE0BB, 0xFF75BBF7}; private final GuiScreen parent; + private boolean draggingTransparency; public PixelGuiEditor(GuiScreen parent) { this.parent = parent; } @@ -25,7 +28,9 @@ private void box(int x, int y, int w, int h, int color) { Gui.drawRect(x + 1, y + 1, x + w - 1, y + h - 1, color); } @Override public void drawScreen(int mx, int my, float partialTicks) { - Gui.drawRect(0, 0, width, height, 0x89000000); + if (parent != null) parent.drawScreen(mx, my, partialTicks); + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); int x = x(), y = y(); box(x, y, 260, 180, 0xF0151515); Gui.drawRect(x + 1, y + 1, x + 259, y + 23, ACCENTS[GuiPreferences.theme]); @@ -39,18 +44,23 @@ private void box(int x, int y, int w, int h, int color) { if (GuiPreferences.theme == i) Gui.drawRect(bx, y + 74, bx + 56, y + 76, ACCENTS[i]); } - label("WINDOW OPACITY", x + 10, y + 87, 0xFFE7E7DF); + label("WINDOW TRANSPARENCY", x + 10, y + 87, 0xFFE7E7DF); box(x + 10, y + 102, 25, 22, 0xFF444444); label("-", x + 20, y + 109, 0xFFFFFFFF); box(x + 224, y + 102, 25, 22, 0xFF444444); label("+", x + 233, y + 109, 0xFFFFFFFF); Gui.drawRect(x + 42, y + 109, x + 217, y + 117, 0xFF343434); - int fill = 175 * GuiPreferences.pixelOpacity / 255; + int fill = 175 * GuiPreferences.pixelTransparency / 100; Gui.drawRect(x + 42, y + 109, x + 42 + fill, y + 117, ACCENTS[GuiPreferences.theme]); - label(Integer.toString(Math.round(GuiPreferences.pixelOpacity * 100F / 255F)) + "%", + label(GuiPreferences.pixelTransparency + "%", x + 113, y + 127, 0xFFE7E7DF); - int preview = (GuiPreferences.pixelOpacity << 24) | 0x141515; - box(x + 10, y + 148, 239, 23, preview); + box(x + 10, y + 148, 239, 23, 0xFF808080); + for (int px = x + 11; px < x + 248; px += 10) + for (int py = y + 149; py < y + 170; py += 10) + if (((px - x) / 10 + (py - y) / 10) % 2 == 0) + Gui.drawRect(px, py, Math.min(px + 10, x + 248), Math.min(py + 10, y + 170), 0xFFC0C0C0); + int alpha = Math.round(255F * (100 - GuiPreferences.pixelTransparency) / 100F); + Gui.drawRect(x + 11, y + 149, x + 248, y + 170, (alpha << 24) | 0x141515); label("PREVIEW " + THEMES[GuiPreferences.theme], x + 17, y + 155, ACCENTS[GuiPreferences.theme]); } @@ -69,12 +79,26 @@ private void box(int x, int y, int w, int h, int color) { } if (my >= y + 102 && my < y + 124) { if (mx >= x + 10 && mx < x + 35) - GuiPreferences.pixelOpacity = Math.max(32, GuiPreferences.pixelOpacity - 16); + GuiPreferences.pixelTransparency = Math.max(0, GuiPreferences.pixelTransparency - 5); else if (mx >= x + 224 && mx < x + 249) - GuiPreferences.pixelOpacity = Math.min(255, GuiPreferences.pixelOpacity + 16); - else if (mx >= x + 42 && mx < x + 217) - GuiPreferences.pixelOpacity = Math.max(32, Math.min(255, - (mx - x - 42) * 255 / 175)); + GuiPreferences.pixelTransparency = Math.min(100, GuiPreferences.pixelTransparency + 5); + else if (mx >= x + 42 && mx < x + 217) { + draggingTransparency = true; + setTransparency(mx); + } + ConfigManager.save(); + } + } + private void setTransparency(int mx) { + GuiPreferences.pixelTransparency = Math.max(0, Math.min(100, + Math.round((mx - x() - 42) * 100F / 175F))); + } + @Override protected void mouseClickMove(int mx, int my, int button, long time) { + if (button == 0 && draggingTransparency) setTransparency(mx); + } + @Override protected void mouseReleased(int mx, int my, int button) { + if (button == 0 && draggingTransparency) { + draggingTransparency = false; ConfigManager.save(); } } diff --git a/src/main/java/dev/goldforge/module/Module.java b/src/main/java/dev/goldforge/module/Module.java index 75ef0f7..e08661e 100644 --- a/src/main/java/dev/goldforge/module/Module.java +++ b/src/main/java/dev/goldforge/module/Module.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.function.IntConsumer; import java.util.function.IntSupplier; +import java.util.function.DoubleConsumer; +import java.util.function.DoubleSupplier; public abstract class Module { protected final Minecraft mc = Minecraft.getMinecraft(); @@ -64,5 +66,10 @@ final void resumeAfterWorldChange(){ protected final void addIntSetting(String name,int min,int max,int step,IntSupplier getter,IntConsumer setter) { settings.add(new ModuleSetting(name,min,max,step,getter,setter)); } + protected final void addDoubleSetting(String name,double min,double max,double step, + DoubleSupplier getter,DoubleConsumer setter) { + settings.add(new ModuleSetting(name,min,max,step,getter,setter)); + } public final List getSettings() { return Collections.unmodifiableList(settings); } } + diff --git a/src/main/java/dev/goldforge/module/ModuleSetting.java b/src/main/java/dev/goldforge/module/ModuleSetting.java index 6322097..3094e53 100644 --- a/src/main/java/dev/goldforge/module/ModuleSetting.java +++ b/src/main/java/dev/goldforge/module/ModuleSetting.java @@ -1,18 +1,57 @@ package dev.goldforge.module; +import dev.goldforge.ConfigManager; +import java.math.BigDecimal; +import java.util.function.DoubleConsumer; +import java.util.function.DoubleSupplier; import java.util.function.IntConsumer; import java.util.function.IntSupplier; -/** Small GUI-editable integer option owned by a module. */ +/** Numeric module option with a bounded, step-aligned slider value. */ public final class ModuleSetting { public final String name; - private final int min, max, step; - private final IntSupplier getter; - private final IntConsumer setter; + private final double min, max, step; + private final DoubleSupplier getter; + private final DoubleConsumer setter; + private final boolean integral; - public ModuleSetting(String name, int min, int max, int step, IntSupplier getter, IntConsumer setter) { - this.name=name; this.min=min; this.max=max; this.step=step; this.getter=getter; this.setter=setter; + public ModuleSetting(String name, int min, int max, int step, final IntSupplier getter, final IntConsumer setter) { + this(name, min, max, step, () -> getter.getAsInt(), value -> setter.accept((int) Math.round(value)), true); + } + public ModuleSetting(String name, double min, double max, double step, DoubleSupplier getter, DoubleConsumer setter) { + this(name, min, max, step, getter, setter, false); + } + private ModuleSetting(String name, double min, double max, double step, + DoubleSupplier getter, DoubleConsumer setter, boolean integral) { + if (max < min || step <= 0 || !Double.isFinite(min) || !Double.isFinite(max) || !Double.isFinite(step)) + throw new IllegalArgumentException("Invalid numeric setting range: " + name); + this.name = name; + this.min = min; + this.max = max; + this.step = step; + this.getter = getter; + this.setter = setter; + this.integral = integral; + } + public int get() { return (int) Math.round(getter.getAsDouble()); } + public double getDouble() { return getter.getAsDouble(); } + public double fraction() { return max == min ? 0 : Math.max(0, Math.min(1, (getDouble() - min) / (max - min))); } + public String formattedValue() { + double value = getDouble(); + if (integral) return Integer.toString((int) Math.round(value)); + return BigDecimal.valueOf(value).setScale(Math.max(0, BigDecimal.valueOf(step).stripTrailingZeros().scale()), + java.math.RoundingMode.HALF_UP).stripTrailingZeros().toPlainString(); + } + public void setFraction(double fraction) { + double bounded = Math.max(0, Math.min(1, fraction)); + long increments = Math.round(((max - min) * bounded) / step); + double value = BigDecimal.valueOf(min).add(BigDecimal.valueOf(step).multiply(BigDecimal.valueOf(increments))).doubleValue(); + value = bounded >= 1 ? max : Math.max(min, Math.min(max, value)); + if (Math.abs(value - getDouble()) > 0.0000001) setter.accept(value); + } + public void adjust(int direction) { + double value = Math.max(min, Math.min(max, getDouble() + step * direction)); + if (Math.abs(value - getDouble()) > 0.0000001) { setter.accept(value); ConfigManager.save(); } } - public int get() { return getter.getAsInt(); } - public void adjust(int direction) { setter.accept(Math.max(min,Math.min(max,get()+step*direction))); dev.goldforge.ConfigManager.save(); } } + diff --git a/src/main/java/dev/goldforge/module/impl/AntiLevitate.java b/src/main/java/dev/goldforge/module/impl/AntiLevitate.java index dc52546..134a758 100644 --- a/src/main/java/dev/goldforge/module/impl/AntiLevitate.java +++ b/src/main/java/dev/goldforge/module/impl/AntiLevitate.java @@ -13,7 +13,8 @@ public AntiLevitate(){ super("AntiLevitate","Detects Levitation and can cap local upward motion while it is active.",Category.MOVEMENT,0); addIntSetting("Mode (0 notify, 1 cap)",0,1,1,()->mode,v->mode=v); addIntSetting("Notifications (0/1)",0,1,1,()->notify,v->notify=v); - addIntSetting("Upward cap (tenths)",0,10,1,()->upwardCapTenths,v->upwardCapTenths=v); + addDoubleSetting("Upward cap",0,1,0.1, + ()->upwardCapTenths/10.0,v->upwardCapTenths=(int)Math.round(v*10)); } @Override public void tick(){ if(mc.player==null||mc.world==null){wasLevitation=false;return;} @@ -25,3 +26,4 @@ public AntiLevitate(){ } @Override protected void onDisable(){wasLevitation=false;} } + diff --git a/src/main/java/dev/goldforge/module/impl/ElytraFly.java b/src/main/java/dev/goldforge/module/impl/ElytraFly.java index 14a74e4..833277a 100644 --- a/src/main/java/dev/goldforge/module/impl/ElytraFly.java +++ b/src/main/java/dev/goldforge/module/impl/ElytraFly.java @@ -26,11 +26,16 @@ public final class ElytraFly extends Module { public ElytraFly() { super("ElytraFly", "Configurable Elytra steering and packet movement profiles.", Category.MOVEMENT, 0); addIntSetting("Mode (0 Control, 1 Packet, 2 Both)", 0, 2, 1, () -> mode, v -> mode = v); - addIntSetting("Horizontal speed (x100)", 20, 500, 5, () -> horizontalSpeed, v -> horizontalSpeed = v); - addIntSetting("Steering (x1000)", 1, 200, 1, () -> steering, v -> steering = v); - addIntSetting("Vertical input speed (x100)", 5, 200, 5, () -> verticalSpeed, v -> verticalSpeed = v); - addIntSetting("Packet multiplier (x100)", 100, 400, 5, () -> packetMultiplier, v -> packetMultiplier = v); - addIntSetting("Packet pulse (x10000)", 0, 2000, 25, () -> packetPulse, v -> packetPulse = v); + addDoubleSetting("Horizontal speed", 0.2, 5, 0.05, + () -> horizontalSpeed / 100.0, v -> horizontalSpeed = (int) Math.round(v * 100)); + addDoubleSetting("Steering", 0.001, 0.2, 0.001, + () -> steering / 1000.0, v -> steering = (int) Math.round(v * 1000)); + addDoubleSetting("Vertical speed", 0.05, 2, 0.05, + () -> verticalSpeed / 100.0, v -> verticalSpeed = (int) Math.round(v * 100)); + addDoubleSetting("Packet multiplier", 1, 4, 0.05, + () -> packetMultiplier / 100.0, v -> packetMultiplier = (int) Math.round(v * 100)); + addDoubleSetting("Packet pulse", 0, 0.2, 0.0025, + () -> packetPulse / 10000.0, v -> packetPulse = (int) Math.round(v * 10000)); addIntSetting("Spoof onGround (0/1)", 0, 1, 1, () -> spoofGround, v -> spoofGround = v); } @@ -105,3 +110,4 @@ private static int clamp(int value, int min, int max) { return Math.max(min, Math.min(max, value)); } } + From e6bd54d2afc773bb847ec37310a8368c4e159f67 Mon Sep 17 00:00:00 2001 From: charkholl Date: Sun, 25 Oct 2026 10:50:52 -0400 Subject: [PATCH 2/4] Use draggable numeric sliders in the modern browser too --- .../dev/goldforge/gui/ModernClickGui.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/dev/goldforge/gui/ModernClickGui.java b/src/main/java/dev/goldforge/gui/ModernClickGui.java index 028f0eb..8585a57 100644 --- a/src/main/java/dev/goldforge/gui/ModernClickGui.java +++ b/src/main/java/dev/goldforge/gui/ModernClickGui.java @@ -20,6 +20,7 @@ import dev.goldforge.module.impl.ShaderFX; import net.minecraft.client.gui.GuiScreen; import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; import java.util.ArrayList; @@ -66,6 +67,8 @@ public final class ModernClickGui extends OneUIScreen { private int settingsScroll; private boolean inspectorHovered; private String hoveredSettingName; + private ModuleSetting draggingSetting; + private float draggingTrackX, draggingTrackWidth; private long openedAt; public ModernClickGui() { this(null); } @@ -86,6 +89,12 @@ public ModernClickGui(GuiScreen parent) { @Override public void draw(long vg, float partialTicks, InputHandler input) { NanoVGHelper nvg = NanoVGHelper.INSTANCE; + if (draggingSetting != null) { + if (Mouse.isButtonDown(0)) + draggingSetting.setFraction((Mouse.getX() * width / (double) mc.displayWidth + - draggingTrackX) / draggingTrackWidth); + else { ConfigManager.save(); draggingSetting = null; } + } float alpha = Math.min(1F, (System.currentTimeMillis() - openedAt) / 170F); // Keep the browser substantial without turning it into a full-screen // overlay at lower Minecraft GUI scales. @@ -356,12 +365,19 @@ private void drawInspector(long vg, InputHandler input, float x, float y, float if (input.isAreaHovered(x + 14F, sy, w - 28F, 31F)) hoveredSettingName = setting.name; String rowLabel = selectedModule instanceof Phase && "Phase mode".equals(setting.name) ? "Mode: " + Phase.modeName(((Phase) selectedModule).mode) : setting.name; - text(vg, trim(vg, rowLabel, w - 112F, 11F), x + 24F, sy + 10F, 11F, TEXT); - text(vg, "−", x + w - 80F, sy + 7F, 16F, YELLOW); - textRight(vg, Integer.toString(setting.get()), x + w - 42F, sy + 10F, 11F, TEXT); - text(vg, "+", x + w - 30F, sy + 7F, 16F, YELLOW); - if (input.isAreaClicked(x + w - 89F, sy, 25F, 31F)) setting.adjust(-1); - else if (input.isAreaClicked(x + w - 34F, sy, 25F, 31F)) setting.adjust(1); + text(vg, trim(vg, rowLabel, w - 92F, 11F), x + 24F, sy + 4F, 11F, TEXT); + textRight(vg, setting.formattedValue(), x + w - 24F, sy + 4F, 11F, YELLOW); + float trackX = x + 24F, trackWidth = w - 48F; + nvg.drawRect(vg, trackX, sy + 23F, trackWidth, 4F, STROKE); + nvg.drawRect(vg, trackX, sy + 23F, trackWidth * (float) setting.fraction(), 4F, YELLOW); + if (input.isAreaHovered(trackX, sy + 20F, trackWidth, 11F)) + nvg.drawRect(vg, trackX, sy + 20F, trackWidth, 1F, YELLOW_DARK); + if (input.isAreaClicked(trackX, sy + 18F, trackWidth, 13F)) { + draggingSetting = setting; + draggingTrackX = trackX; + draggingTrackWidth = trackWidth; + setting.setFraction((Mouse.getX() * width / (double) mc.displayWidth - trackX) / trackWidth); + } } popClip(); } From aa079080f2adc02a3b00b560c81a232dfa11554c Mon Sep 17 00:00:00 2001 From: charkholl Date: Sun, 25 Oct 2026 10:51:24 -0400 Subject: [PATCH 3/4] Animate and clip long setting labels in the modern browser --- .../dev/goldforge/gui/ModernClickGui.java | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/main/java/dev/goldforge/gui/ModernClickGui.java b/src/main/java/dev/goldforge/gui/ModernClickGui.java index 8585a57..f1255f7 100644 --- a/src/main/java/dev/goldforge/gui/ModernClickGui.java +++ b/src/main/java/dev/goldforge/gui/ModernClickGui.java @@ -21,9 +21,12 @@ import net.minecraft.client.gui.GuiScreen; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; +import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; +import java.nio.IntBuffer; import java.util.ArrayList; +import java.util.IdentityHashMap; import java.util.List; import java.util.Locale; @@ -69,8 +72,16 @@ public final class ModernClickGui extends OneUIScreen { private String hoveredSettingName; private ModuleSetting draggingSetting; private float draggingTrackX, draggingTrackWidth; + private final IdentityHashMap settingMarquees = new IdentityHashMap(); + private long lastFrame; + private float frameSeconds; private long openedAt; + private static final class Marquee { + float offset, timer; + int phase; + } + public ModernClickGui() { this(null); } public ModernClickGui(GuiScreen parent) { @@ -88,6 +99,9 @@ public ModernClickGui(GuiScreen parent) { @Override public boolean hasBackgroundBlur() { return true; } @Override public void draw(long vg, float partialTicks, InputHandler input) { + long now = System.nanoTime(); + frameSeconds = lastFrame == 0 ? 0 : Math.min(0.05F, (now - lastFrame) / 1000000000F); + lastFrame = now; NanoVGHelper nvg = NanoVGHelper.INSTANCE; if (draggingSetting != null) { if (Mouse.isButtonDown(0)) @@ -362,10 +376,11 @@ private void drawInspector(long vg, InputHandler input, float x, float y, float float sy = settingsY + 16F + (i - settingsScroll) * 37F; ModuleSetting setting = settings.get(i); nvg.drawRoundedRect(vg, x + 14F, sy, w - 28F, 31F, CARD, 9F); - if (input.isAreaHovered(x + 14F, sy, w - 28F, 31F)) hoveredSettingName = setting.name; + boolean rowHovered = input.isAreaHovered(x + 14F, sy, w - 28F, 31F); + if (rowHovered) hoveredSettingName = setting.name; String rowLabel = selectedModule instanceof Phase && "Phase mode".equals(setting.name) ? "Mode: " + Phase.modeName(((Phase) selectedModule).mode) : setting.name; - text(vg, trim(vg, rowLabel, w - 92F, 11F), x + 24F, sy + 4F, 11F, TEXT); + marqueeSetting(vg, setting, rowLabel, x + 24F, sy + 4F, w - 92F, rowHovered); textRight(vg, setting.formattedValue(), x + w - 24F, sy + 4F, 11F, YELLOW); float trackX = x + 24F, trackWidth = w - 48F; nvg.drawRect(vg, trackX, sy + 23F, trackWidth, 4F, STROKE); @@ -382,6 +397,42 @@ private void drawInspector(long vg, InputHandler input, float x, float y, float popClip(); } + private void marqueeSetting(long vg, ModuleSetting setting, String label, + float x, float y, float available, boolean hovered) { + float overflow = NanoVGHelper.INSTANCE.getTextWidth(vg, label, 11F, Fonts.REGULAR) - available; + if (overflow <= 0F) { text(vg, label, x, y, 11F, TEXT); return; } + Marquee state = settingMarquees.get(setting); + if (state == null) { state = new Marquee(); settingMarquees.put(setting, state); } + if (!hovered) { + state.phase = 0; state.timer = 0; + state.offset *= (float) Math.exp(-11F * frameSeconds); + if (state.offset < 0.5F) state.offset = 0; + } else if (state.phase == 0) { + state.timer += frameSeconds; + if (state.timer >= 0.45F) { state.phase = 1; state.timer = 0; } + } else if (state.phase == 1) { + state.offset += (overflow - state.offset) * (1F - (float) Math.exp(-2.8F * frameSeconds)); + if (overflow - state.offset < 0.5F) { state.offset = overflow; state.phase = 2; state.timer = 0; } + } else if (state.phase == 2) { + state.timer += frameSeconds; + if (state.timer >= 0.8F) state.phase = 3; + } else { + state.offset *= (float) Math.exp(-5F * frameSeconds); + if (state.offset < 0.5F) { state.offset = 0; state.phase = 0; state.timer = 0; } + } + IntBuffer previous = BufferUtils.createIntBuffer(4); + GL11.glGetInteger(GL11.GL_SCISSOR_BOX, previous); + float scaleX = mc.displayWidth / Math.max(1F, width); + float scaleY = mc.displayHeight / Math.max(1F, height); + int left = Math.max(previous.get(0), Math.round(x * scaleX)); + int bottom = Math.max(previous.get(1), Math.round((height - y - 12F) * scaleY)); + int right = Math.min(previous.get(0) + previous.get(2), Math.round((x + available) * scaleX)); + int top = Math.min(previous.get(1) + previous.get(3), Math.round((height - y) * scaleY)); + GL11.glScissor(left, bottom, Math.max(0, right - left), Math.max(0, top - bottom)); + text(vg, label, x - state.offset, y, 11F, TEXT); + GL11.glScissor(previous.get(0), previous.get(1), previous.get(2), previous.get(3)); + } + private void action(long vg, InputHandler input, float x, float y, float w, String label, String value) { NanoVGHelper nvg = NanoVGHelper.INSTANCE; boolean hover = input.isAreaHovered(x, y, w, 31F); From 5bc10f33a583bebacf7a0bb13a4e5ab35ed88b66 Mon Sep 17 00:00:00 2001 From: charkholl Date: Sun, 25 Oct 2026 10:51:45 -0400 Subject: [PATCH 4/4] Clamp marquee offset when an option label changes --- src/main/java/dev/goldforge/gui/ModernClickGui.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/dev/goldforge/gui/ModernClickGui.java b/src/main/java/dev/goldforge/gui/ModernClickGui.java index f1255f7..17cbf67 100644 --- a/src/main/java/dev/goldforge/gui/ModernClickGui.java +++ b/src/main/java/dev/goldforge/gui/ModernClickGui.java @@ -403,6 +403,7 @@ private void marqueeSetting(long vg, ModuleSetting setting, String label, if (overflow <= 0F) { text(vg, label, x, y, 11F, TEXT); return; } Marquee state = settingMarquees.get(setting); if (state == null) { state = new Marquee(); settingMarquees.put(setting, state); } + state.offset = Math.min(state.offset, overflow); if (!hovered) { state.phase = 0; state.timer = 0; state.offset *= (float) Math.exp(-11F * frameSeconds);