From af5ec9fe170bbb8c0b0f492db2715dd3b9e35b0a Mon Sep 17 00:00:00 2001 From: viciscat <51047087+viciscat@users.noreply.github.com> Date: Fri, 6 Dec 2024 00:57:19 +0100 Subject: Random status bar changes (#1009) * Random status bar changes * Add a hide button to the right-click menu * More text/value customization * Hopefully make text resizing easier on lower gui scales * Make values show in config screen * T A B S * port * use vanilla outline text renderer might be faster since it only draws the text 2 times instead of 5 --- .../skyblock/fancybars/EditBarWidget.java | 550 +++++++++-------- .../skyblock/fancybars/FancyStatusBars.java | 566 +++++++++--------- .../skyblocker/skyblock/fancybars/StatusBar.java | 656 +++++++++++---------- .../skyblock/fancybars/StatusBarsConfigScreen.java | 645 ++++++++++---------- 4 files changed, 1262 insertions(+), 1155 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java index d39c67d7..f74d53da 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java @@ -23,253 +23,305 @@ import java.util.function.Consumer; public class EditBarWidget extends ContainerWidget { - private final EnumCyclingOption iconOption; - private final BooleanOption booleanOption; - - private final ColorOption color1; - private final ColorOption color2; - private final ColorOption textColor; - private final TextWidget nameWidget; - - private int contentsWidth = 0; - - public EditBarWidget(int x, int y, Screen parent) { - super(x, y, 100, 66, Text.literal("Edit bar")); - - TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; - - nameWidget = new TextWidget(Text.empty(), textRenderer); - - MutableText translatable = Text.translatable("skyblocker.bars.config.icon"); - contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + textRenderer.getWidth("RIGHT") + 10); - iconOption = new EnumCyclingOption<>(0, 11, getWidth(), translatable, StatusBar.IconPosition.class); - - translatable = Text.translatable("skyblocker.bars.config.showValue"); - contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10); - booleanOption = new BooleanOption(0, 22, getWidth(), translatable); - - // COLO(u)RS - translatable = Text.translatable("skyblocker.bars.config.mainColor"); - contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10); - color1 = new ColorOption(0, 33, getWidth(), translatable, parent); - - translatable = Text.translatable("skyblocker.bars.config.overflowColor"); - contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10); - color2 = new ColorOption(0, 44, getWidth(), translatable, parent); - - translatable = Text.translatable("skyblocker.bars.config.textColor"); - contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10); - textColor = new ColorOption(0, 55, getWidth(), translatable, parent); - - setWidth(contentsWidth); - } - - @Override - public List children() { - return List.of(iconOption, booleanOption, color1, color2, textColor); - } - - public int insideMouseX = 0; - public int insideMouseY = 0; - - @Override - protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { - if (isHovered()) { - insideMouseX = mouseX; - insideMouseY = mouseY; - } else { - int i = mouseX - insideMouseX; - int j = mouseY - insideMouseY; - if (i * i + j * j > 30 * 30) visible = false; - } - TooltipBackgroundRenderer.render(context, getX(), getY(), getWidth(), getHeight(), 0, null); - MatrixStack matrices = context.getMatrices(); - matrices.push(); - matrices.translate(getX(), getY(), 0); - nameWidget.render(context, mouseX, mouseY, delta); - iconOption.renderWidget(context, mouseX - getX(), mouseY - getY(), delta); - booleanOption.renderWidget(context, mouseX - getX(), mouseY - getY(), delta); - color1.renderWidget(context, mouseX - getX(), mouseY - getY(), delta); - color2.renderWidget(context, mouseX - getX(), mouseY - getY(), delta); - textColor.renderWidget(context, mouseX - getX(), mouseY - getY(), delta); - matrices.pop(); - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (!visible) return false; - if (!isHovered()) visible = false; - return super.mouseClicked(mouseX - getX(), mouseY - getY(), button); - } - - public void setStatusBar(StatusBar statusBar) { - iconOption.setCurrent(statusBar.getIconPosition()); - iconOption.setOnChange(statusBar::setIconPosition); - booleanOption.setCurrent(statusBar.showText()); - booleanOption.setOnChange(statusBar::setShowText); - - color1.setCurrent(statusBar.getColors()[0].getRGB()); - color1.setOnChange(color -> statusBar.getColors()[0] = color); - - color2.active = statusBar.hasOverflow(); - if (color2.active) { - color2.setCurrent(statusBar.getColors()[1].getRGB()); - color2.setOnChange(color -> statusBar.getColors()[1] = color); - } - - if (statusBar.getTextColor() != null) { - textColor.setCurrent(statusBar.getTextColor().getRGB()); - } - textColor.setOnChange(statusBar::setTextColor); - - MutableText formatted = statusBar.getName().copy().formatted(Formatting.BOLD); - nameWidget.setMessage(formatted); - setWidth(Math.max(MinecraftClient.getInstance().textRenderer.getWidth(formatted), contentsWidth)); - } - - @Override - public void setWidth(int width) { - super.setWidth(width); - iconOption.setWidth(width); - booleanOption.setWidth(width); - color1.setWidth(width); - color2.setWidth(width); - textColor.setWidth(width); - nameWidget.setWidth(width); - - } - - public static class EnumCyclingOption> extends ClickableWidget { - - private T current; - private final T[] values; - private Consumer onChange = null; - - public EnumCyclingOption(int x, int y, int width, Text message, Class enumClass) { - super(x, y, width, 11, message); - values = enumClass.getEnumConstants(); - current = values[0]; - } - - @Override - protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { - if (isMouseOver(mouseX, mouseY)) { - context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF); - } - TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; - context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, -1, true); - String string = current.toString(); - context.drawText(textRenderer, string, getRight() - textRenderer.getWidth(string) - 1, getY() + 1, -1, true); - } - - public void setCurrent(T current) { - this.current = current; - } - - @Override - public void onClick(double mouseX, double mouseY) { - current = values[(current.ordinal() + 1) % values.length]; - if (onChange != null) onChange.accept(current); - super.onClick(mouseX, mouseY); - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - } - - public void setOnChange(Consumer onChange) { - this.onChange = onChange; - } - } - - public static class BooleanOption extends ClickableWidget { - - private boolean current = false; - private BooleanConsumer onChange = null; - - public BooleanOption(int x, int y, int width, Text message) { - super(x, y, width, 11, message); - } - - @Override - protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { - if (isMouseOver(mouseX, mouseY)) { - context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF); - } - TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; - context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, -1, true); - context.drawBorder(getRight() - 10, getY() + 1, 9, 9, -1); - if (current) context.fill(getRight() - 8, getY() + 3, getRight() - 3, getY() + 8, -1); - } - - @Override - public void onClick(double mouseX, double mouseY) { - current = !current; - if (onChange != null) onChange.accept(current); - super.onClick(mouseX, mouseY); - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - } - - public void setCurrent(boolean current) { - this.current = current; - } - - public void setOnChange(BooleanConsumer onChange) { - this.onChange = onChange; - } - } - - public static class ColorOption extends ClickableWidget { - - public void setCurrent(int current) { - this.current = current; - } - - private int current = 0; - private Consumer onChange = null; - private final Screen parent; - - public ColorOption(int x, int y, int width, Text message, Screen parent) { - super(x, y, width, 11, message); - this.parent = parent; - } - - @Override - protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { - if (isMouseOver(mouseX, mouseY)) { - context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF); - } - TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; - context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, active ? -1 : Colors.GRAY, true); - context.drawBorder(getRight() - 10, getY() + 1, 9, 9, active ? -1 : Colors.GRAY); - context.fill(getRight() - 8, getY() + 3, getRight() - 3, getY() + 8, active ? current : Colors.GRAY); - } - - @Override - public void onClick(double mouseX, double mouseY) { - super.onClick(mouseX, mouseY); - MinecraftClient.getInstance().setScreen(new EditBarColorPopup(Text.literal("Edit ").append(getMessage()), parent, this::set)); - } - - private void set(Color color) { - current = color.getRGB(); - if (onChange != null) onChange.accept(color); - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - - } - - public void setOnChange(Consumer onChange) { - this.onChange = onChange; - } - } + private final EnumCyclingOption iconOption; + private final EnumCyclingOption textOption; + + private final ColorOption color1; + private final ColorOption color2; + private final ColorOption textColor; + private final TextWidget nameWidget; + private final RunnableOption hideOption; + + private final List options; + + private int contentsWidth = 0; + + public EditBarWidget(int x, int y, Screen parent) { + super(x, y, 100, 77, Text.literal("Edit bar")); + + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + + nameWidget = new TextWidget(Text.empty(), textRenderer); + + MutableText translatable = Text.translatable("skyblocker.bars.config.icon"); + iconOption = new EnumCyclingOption<>(0, 11, getWidth(), translatable, StatusBar.IconPosition.class); + contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + iconOption.getLongestOptionWidth() + 10); + + translatable = Text.translatable("skyblocker.bars.config.text"); + textOption = new EnumCyclingOption<>(0, 22, getWidth(), translatable, StatusBar.TextPosition.class); + contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + textOption.getLongestOptionWidth() + 10); + + // COLO(u)RS + translatable = Text.translatable("skyblocker.bars.config.mainColor"); + contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10); + color1 = new ColorOption(0, 33, getWidth(), translatable, parent); + + translatable = Text.translatable("skyblocker.bars.config.overflowColor"); + contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10); + color2 = new ColorOption(0, 44, getWidth(), translatable, parent); + + translatable = Text.translatable("skyblocker.bars.config.textColor"); + contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10); + textColor = new ColorOption(0, 55, getWidth(), translatable, parent); + + translatable = Text.translatable("skyblocker.bars.config.hide"); + contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10); + hideOption = new RunnableOption(0, 66, getWidth(), translatable); + + options = List.of(iconOption, textOption, color1, color2, textColor, hideOption); + + setWidth(contentsWidth); + } + + @Override + public List children() { + return options; + } + + public int insideMouseX = 0; + public int insideMouseY = 0; + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + if (isHovered()) { + insideMouseX = mouseX; + insideMouseY = mouseY; + } else { + int i = mouseX - insideMouseX; + int j = mouseY - insideMouseY; + if (i * i + j * j > 30 * 30) visible = false; + } + MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.translate(getX(), getY(), 200.f); + TooltipBackgroundRenderer.render(context, 0, 0, getWidth(), getHeight(), 0, null); + nameWidget.render(context, mouseX, mouseY, delta); + for (ClickableWidget option : options) option.render(context, mouseX - getX(), mouseY - getY(), delta); + matrices.pop(); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (!visible) return false; + if (!isHovered()) visible = false; + return super.mouseClicked(mouseX - getX(), mouseY - getY(), button); + } + + public void setStatusBar(StatusBar statusBar) { + iconOption.setCurrent(statusBar.getIconPosition()); + iconOption.setOnChange(statusBar::setIconPosition); + textOption.setCurrent(statusBar.getTextPosition()); + textOption.setOnChange(statusBar::setTextPosition); + + color1.setCurrent(statusBar.getColors()[0].getRGB()); + color1.setOnChange(color -> statusBar.getColors()[0] = color); + + color2.active = statusBar.hasOverflow(); + if (color2.active) { + color2.setCurrent(statusBar.getColors()[1].getRGB()); + color2.setOnChange(color -> statusBar.getColors()[1] = color); + } + + if (statusBar.getTextColor() != null) { + textColor.setCurrent(statusBar.getTextColor().getRGB()); + } + textColor.setOnChange(statusBar::setTextColor); + hideOption.active = statusBar.anchor != null; + hideOption.setRunnable(() -> { + if (statusBar.anchor != null) + FancyStatusBars.barPositioner.removeBar(statusBar.anchor, statusBar.gridY, statusBar); + FancyStatusBars.updatePositions(); + }); + + MutableText formatted = statusBar.getName().copy().formatted(Formatting.BOLD); + nameWidget.setMessage(formatted); + setWidth(Math.max(MinecraftClient.getInstance().textRenderer.getWidth(formatted), contentsWidth)); + } + + @Override + public void setWidth(int width) { + super.setWidth(width); + for (ClickableWidget option : options) option.setWidth(width); + nameWidget.setWidth(width); + + } + + public class RunnableOption extends ClickableWidget { + + private Runnable runnable; + + public RunnableOption(int x, int y, int width, Text message) { + super(x, y, width, 11, message); + } + + public void setRunnable(Runnable runnable) { + this.runnable = runnable; + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + if (isMouseOver(mouseX, mouseY)) { + context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF); + } + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, active ? -1: Colors.GRAY, true); + } + + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + EditBarWidget.this.visible = false; + if (runnable != null) runnable.run(); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) {} + } + + public static class EnumCyclingOption> extends ClickableWidget { + + private T current; + private final T[] values; + private Consumer onChange = null; + + public EnumCyclingOption(int x, int y, int width, Text message, Class enumClass) { + super(x, y, width, 11, message); + values = enumClass.getEnumConstants(); + current = values[0]; + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + if (isMouseOver(mouseX, mouseY)) { + context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF); + } + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, -1, true); + String string = current.toString(); + context.drawText(textRenderer, string, getRight() - textRenderer.getWidth(string) - 1, getY() + 1, -1, true); + } + + public void setCurrent(T current) { + this.current = current; + } + + @Override + public void onClick(double mouseX, double mouseY) { + current = values[(current.ordinal() + 1) % values.length]; + if (onChange != null) onChange.accept(current); + super.onClick(mouseX, mouseY); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + } + + public void setOnChange(Consumer onChange) { + this.onChange = onChange; + } + + int getLongestOptionWidth() { + int m = 0; + for (T value : values) { + int i = MinecraftClient.getInstance().textRenderer.getWidth(value.toString()); + m = Math.max(m, i); + } + return m; + } + } + + /** + * Unused but could always come in handy I guess + */ + @SuppressWarnings("unused") + public static class BooleanOption extends ClickableWidget { + + private boolean current = false; + private BooleanConsumer onChange = null; + + public BooleanOption(int x, int y, int width, Text message) { + super(x, y, width, 11, message); + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + if (isMouseOver(mouseX, mouseY)) { + context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF); + } + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, -1, true); + context.drawBorder(getRight() - 10, getY() + 1, 9, 9, -1); + if (current) context.fill(getRight() - 8, getY() + 3, getRight() - 3, getY() + 8, -1); + } + + @Override + public void onClick(double mouseX, double mouseY) { + current = !current; + if (onChange != null) onChange.accept(current); + super.onClick(mouseX, mouseY); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + } + + public void setCurrent(boolean current) { + this.current = current; + } + + public void setOnChange(BooleanConsumer onChange) { + this.onChange = onChange; + } + } + + public static class ColorOption extends ClickableWidget { + + public void setCurrent(int current) { + this.current = current; + } + + private int current = 0; + private Consumer onChange = null; + private final Screen parent; + + public ColorOption(int x, int y, int width, Text message, Screen parent) { + super(x, y, width, 11, message); + this.parent = parent; + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + if (isMouseOver(mouseX, mouseY)) { + context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF); + } + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, active ? -1 : Colors.GRAY, true); + context.drawBorder(getRight() - 10, getY() + 1, 9, 9, active ? -1 : Colors.GRAY); + context.fill(getRight() - 8, getY() + 3, getRight() - 3, getY() + 8, active ? current : Colors.GRAY); + } + + @Override + public void onClick(double mouseX, double mouseY) { + super.onClick(mouseX, mouseY); + MinecraftClient.getInstance().setScreen(new EditBarColorPopup(Text.literal("Edit ").append(getMessage()), parent, this::set)); + } + + private void set(Color color) { + current = color.getRGB(); + if (onChange != null) onChange.accept(color); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + + } + + public void setOnChange(Consumer onChange) { + this.onChange = onChange; + } + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/FancyStatusBars.java b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/FancyStatusBars.java index 46685468..cc6a3571 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/FancyStatusBars.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/FancyStatusBars.java @@ -33,288 +33,298 @@ import java.util.*; import java.util.concurrent.CompletableFuture; public class FancyStatusBars { - private static final Path FILE = SkyblockerMod.CONFIG_DIR.resolve("status_bars.json"); - private static final Logger LOGGER = LoggerFactory.getLogger(FancyStatusBars.class); + private static final Path FILE = SkyblockerMod.CONFIG_DIR.resolve("status_bars.json"); + private static final Logger LOGGER = LoggerFactory.getLogger(FancyStatusBars.class); - private final MinecraftClient client = MinecraftClient.getInstance(); - private final StatusBarTracker statusBarTracker = SkyblockerMod.getInstance().statusBarTracker; + private final MinecraftClient client = MinecraftClient.getInstance(); + private final StatusBarTracker statusBarTracker = SkyblockerMod.getInstance().statusBarTracker; - public static BarPositioner barPositioner = new BarPositioner(); - public static Map statusBars = new HashMap<>(); + public static BarPositioner barPositioner = new BarPositioner(); + public static Map statusBars = new HashMap<>(); - public static boolean isHealthFancyBarVisible() { - StatusBar health = statusBars.get("health"); - return health.anchor != null || health.inMouse; - } + public static boolean isHealthFancyBarVisible() { + StatusBar health = statusBars.get("health"); + return health.anchor != null || health.inMouse; + } - public static boolean isExperienceFancyBarVisible() { - StatusBar experience = statusBars.get("experience"); - return experience.anchor != null || experience.inMouse; - } + public static boolean isExperienceFancyBarVisible() { + StatusBar experience = statusBars.get("experience"); + return experience.anchor != null || experience.inMouse; + } - @SuppressWarnings("deprecation") + @SuppressWarnings("deprecation") @Init - public static void init() { - statusBars.put("health", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/health"), - new Color[]{new Color(255, 0, 0), new Color(255, 220, 0)}, - true, new Color(255, 85, 85), Text.translatable("skyblocker.bars.config.health"))); - statusBars.put("intelligence", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/intelligence"), - new Color[]{new Color(0, 255, 255), new Color(180, 0, 255)}, - true, new Color(85, 255, 255), Text.translatable("skyblocker.bars.config.intelligence"))); - statusBars.put("defense", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/defense"), - new Color[]{new Color(255, 255, 255)}, - false, new Color(185, 185, 185), Text.translatable("skyblocker.bars.config.defense"))); - statusBars.put("experience", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/experience"), - new Color[]{new Color(100, 230, 70)}, - false, new Color(128, 255, 32), Text.translatable("skyblocker.bars.config.experience"))); - statusBars.put("speed", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/speed"), - new Color[]{new Color(255, 255, 255)}, - false, new Color(185, 185, 185), Text.translatable("skyblocker.bars.config.speed"))); - - // Fetch from old status bar config - int[] counts = new int[3]; // counts for RIGHT, LAYER1, LAYER2 - StatusBar health = statusBars.get("health"); - UIAndVisualsConfig.LegacyBarPositions barPositions = SkyblockerConfigManager.get().uiAndVisuals.bars.barPositions; - initBarPosition(health, counts, barPositions.healthBarPosition); - StatusBar intelligence = statusBars.get("intelligence"); - initBarPosition(intelligence, counts, barPositions.manaBarPosition); - StatusBar defense = statusBars.get("defense"); - initBarPosition(defense, counts, barPositions.defenceBarPosition); - StatusBar experience = statusBars.get("experience"); - initBarPosition(experience, counts, barPositions.experienceBarPosition); - StatusBar speed = statusBars.get("speed"); - initBarPosition(speed, counts, UIAndVisualsConfig.LegacyBarPosition.RIGHT); - - CompletableFuture.supplyAsync(FancyStatusBars::loadBarConfig).thenAccept(object -> { - if (object != null) { - for (String s : object.keySet()) { - if (statusBars.containsKey(s)) { - try { - statusBars.get(s).loadFromJson(object.get(s).getAsJsonObject()); - } catch (Exception e) { - LOGGER.error("[Skyblocker] Failed to load {} status bar", s, e); - } - } else { - LOGGER.warn("[Skyblocker] Unknown status bar: {}", s); - } - } - } - placeBarsInPositioner(); - configLoaded = true; - }).exceptionally(throwable -> { - LOGGER.error("[Skyblocker] Failed reading status bars config", throwable); - return null; - }); - ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> { - saveBarConfig(); - GLFW.glfwDestroyCursor(StatusBarsConfigScreen.RESIZE_CURSOR); - }); - - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register( - ClientCommandManager.literal(SkyblockerMod.NAMESPACE) - .then(ClientCommandManager.literal("bars").executes(Scheduler.queueOpenScreenCommand(StatusBarsConfigScreen::new))))); - } - - /** - * Loads the bar position from the old config. Should be used to initialize new bars too. - * @param bar the bar to load the position for - * @param counts the counts for each bar position (LAYER1, LAYER2, RIGHT) - * @param position the position to load - */ - private static void initBarPosition(StatusBar bar, int[] counts, UIAndVisualsConfig.LegacyBarPosition position) { - switch (position) { - case RIGHT: - bar.anchor = BarPositioner.BarAnchor.HOTBAR_RIGHT; - bar.gridY = 0; - bar.gridX = counts[position.ordinal()]++; - break; - case LAYER1: - bar.anchor = BarPositioner.BarAnchor.HOTBAR_TOP; - bar.gridY = 0; - bar.gridX = counts[position.ordinal()]++; - break; - case LAYER2: - bar.anchor = BarPositioner.BarAnchor.HOTBAR_TOP; - bar.gridY = 1; - bar.gridX = counts[position.ordinal()]++; - break; - } - } - - private static boolean configLoaded = false; - - private static void placeBarsInPositioner() { - List original = statusBars.values().stream().toList(); - - for (BarPositioner.BarAnchor barAnchor : BarPositioner.BarAnchor.allAnchors()) { - List barList = new ArrayList<>(original.stream().filter(bar -> bar.anchor == barAnchor).toList()); - if (barList.isEmpty()) continue; - barList.sort((a, b) -> a.gridY == b.gridY ? Integer.compare(a.gridX, b.gridX) : Integer.compare(a.gridY, b.gridY)); - - int y = -1; - int rowNum = -1; - for (StatusBar statusBar : barList) { - if (statusBar.gridY > y) { - barPositioner.addRow(barAnchor); - rowNum++; - y = statusBar.gridY; - } - barPositioner.addBar(barAnchor, rowNum, statusBar); - } - } - } - - public static JsonObject loadBarConfig() { - try (BufferedReader reader = Files.newBufferedReader(FILE)) { - return SkyblockerMod.GSON.fromJson(reader, JsonObject.class); - } catch (NoSuchFileException e) { - LOGGER.warn("[Skyblocker] No status bar config file found, using defaults"); - } catch (Exception e) { - LOGGER.error("[Skyblocker] Failed to load status bars config", e); - } - return null; - } - - public static void saveBarConfig() { - JsonObject output = new JsonObject(); - statusBars.forEach((s, statusBar) -> output.add(s, statusBar.toJson())); - try (BufferedWriter writer = Files.newBufferedWriter(FILE)) { - SkyblockerMod.GSON.toJson(output, writer); - LOGGER.info("[Skyblocker] Saved status bars config"); - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to save status bars config", e); - } - } - - public static void updatePositions() { - if (!configLoaded) return; - final int width = MinecraftClient.getInstance().getWindow().getScaledWidth(); - final int height = MinecraftClient.getInstance().getWindow().getScaledHeight(); - - for (BarPositioner.BarAnchor barAnchor : BarPositioner.BarAnchor.allAnchors()) { - ScreenPos anchorPosition = barAnchor.getAnchorPosition(width, height); - BarPositioner.SizeRule sizeRule = barAnchor.getSizeRule(); - - int targetSize = sizeRule.targetSize(); - boolean visibleHealthMove = barAnchor == BarPositioner.BarAnchor.HOTBAR_TOP && !isHealthFancyBarVisible(); - if (visibleHealthMove) { - targetSize /= 2; - } - - if (sizeRule.isTargetSize()) { - for (int row = 0; row < barPositioner.getRowCount(barAnchor); row++) { - LinkedList barRow = barPositioner.getRow(barAnchor, row); - if (barRow.isEmpty()) continue; - - // FIX SIZES - int totalSize = 0; - for (StatusBar statusBar : barRow) - totalSize += (statusBar.size = Math.clamp(statusBar.size, sizeRule.minSize(), sizeRule.maxSize())); - - whileLoop: - while (totalSize != targetSize) { - if (totalSize > targetSize) { - for (StatusBar statusBar : barRow) { - if (statusBar.size > sizeRule.minSize()) { - statusBar.size--; - totalSize--; - if (totalSize == targetSize) break whileLoop; - } - } - } else { - for (StatusBar statusBar : barRow) { - if (statusBar.size < sizeRule.maxSize()) { - statusBar.size++; - totalSize++; - if (totalSize == targetSize) break whileLoop; - } - } - } - } - - } - } - - for (int row = 0; row < barPositioner.getRowCount(barAnchor); row++) { - List barRow = barPositioner.getRow(barAnchor, row); - if (barRow.isEmpty()) continue; - - - // Update the positions - float widthPerSize; - if (sizeRule.isTargetSize()) - widthPerSize = (float) sizeRule.totalWidth() / targetSize; - else - widthPerSize = sizeRule.widthPerSize(); - - if (visibleHealthMove) widthPerSize /= 2; - - int currSize = 0; - int rowSize = barRow.size(); - for (int i = 0; i < rowSize; i++) { - // A bit of a padding - int offsetX = 0; - int lessWidth = 0; - if (rowSize > 1) { // Technically bars in the middle of 3+ bars will be smaller than the 2 side ones but shh - if (i == 0) lessWidth = 1; - else if (i == rowSize - 1) { - lessWidth = 1; - offsetX = 1; - } else { - lessWidth = 2; - offsetX = 1; - } - } - StatusBar statusBar = barRow.get(i); - statusBar.size = Math.clamp(statusBar.size, sizeRule.minSize(), sizeRule.maxSize()); - - float x = barAnchor.isRight() ? - anchorPosition.x() + (visibleHealthMove ? sizeRule.totalWidth() / 2.f : 0) + currSize * widthPerSize : - anchorPosition.x() - currSize * widthPerSize - statusBar.size * widthPerSize; - statusBar.setX(MathHelper.ceil(x) + offsetX); - - int y = barAnchor.isUp() ? - anchorPosition.y() - (row + 1) * (statusBar.getHeight() + 1) : - anchorPosition.y() + row * (statusBar.getHeight() + 1); - statusBar.setY(y); - - statusBar.setWidth(MathHelper.floor(statusBar.size * widthPerSize) - lessWidth); - currSize += statusBar.size; - statusBar.gridX = i; - statusBar.gridY = row; - - } - } - - } - } - - public static boolean isEnabled() { - return SkyblockerConfigManager.get().uiAndVisuals.bars.enableBars && !Utils.isInTheRift(); - } - - public boolean render(DrawContext context, int scaledWidth, int scaledHeight) { - var player = client.player; - if (!isEnabled() || player == null) - return false; - - Collection barCollection = statusBars.values(); - for (StatusBar statusBar : barCollection) { - if (statusBar.anchor != null) statusBar.render(context, -1, -1, client.getRenderTickCounter().getLastFrameDuration()); - } - for (StatusBar statusBar : barCollection) { - if (statusBar.anchor != null && statusBar.showText()) statusBar.renderText(context); - } - StatusBarTracker.Resource health = statusBarTracker.getHealth(); - statusBars.get("health").updateValues(health.value() / (float) health.max(), health.overflow() / (float) health.max(), health.value()); - - StatusBarTracker.Resource intelligence = statusBarTracker.getMana(); - statusBars.get("intelligence").updateValues(intelligence.value() / (float) intelligence.max(), intelligence.overflow() / (float) intelligence.max(), intelligence.value()); - int defense = statusBarTracker.getDefense(); - statusBars.get("defense").updateValues(defense / (defense + 100.f), 0, defense); - StatusBarTracker.Resource speed = statusBarTracker.getSpeed(); - statusBars.get("speed").updateValues(speed.value() / (float) speed.max(), 0, speed.value()); - statusBars.get("experience").updateValues(player.experienceProgress, 0, player.experienceLevel); - return true; - } + public static void init() { + statusBars.put("health", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/health"), + new Color[]{new Color(255, 0, 0), new Color(255, 220, 0)}, + true, new Color(255, 85, 85), Text.translatable("skyblocker.bars.config.health"))); + statusBars.put("intelligence", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/intelligence"), + new Color[]{new Color(0, 255, 255), new Color(180, 0, 255)}, + true, new Color(85, 255, 255), Text.translatable("skyblocker.bars.config.intelligence"))); + statusBars.put("defense", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/defense"), + new Color[]{new Color(255, 255, 255)}, + false, new Color(185, 185, 185), Text.translatable("skyblocker.bars.config.defense"))); + statusBars.put("experience", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/experience"), + new Color[]{new Color(100, 230, 70)}, + false, new Color(128, 255, 32), Text.translatable("skyblocker.bars.config.experience"))); + statusBars.put("speed", new StatusBar(Identifier.of(SkyblockerMod.NAMESPACE, "bars/icons/speed"), + new Color[]{new Color(255, 255, 255)}, + false, new Color(185, 185, 185), Text.translatable("skyblocker.bars.config.speed"))); + + // Fetch from old status bar config + int[] counts = new int[3]; // counts for RIGHT, LAYER1, LAYER2 + StatusBar health = statusBars.get("health"); + + UIAndVisualsConfig.LegacyBarPositions barPositions = SkyblockerConfigManager.get().uiAndVisuals.bars.barPositions; + initBarPosition(health, counts, barPositions.healthBarPosition); + StatusBar intelligence = statusBars.get("intelligence"); + initBarPosition(intelligence, counts, barPositions.manaBarPosition); + StatusBar defense = statusBars.get("defense"); + initBarPosition(defense, counts, barPositions.defenceBarPosition); + StatusBar experience = statusBars.get("experience"); + initBarPosition(experience, counts, barPositions.experienceBarPosition); + StatusBar speed = statusBars.get("speed"); + initBarPosition(speed, counts, UIAndVisualsConfig.LegacyBarPosition.RIGHT); + + CompletableFuture.supplyAsync(FancyStatusBars::loadBarConfig).thenAccept(object -> { + if (object != null) { + for (String s : object.keySet()) { + if (statusBars.containsKey(s)) { + try { + statusBars.get(s).loadFromJson(object.get(s).getAsJsonObject()); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to load {} status bar", s, e); + } + } else { + LOGGER.warn("[Skyblocker] Unknown status bar: {}", s); + } + } + } + placeBarsInPositioner(); + configLoaded = true; + }).exceptionally(throwable -> { + LOGGER.error("[Skyblocker] Failed reading status bars config", throwable); + return null; + }); + ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> { + saveBarConfig(); + GLFW.glfwDestroyCursor(StatusBarsConfigScreen.RESIZE_CURSOR); + }); + + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register( + ClientCommandManager.literal(SkyblockerMod.NAMESPACE) + .then(ClientCommandManager.literal("bars").executes(Scheduler.queueOpenScreenCommand(StatusBarsConfigScreen::new))))); + } + + /** + * Loads the bar position from the old config. Should be used to initialize new bars too. + * + * @param bar the bar to load the position for + * @param counts the counts for each bar position (LAYER1, LAYER2, RIGHT) + * @param position the position to load + */ + private static void initBarPosition(StatusBar bar, int[] counts, UIAndVisualsConfig.LegacyBarPosition position) { + switch (position) { + case RIGHT: + bar.anchor = BarPositioner.BarAnchor.HOTBAR_RIGHT; + bar.gridY = 0; + bar.gridX = counts[position.ordinal()]++; + break; + case LAYER1: + bar.anchor = BarPositioner.BarAnchor.HOTBAR_TOP; + bar.gridY = 0; + bar.gridX = counts[position.ordinal()]++; + break; + case LAYER2: + bar.anchor = BarPositioner.BarAnchor.HOTBAR_TOP; + bar.gridY = 1; + bar.gridX = counts[position.ordinal()]++; + break; + } + } + + private static boolean configLoaded = false; + + private static void placeBarsInPositioner() { + List original = statusBars.values().stream().toList(); + + for (BarPositioner.BarAnchor barAnchor : BarPositioner.BarAnchor.allAnchors()) { + List barList = new ArrayList<>(original.stream().filter(bar -> bar.anchor == barAnchor).toList()); + if (barList.isEmpty()) continue; + barList.sort((a, b) -> a.gridY == b.gridY ? Integer.compare(a.gridX, b.gridX) : Integer.compare(a.gridY, b.gridY)); + + int y = -1; + int rowNum = -1; + for (StatusBar statusBar : barList) { + if (statusBar.gridY > y) { + barPositioner.addRow(barAnchor); + rowNum++; + y = statusBar.gridY; + } + barPositioner.addBar(barAnchor, rowNum, statusBar); + } + } + } + + public static JsonObject loadBarConfig() { + try (BufferedReader reader = Files.newBufferedReader(FILE)) { + return SkyblockerMod.GSON.fromJson(reader, JsonObject.class); + } catch (NoSuchFileException e) { + LOGGER.warn("[Skyblocker] No status bar config file found, using defaults"); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to load status bars config", e); + } + return null; + } + + public static void saveBarConfig() { + JsonObject output = new JsonObject(); + statusBars.forEach((s, statusBar) -> output.add(s, statusBar.toJson())); + try (BufferedWriter writer = Files.newBufferedWriter(FILE)) { + SkyblockerMod.GSON.toJson(output, writer); + LOGGER.info("[Skyblocker] Saved status bars config"); + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to save status bars config", e); + } + } + + public static void updatePositions() { + if (!configLoaded) return; + final int width = MinecraftClient.getInstance().getWindow().getScaledWidth(); + final int height = MinecraftClient.getInstance().getWindow().getScaledHeight(); + + // Put these in the corner for the config screen + int offset = 0; + for (StatusBar statusBar : statusBars.values()) { + if (statusBar.anchor == null) { + statusBar.setX(5); + statusBar.setY(50 + offset); + statusBar.setWidth(30); + offset += statusBar.getHeight(); + } + } + + for (BarPositioner.BarAnchor barAnchor : BarPositioner.BarAnchor.allAnchors()) { + ScreenPos anchorPosition = barAnchor.getAnchorPosition(width, height); + BarPositioner.SizeRule sizeRule = barAnchor.getSizeRule(); + + int targetSize = sizeRule.targetSize(); + boolean visibleHealthMove = barAnchor == BarPositioner.BarAnchor.HOTBAR_TOP && !isHealthFancyBarVisible(); + if (visibleHealthMove) { + targetSize /= 2; + } + + if (sizeRule.isTargetSize()) { + for (int row = 0; row < barPositioner.getRowCount(barAnchor); row++) { + LinkedList barRow = barPositioner.getRow(barAnchor, row); + if (barRow.isEmpty()) continue; + + // FIX SIZES + int totalSize = 0; + for (StatusBar statusBar : barRow) + totalSize += (statusBar.size = Math.clamp(statusBar.size, sizeRule.minSize(), sizeRule.maxSize())); + + whileLoop: + while (totalSize != targetSize) { + if (totalSize > targetSize) { + for (StatusBar statusBar : barRow) { + if (statusBar.size > sizeRule.minSize()) { + statusBar.size--; + totalSize--; + if (totalSize == targetSize) break whileLoop; + } + } + } else { + for (StatusBar statusBar : barRow) { + if (statusBar.size < sizeRule.maxSize()) { + statusBar.size++; + totalSize++; + if (totalSize == targetSize) break whileLoop; + } + } + } + } + + } + } + + for (int row = 0; row < barPositioner.getRowCount(barAnchor); row++) { + List barRow = barPositioner.getRow(barAnchor, row); + if (barRow.isEmpty()) continue; + + + // Update the positions + float widthPerSize; + if (sizeRule.isTargetSize()) + widthPerSize = (float) sizeRule.totalWidth() / targetSize; + else + widthPerSize = sizeRule.widthPerSize(); + + if (visibleHealthMove) widthPerSize /= 2; + + int currSize = 0; + int rowSize = barRow.size(); + for (int i = 0; i < rowSize; i++) { + // A bit of a padding + int offsetX = 0; + int lessWidth = 0; + if (rowSize > 1) { // Technically bars in the middle of 3+ bars will be smaller than the 2 side ones but shh + if (i == 0) lessWidth = 1; + else if (i == rowSize - 1) { + lessWidth = 1; + offsetX = 1; + } else { + lessWidth = 2; + offsetX = 1; + } + } + StatusBar statusBar = barRow.get(i); + statusBar.size = Math.clamp(statusBar.size, sizeRule.minSize(), sizeRule.maxSize()); + + float x = barAnchor.isRight() ? + anchorPosition.x() + (visibleHealthMove ? sizeRule.totalWidth() / 2.f : 0) + currSize * widthPerSize : + anchorPosition.x() - currSize * widthPerSize - statusBar.size * widthPerSize; + statusBar.setX(MathHelper.ceil(x) + offsetX); + + int y = barAnchor.isUp() ? + anchorPosition.y() - (row + 1) * (statusBar.getHeight() + 1) : + anchorPosition.y() + row * (statusBar.getHeight() + 1); + statusBar.setY(y); + + statusBar.setWidth(MathHelper.floor(statusBar.size * widthPerSize) - lessWidth); + currSize += statusBar.size; + statusBar.gridX = i; + statusBar.gridY = row; + + } + } + + } + } + + public static boolean isEnabled() { + return SkyblockerConfigManager.get().uiAndVisuals.bars.enableBars && !Utils.isInTheRift(); + } + + public boolean render(DrawContext context, int scaledWidth, int scaledHeight) { + var player = client.player; + if (!isEnabled() || player == null) + return false; + + Collection barCollection = statusBars.values(); + for (StatusBar statusBar : barCollection) { + if (statusBar.anchor != null) statusBar.render(context, -1, -1, client.getRenderTickCounter().getLastFrameDuration()); + } + StatusBarTracker.Resource health = statusBarTracker.getHealth(); + statusBars.get("health").updateValues(health.value() / (float) health.max(), health.overflow() / (float) health.max(), health.value()); + + StatusBarTracker.Resource intelligence = statusBarTracker.getMana(); + statusBars.get("intelligence").updateValues(intelligence.value() / (float) intelligence.max(), intelligence.overflow() / (float) intelligence.max(), intelligence.value()); + int defense = statusBarTracker.getDefense(); + statusBars.get("defense").updateValues(defense / (defense + 100.f), 0, defense); + StatusBarTracker.Resource speed = statusBarTracker.getSpeed(); + statusBars.get("speed").updateValues(speed.value() / (float) speed.max(), 0, speed.value()); + statusBars.get("experience").updateValues(player.experienceProgress, 0, player.experienceLevel); + return true; + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/StatusBar.java b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/StatusBar.java index f06effec..a2fd510b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/StatusBar.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/StatusBar.java @@ -3,7 +3,6 @@ package de.hysky.skyblocker.skyblock.fancybars; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.mojang.blaze3d.systems.RenderSystem; import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.utils.render.RenderHelper; @@ -13,9 +12,11 @@ import net.minecraft.client.gui.*; import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.client.gui.widget.Widget; +import net.minecraft.client.resource.language.I18n; import net.minecraft.client.render.RenderLayer; import net.minecraft.text.Text; import net.minecraft.util.Identifier; +import net.minecraft.util.StringIdentifiable; import org.apache.commons.lang3.builder.ToStringBuilder; import org.jetbrains.annotations.Nullable; @@ -24,8 +25,8 @@ import java.util.function.Consumer; public class StatusBar implements Widget, Drawable, Element, Selectable { - private static final Identifier BAR_FILL = Identifier.of(SkyblockerMod.NAMESPACE, "bars/bar_fill"); - private static final Identifier BAR_BACK = Identifier.of(SkyblockerMod.NAMESPACE, "bars/bar_back"); + private static final Identifier BAR_FILL = Identifier.of(SkyblockerMod.NAMESPACE, "bars/bar_fill"); + private static final Identifier BAR_BACK = Identifier.of(SkyblockerMod.NAMESPACE, "bars/bar_back"); /* public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( @@ -57,301 +58,356 @@ public class StatusBar implements Widget, Drawable, Element, Selectable { .apply(instance, ));*/ - private final Identifier icon; - - public Color[] getColors() { - return colors; - } - - public boolean hasOverflow() { - return hasOverflow; - } - - public @Nullable Color getTextColor() { - return textColor; - } - - private Color[] colors; - private final boolean hasOverflow; - - public void setTextColor(@Nullable Color textColor) { - this.textColor = textColor; - } - - private @Nullable Color textColor; - - public Text getName() { - return name; - } - - private final Text name; - - private @Nullable OnClick onClick = null; - public int gridX = 0; - public int gridY = 0; - public @Nullable BarPositioner.BarAnchor anchor = null; - - public int size = 1; - private int width = 0; - - public float fill = 0; - public float overflowFill = 0; - public boolean inMouse = false; - - private Object value = ""; - - private int x = 0; - private int y = 0; - - private IconPosition iconPosition = IconPosition.LEFT; - private boolean showText = true; - - public StatusBar(Identifier icon, Color[] colors, boolean hasOverflow, @Nullable Color textColor, Text name) { - this.icon = icon; - this.colors = colors; - this.hasOverflow = hasOverflow; - this.textColor = textColor; - this.name = name; - } - - public StatusBar(Identifier icon, Color[] colors, boolean hasOverflow, @Nullable Color textColor) { - this(icon, colors, hasOverflow, textColor, Text.empty()); - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - if (width <= 0) return; - //FIXME dunno if the shader colour stuff still works - // half works lol. only puts transparency on the filler of the bar - if (inMouse) RenderSystem.setShaderColor(1f, 1f, 1f, 0.25f); - switch (iconPosition) { - case LEFT -> context.drawGuiTexture(RenderLayer::getGuiTextured, icon, x, y, 9, 9); - case RIGHT -> context.drawGuiTexture(RenderLayer::getGuiTextured, icon, x + width - 9, y, 9, 9); - } - - int barWith = iconPosition.equals(IconPosition.OFF) ? width : width - 10; - int barX = iconPosition.equals(IconPosition.LEFT) ? x + 10 : x; - context.drawGuiTexture(RenderLayer::getGuiTextured, BAR_BACK, barX, y + 1, barWith, 7); - RenderHelper.renderNineSliceColored(context, BAR_FILL, barX + 1, y + 2, (int) ((barWith - 2) * fill), 5, colors[0]); - - - if (hasOverflow && overflowFill > 0) { - RenderHelper.renderNineSliceColored(context, BAR_FILL, barX + 1, y + 2, (int) ((barWith - 2) * overflowFill), 5, colors[1]); - } - if (inMouse) RenderSystem.setShaderColor(1f, 1f, 1f, 1f); - //context.drawText(MinecraftClient.getInstance().textRenderer, gridX + " " + gridY + " s:" + size , x, y-9, Colors.WHITE, true); - } - - public void updateValues(float fill, float overflowFill, Object text) { - this.value = text; - this.fill = fill; - this.overflowFill = overflowFill; - } - - public void renderText(DrawContext context) { - TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; - int barWith = iconPosition.equals(IconPosition.OFF) ? width : width - 10; - int barX = iconPosition.equals(IconPosition.LEFT) ? x + 11 : x; - String text = this.value.toString(); - int x = barX + (barWith - textRenderer.getWidth(text)) / 2; - int y = this.y - 3; - - final int[] offsets = new int[]{-1, 1}; - for (int i : offsets) { - context.drawText(textRenderer, text, x + i, y, 0, false); - context.drawText(textRenderer, text, x, y + i, 0, false); - } - context.drawText(textRenderer, text, x, y, (textColor == null ? colors[0] : textColor).getRGB(), false); - } - - public void renderCursor(DrawContext context, int mouseX, int mouseY, float delta) { - int temp_x = x; - int temp_y = y; - int temp_width = width; - boolean temp_ghost = inMouse; - - x = mouseX; - y = mouseY; - width = 100; - inMouse = false; - - render(context, mouseX, mouseY, delta); - - x = temp_x; - y = temp_y; - width = temp_width; - inMouse = temp_ghost; - } - - // GUI shenanigans - - @Override - public void setX(int x) { - this.x = x; - } - - @Override - public void setY(int y) { - this.y = y; - } - - @Override - public int getX() { - return x; - } - - @Override - public int getY() { - return y; - } - - @Override - public int getWidth() { - return width; - } - - public void setWidth(int width) { - this.width = width; - } - - @Override - public int getHeight() { - return 9; - } - - @Override - public ScreenRect getNavigationFocus() { - return Widget.super.getNavigationFocus(); - } - - @Override - public boolean isMouseOver(double mouseX, double mouseY) { - return mouseX >= x && mouseX <= x + getWidth() && mouseY >= y && mouseY <= y + getHeight(); - } - - @Override - public void forEachChild(Consumer consumer) { - } - - @Override - public void setFocused(boolean focused) { - } - - @Override - public boolean isFocused() { - return false; - } - - @Override - public SelectionType getType() { - return SelectionType.NONE; - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (!isMouseOver(mouseX, mouseY)) return false; - if (onClick != null) { - onClick.onClick(this, button, (int) mouseX, (int) mouseY); - } - return true; - } - - public void setOnClick(@Nullable OnClick onClick) { - this.onClick = onClick; - } - - @Override - public void appendNarrations(NarrationMessageBuilder builder) { - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("name", name) - .append("gridX", gridX) - .append("gridY", gridY) - .append("size", size) - .append("x", x) - .append("y", y) - .append("width", width) - .append("anchor", anchor) - .toString(); - } - - public IconPosition getIconPosition() { - return iconPosition; - } - - public void setIconPosition(IconPosition iconPosition) { - this.iconPosition = iconPosition; - } - - public boolean showText() { - return showText; - } - - public void setShowText(boolean showText) { - this.showText = showText; - } - - public enum IconPosition { - LEFT, - RIGHT, - OFF - } - - @FunctionalInterface - public interface OnClick { - - void onClick(StatusBar statusBar, int button, int mouseX, int mouseY); - } - - public void loadFromJson(JsonObject object) { - // Make colors optional, so it's easy to reset to default - if (object.has("colors")) { - JsonArray colors1 = object.get("colors").getAsJsonArray(); - if (colors1.size() < 2 && hasOverflow) { - throw new IllegalStateException("Missing second color of bar that has overflow"); - } - Color[] newColors = new Color[colors1.size()]; - for (int i = 0; i < colors1.size(); i++) { - JsonElement jsonElement = colors1.get(i); - newColors[i] = new Color(Integer.parseInt(jsonElement.getAsString(), 16)); - } - this.colors = newColors; - } - - if (object.has("text_color")) this.textColor = new Color(Integer.parseInt(object.get("text_color").getAsString(), 16)); - - String maybeAnchor = object.get("anchor").getAsString().trim(); - this.anchor = maybeAnchor.equals("null") ? null : BarPositioner.BarAnchor.valueOf(maybeAnchor); - this.size = object.get("size").getAsInt(); - this.gridX = object.get("x").getAsInt(); - this.gridY = object.get("y").getAsInt(); - // these are optional too, why not - if (object.has("icon_position")) this.iconPosition = IconPosition.valueOf(object.get("icon_position").getAsString().trim()); - if (object.has("show_text")) this.showText = object.get("show_text").getAsBoolean(); - - } - - public JsonObject toJson() { - JsonObject object = new JsonObject(); - JsonArray colors1 = new JsonArray(); - for (Color color : colors) { - colors1.add(Integer.toHexString(color.getRGB()).substring(2)); - } - object.add("colors", colors1); - if (textColor != null) { - object.addProperty("text_color", Integer.toHexString(textColor.getRGB()).substring(2)); - } - object.addProperty("size", size); - if (anchor != null) { - object.addProperty("anchor", anchor.toString()); - } else object.addProperty("anchor", "null"); - object.addProperty("x", gridX); - object.addProperty("y", gridY); - object.addProperty("icon_position", iconPosition.toString()); - object.addProperty("show_text", showText); - return object; - } + private final Identifier icon; + + public Color[] getColors() { + return colors; + } + + public boolean hasOverflow() { + return hasOverflow; + } + + public @Nullable Color getTextColor() { + return textColor; + } + + private Color[] colors; + private final boolean hasOverflow; + + public void setTextColor(@Nullable Color textColor) { + this.textColor = textColor; + } + + private @Nullable Color textColor; + + public Text getName() { + return name; + } + + private final Text name; + + private @Nullable OnClick onClick = null; + public int gridX = 0; + public int gridY = 0; + public @Nullable BarPositioner.BarAnchor anchor = null; + + public int size = 1; + private int width = 0; + + public float fill = 0; + public float overflowFill = 0; + public boolean inMouse = false; + + private Object value = "???"; + + private int x = 0; + private int y = 0; + + private IconPosition iconPosition = IconPosition.LEFT; + private TextPosition textPosition = TextPosition.BAR_CENTER; + + public StatusBar(Identifier icon, Color[] colors, boolean hasOverflow, @Nullable Color textColor, Text name) { + this.icon = icon; + this.colors = colors; + this.hasOverflow = hasOverflow; + this.textColor = textColor; + this.name = name; + } + + public StatusBar(Identifier icon, Color[] colors, boolean hasOverflow, @Nullable Color textColor) { + this(icon, colors, hasOverflow, textColor, Text.empty()); + } + + private int transparency(int color) { + if (inMouse) return (color & 0x00FFFFFF) | 0x44_000000; + return color; + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + if (width <= 0) return; + int transparency = transparency(-1); + switch (iconPosition) { + case LEFT -> context.drawGuiTexture(RenderLayer::getGuiTextured, icon, x, y, 9, 9, transparency); + case RIGHT -> context.drawGuiTexture(RenderLayer::getGuiTextured, icon, x + width - 9, y, 9, transparency); + } + + int barWith = iconPosition.equals(IconPosition.OFF) ? width : width - 10; + int barX = iconPosition.equals(IconPosition.LEFT) ? x + 10 : x; + context.drawGuiTexture(RenderLayer::getGuiTextured, BAR_BACK, barX, y + 1, barWith, 7, transparency); + RenderHelper.renderNineSliceColored(context, BAR_FILL, barX + 1, y + 2, (int) ((barWith - 2) * fill), 5, transparency(colors[0].getRGB())); + + + if (hasOverflow && overflowFill > 0) { + RenderHelper.renderNineSliceColored(context, BAR_FILL, barX + 1, y + 2, (int) ((barWith - 2) * overflowFill), 5, transparency(colors[1].getRGB())); + } + //context.drawText(MinecraftClient.getInstance().textRenderer, gridX + " " + gridY + " s:" + size , x, y-9, Colors.WHITE, true); + if (showText()) { + context.getMatrices().push(); + context.getMatrices().translate(0, 0, 100); + renderText(context); + context.getMatrices().pop(); + } + } + + public void updateValues(float fill, float overflowFill, Object text) { + this.value = text; + this.fill = fill; + this.overflowFill = overflowFill; + } + + public void renderText(DrawContext context) { + if (!showText()) return; + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + int barWith = iconPosition.equals(IconPosition.OFF) ? width : width - 10; + int barX = iconPosition.equals(IconPosition.LEFT) ? x + 11 : x; + String text = this.value.toString(); + int textWidth = textRenderer.getWidth(text); + int x; + switch (textPosition) { + case RIGHT -> x = barX + barWith - textWidth; + case CENTER -> x = this.x + (width - textWidth) / 2; + case BAR_CENTER -> x = barX + (barWith - textWidth) / 2; + case null, default -> x = barX; // Put on the left by default because I said so. + } + int y = this.y - 3; + + context.draw(vertex -> textRenderer.drawWithOutline( + Text.of(text).asOrderedText(), + x, + y, + transparency((textColor == null ? colors[0] : textColor).getRGB()), + transparency(0), + context.getMatrices().peek().getPositionMatrix(), + vertex, + 15728880 + )); + } + + public void renderCursor(DrawContext context, int mouseX, int mouseY, float delta) { + int temp_x = x; + int temp_y = y; + int temp_width = width; + boolean temp_ghost = inMouse; + + x = mouseX; + y = mouseY; + width = 100; + inMouse = false; + + render(context, mouseX, mouseY, delta); + + x = temp_x; + y = temp_y; + width = temp_width; + inMouse = temp_ghost; + } + + // GUI shenanigans + + @Override + public void setX(int x) { + this.x = x; + } + + @Override + public void setY(int y) { + this.y = y; + } + + @Override + public int getX() { + return x; + } + + @Override + public int getY() { + return y; + } + + @Override + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + @Override + public int getHeight() { + return 9; + } + + @Override + public ScreenRect getNavigationFocus() { + return Widget.super.getNavigationFocus(); + } + + @Override + public boolean isMouseOver(double mouseX, double mouseY) { + return mouseX >= x && mouseX <= x + getWidth() && mouseY >= y && mouseY <= y + getHeight(); + } + + @Override + public void forEachChild(Consumer consumer) { + } + + @Override + public void setFocused(boolean focused)