aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java37
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java32
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/WindowMixin.java16
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java191
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java13
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarPositioner.java308
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarColorPopup.java117
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java274
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/FancyStatusBars.java288
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/StatusBar.java353
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/StatusBarsConfigScreen.java365
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java71
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java37
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json21
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/bars.pngbin9288 -> 0 bytes
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/sprites/bars/bar_back.pngbin0 -> 196 bytes
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/sprites/bars/bar_back.png.mcmeta10
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/sprites/bars/bar_fill.pngbin0 -> 179 bytes
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/sprites/bars/bar_fill.png.mcmeta10
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/sprites/bars/icons/defense.pngbin0 -> 198 bytes
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/sprites/bars/icons/experience.pngbin0 -> 243 bytes
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/sprites/bars/icons/health.pngbin0 -> 197 bytes
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/sprites/bars/icons/intelligence.pngbin0 -> 194 bytes
-rw-r--r--src/main/resources/skyblocker.mixins.json1
27 files changed, 1891 insertions, 261 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index 091e8548..e0815eee 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -25,6 +25,7 @@ import de.hysky.skyblocker.skyblock.end.BeaconHighlighter;
import de.hysky.skyblocker.skyblock.end.EnderNodes;
import de.hysky.skyblocker.skyblock.end.TheEnd;
import de.hysky.skyblocker.skyblock.entity.MobBoundingBoxes;
+import de.hysky.skyblocker.skyblock.fancybars.FancyStatusBars;
import de.hysky.skyblocker.skyblock.garden.FarmingHud;
import de.hysky.skyblocker.skyblock.garden.LowerSensitivity;
import de.hysky.skyblocker.skyblock.garden.VisitorHelper;
@@ -172,6 +173,7 @@ public class SkyblockerMod implements ClientModInitializer {
Debug.init();
Kuudra.init();
RenderHelper.init();
+ FancyStatusBars.init();
containerSolverManager.init();
statusBarTracker.init();
BeaconHighlighter.init();
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index 86405f58..912636d6 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -305,41 +305,34 @@ public class SkyblockerConfig {
@SerialEntry
public boolean enableBars = true;
+ // Kept in for backwards compatibility, remove if needed
@SerialEntry
- public BarPositions barPositions = new BarPositions();
+ public OldBarPositions barPositions = new OldBarPositions();
}
- public static class BarPositions {
+ /**
+ * Backwards compat
+ */
+ public static class OldBarPositions {
@SerialEntry
- public BarPosition healthBarPosition = BarPosition.LAYER1;
+ public OldBarPosition healthBarPosition = OldBarPosition.LAYER1;
@SerialEntry
- public BarPosition manaBarPosition = BarPosition.LAYER1;
+ public OldBarPosition manaBarPosition = OldBarPosition.LAYER1;
@SerialEntry
- public BarPosition defenceBarPosition = BarPosition.LAYER1;
+ public OldBarPosition defenceBarPosition = OldBarPosition.LAYER1;
@SerialEntry
- public BarPosition experienceBarPosition = BarPosition.LAYER1;
+ public OldBarPosition experienceBarPosition = OldBarPosition.LAYER1;
}
- public enum BarPosition {
- LAYER1, LAYER2, RIGHT, NONE;
-
- @Override
- public String toString() {
- return I18n.translate("text.autoconfig.skyblocker.option.general.bars.barpositions." + name());
- }
-
- public int toInt() {
- return switch (this) {
- case LAYER1 -> 0;
- case LAYER2 -> 1;
- case RIGHT -> 2;
- case NONE -> -1;
- };
- }
+ /**
+ * Backwards compat
+ */
+ public enum OldBarPosition {
+ LAYER1, LAYER2, RIGHT, NONE
}
public static class Experiments {
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
index dbfbbb10..77627242 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
@@ -2,6 +2,7 @@ package de.hysky.skyblocker.config.categories;
import de.hysky.skyblocker.config.ConfigUtils;
import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.skyblock.fancybars.StatusBarsConfigScreen;
import de.hysky.skyblocker.skyblock.shortcut.ShortcutsConfigScreen;
import de.hysky.skyblocker.utils.render.title.TitleContainerConfigScreen;
import de.hysky.skyblocker.utils.waypoint.Waypoint;
@@ -158,33 +159,10 @@ public class GeneralCategory {
newValue -> config.general.bars.enableBars = newValue)
.controller(ConfigUtils::createBooleanController)
.build())
- .option(Option.<SkyblockerConfig.BarPosition>createBuilder()
- .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition"))
- .binding(defaults.general.bars.barPositions.healthBarPosition,
- () -> config.general.bars.barPositions.healthBarPosition,
- newValue -> config.general.bars.barPositions.healthBarPosition = newValue)
- .controller(ConfigUtils::createEnumCyclingListController)
- .build())
- .option(Option.<SkyblockerConfig.BarPosition>createBuilder()
- .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition"))
- .binding(defaults.general.bars.barPositions.manaBarPosition,
- () -> config.general.bars.barPositions.manaBarPosition,
- newValue -> config.general.bars.barPositions.manaBarPosition = newValue)
- .controller(ConfigUtils::createEnumCyclingListController)
- .build())
- .option(Option.<SkyblockerConfig.BarPosition>createBuilder()
- .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition"))
- .binding(defaults.general.bars.barPositions.defenceBarPosition,
- () -> config.general.bars.barPositions.defenceBarPosition,
- newValue -> config.general.bars.barPositions.defenceBarPosition = newValue)
- .controller(ConfigUtils::createEnumCyclingListController)
- .build())
- .option(Option.<SkyblockerConfig.BarPosition>createBuilder()
- .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition"))
- .binding(defaults.general.bars.barPositions.experienceBarPosition,
- () -> config.general.bars.barPositions.experienceBarPosition,
- newValue -> config.general.bars.barPositions.experienceBarPosition = newValue)
- .controller(ConfigUtils::createEnumCyclingListController)
+ .option(ButtonOption.createBuilder()
+ .name(Text.translatable("skyblocker.bars.config.openScreen"))
+ .text(Text.translatable("text.skyblocker.open"))
+ .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new StatusBarsConfigScreen()))
.build())
.build())
diff --git a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java
index 75c516df..b0970b4b 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java
@@ -5,7 +5,7 @@ import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.systems.RenderSystem;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
-import de.hysky.skyblocker.skyblock.FancyStatusBars;
+import de.hysky.skyblocker.skyblock.fancybars.FancyStatusBars;
import de.hysky.skyblocker.skyblock.dungeon.DungeonMap;
import de.hysky.skyblocker.skyblock.dungeon.DungeonScore;
import de.hysky.skyblocker.skyblock.dungeon.DungeonScoreHUD;
diff --git a/src/main/java/de/hysky/skyblocker/mixin/WindowMixin.java b/src/main/java/de/hysky/skyblocker/mixin/WindowMixin.java
new file mode 100644
index 00000000..481a70a6
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/mixin/WindowMixin.java
@@ -0,0 +1,16 @@
+package de.hysky.skyblocker.mixin;
+
+import de.hysky.skyblocker.skyblock.fancybars.FancyStatusBars;
+import net.minecraft.client.util.Window;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(Window.class)
+public class WindowMixin {
+ @Inject(method = "setScaleFactor", at = @At("TAIL"))
+ public void skyblocker$onScaleFactorChange(double scaleFactor, CallbackInfo ci) {
+ FancyStatusBars.updatePositions();
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java
index 8dcccf34..9c14fdc6 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java
@@ -4,6 +4,7 @@ import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.tooltip.TooltipComponent;
import net.minecraft.client.gui.tooltip.TooltipPositioner;
+import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@@ -14,4 +15,7 @@ public interface DrawContextInvoker {
@Invoker
void invokeDrawTooltip(TextRenderer textRenderer, List<TooltipComponent> components, int x, int y, TooltipPositioner positioner);
+
+ @Invoker
+ void invokeDrawTexturedQuad(Identifier texture, int x1, int x2, int y1, int y2, int z, float u1, float u2, float v1, float v2, float red, float green, float blue, float alpha);
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java b/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java
deleted file mode 100644
index 3456d1ad..00000000
--- a/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java
+++ /dev/null
@@ -1,191 +0,0 @@
-package de.hysky.skyblocker.skyblock;
-
-import de.hysky.skyblocker.SkyblockerMod;
-import de.hysky.skyblocker.config.SkyblockerConfigManager;
-import de.hysky.skyblocker.utils.Utils;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.font.TextRenderer;
-import net.minecraft.client.gui.DrawContext;
-import net.minecraft.util.Identifier;
-
-public class FancyStatusBars {
- private static final Identifier BARS = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/bars.png");
-
- private final MinecraftClient client = MinecraftClient.getInstance();
- private final StatusBarTracker statusBarTracker = SkyblockerMod.getInstance().statusBarTracker;
-
- private final StatusBar[] bars = new StatusBar[]{
- new StatusBar(0, 16733525, 2), // Health Bar
- new StatusBar(1, 5636095, 2), // Intelligence Bar
- new StatusBar(2, 12106180, 1), // Defence Bar
- new StatusBar(3, 8453920, 1), // Experience Bar
- };
-
- // Positions to show the bars
- // 0: Hotbar Layer 1, 1: Hotbar Layer 2, 2: Right of hotbar
- // Anything outside the set values hides the bar
- private final int[] anchorsX = new int[3];
- private final int[] anchorsY = new int[3];
-
- public FancyStatusBars() {
- moveBar(0, 0);
- moveBar(1, 0);
- moveBar(2, 0);
- moveBar(3, 0);
- }
-
- private int fill(int value, int max) {
- return (100 * value) / max;
- }
-
- public boolean render(DrawContext context, int scaledWidth, int scaledHeight) {
- var player = client.player;
- if (!SkyblockerConfigManager.get().general.bars.enableBars || player == null || Utils.isInTheRift())
- return false;
- anchorsX[0] = scaledWidth / 2 - 91;
- anchorsY[0] = scaledHeight - 33;
- anchorsX[1] = anchorsX[0];
- anchorsY[1] = anchorsY[0] - 10;
- anchorsX[2] = (scaledWidth / 2 + 91) + 2;
- anchorsY[2] = scaledHeight - 16;
-
- bars[0].update(statusBarTracker.getHealth());
- bars[1].update(statusBarTracker.getMana());
- int def = statusBarTracker.getDefense();
- bars[2].fill[0] = fill(def, def + 100);
- bars[2].text = def;
- bars[3].fill[0] = (int) (100 * player.experienceProgress);
- bars[3].text = player.experienceLevel;
-
- // Update positions of bars from config
- for (int i = 0; i < 4; i++) {
- int configAnchorNum = switch (i) {
- case 0 -> SkyblockerConfigManager.get().general.bars.barPositions.healthBarPosition.toInt();
- case 1 -> SkyblockerConfigManager.get().general.bars.barPositions.manaBarPosition.toInt();
- case 2 -> SkyblockerConfigManager.get().general.bars.barPositions.defenceBarPosition.toInt();
- case 3 -> SkyblockerConfigManager.get().general.bars.barPositions.experienceBarPosition.toInt();
- default -> 0;
- };
-
- if (bars[i].anchorNum != configAnchorNum)
- moveBar(i, configAnchorNum);
- }
-
- for (var bar : bars) {
- bar.draw(context);
- }
- for (var bar : bars) {
- bar.drawText(context);
- }
- return true;
- }
-
- public void moveBar(int bar, int location) {
- // Set the bar to the new anchor
- bars[bar].anchorNum = location;
-
- // Count how many bars are in each location
- int layer1Count = 0, layer2Count = 0;
- for (int i = 0; i < 4; i++) {
- switch (bars[i].anchorNum) {
- case 0 -> layer1Count++;
- case 1 -> layer2Count++;
- }
- }
-
- // Set the bars width and offsetX according to their anchor and how many bars are on that layer
- int adjustedLayer1Count = 0, adjustedLayer2Count = 0, adjustedRightCount = 0;
- for (int i = 0; i < 4; i++) {
- switch (bars[i].anchorNum) {
- case 0 -> {
- bars[i].bar_width = (172 - ((layer1Count - 1) * 11)) / layer1Count;
- bars[i].offsetX = adjustedLayer1Count * (bars[i].bar_width + 11 + (layer1Count == 3 ? 0 : 1));
- adjustedLayer1Count++;
- }
- case 1 -> {
- bars[i].bar_width = (172 - ((layer2Count - 1) * 11)) / layer2Count;
- bars[i].offsetX = adjustedLayer2Count * (bars[i].bar_width + 11 + (layer2Count == 3 ? 0 : 1));
- adjustedLayer2Count++;
- }
- case 2 -> {
- bars[i].bar_width = 50;
- bars[i].offsetX = adjustedRightCount * (50 + 11);
- adjustedRightCount++;
- }
- }
- }
- }
-
- private class StatusBar {
- public final int[] fill;
- public int offsetX;
- private final int v;
- private final int text_color;
- public int anchorNum;
- public int bar_width;
- public Object text;
-
- private StatusBar(int i, int textColor, int fillNum) {
- this.v = i * 9;
- this.text_color = textColor;
- this.fill = new int[fillNum];
- this.fill[0] = 100;
- this.anchorNum = 0;
- this.text = "";
- }
-
- public void update(StatusBarTracker.Resource resource) {
- int max = resource.max();
- int val = resource.value();
- this.fill[0] = fill(val, max);
- this.fill[1] = fill(resource.overflow(), max);
- this.text = val;
- }
-
- public void draw(DrawContext context) {
- // Dont draw if anchorNum is outside of range
- if (anchorNum < 0 || anchorNum > 2) return;
-
- // Draw the icon for the bar
- context.drawTexture(BARS, anchorsX[anchorNum] + offsetX, anchorsY[anchorNum], 0, v, 9, 9);
-
- // Draw the background for the bar
- context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10, anchorsY[anchorNum], 10, v, 2, 9);
- for (int i = 2; i < bar_width - 2; i += 58) {
- context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10 + i, anchorsY[anchorNum], 12, v, Math.min(58, bar_width - 2 - i), 9);
- }
- context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10 + bar_width - 2, anchorsY[anchorNum], 70, v, 2, 9);
-
- // Draw the filled part of the bar
- for (int i = 0; i < fill.length; i++) {
- int fill_width = this.fill[i] * (bar_width - 2) / 100;
- if (fill_width >= 1) {
- context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11, anchorsY[anchorNum], 72 + i * 60, v, 1, 9);
- }
- for (int j = 1; j < fill_width - 1; j += 58) {
- context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11 + j, anchorsY[anchorNum], 73 + i * 60, v, Math.min(58, fill_width - 1 - j), 9);
- }
- if (fill_width == bar_width - 2) {
- context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11 + fill_width - 1, anchorsY[anchorNum], 131 + i * 60, v, 1, 9);
- }
- }
- }
-
- public void drawText(DrawContext context) {
- // Dont draw if anchorNum is outside of range
- if (anchorNum < 0 || anchorNum > 2) return;
-
- TextRenderer textRenderer = client.textRenderer;
- String text = this.text.toString();
- int x = anchorsX[anchorNum] + this.offsetX + 11 + (bar_width - textRenderer.getWidth(text)) / 2;
- int y = anchorsY[anchorNum] - 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, text_color, false);
- }
- }
-}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java b/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java
index 6b90b86c..9d460803 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java
@@ -34,18 +34,7 @@ public class EditBidPopup extends AbstractPopupScreen {
super.init();
layout = DirectionalLayoutWidget.vertical();
layout.spacing(8).getMainPositioner().alignHorizontalCenter();
- textFieldWidget = new TextFieldWidget(textRenderer, 120, 15, Text.empty()) {
- @Override
- public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
- if (!super.keyPressed(keyCode, scanCode, modifiers)) {
- if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) {
- done(null);
- return true;
- }
- } else return true;
- return false;
- }
- };
+ textFieldWidget = new EnterConfirmTextFieldWidget(textRenderer, 120, 15, Text.empty(), () -> done(null));
textFieldWidget.setTextPredicate(this::isStringGood);
layout.add(new TextWidget(Text.literal("- Set Bid -").fillStyle(Style.EMPTY.withBold(true)), textRenderer));
layout.add(textFieldWidget);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarPositioner.java b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarPositioner.java
new file mode 100644
index 00000000..a72b955b
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarPositioner.java
@@ -0,0 +1,308 @@
+package de.hysky.skyblocker.skyblock.fancybars;
+
+import net.minecraft.client.gui.ScreenPos;
+import net.minecraft.client.gui.ScreenRect;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+public class BarPositioner {
+
+ private final Map<BarAnchor, LinkedList<LinkedList<StatusBar>>> map = new HashMap<>(BarAnchor.values().length);
+
+ public BarPositioner() {
+ for (BarAnchor value : BarAnchor.values()) {
+ map.put(value, new LinkedList<>());
+ }
+ }
+
+
+ public int getRowCount(@NotNull BarAnchor barAnchor) {
+ return map.get(barAnchor).size();
+ }
+
+ /**
+ * Adds a row to the end of an anchor
+ *
+ * @param barAnchor the anchor
+ */
+ public void addRow(@NotNull BarAnchor barAnchor) {
+ map.get(barAnchor).add(new LinkedList<>());
+ }
+
+ /**
+ * Adds a row at the specified index
+ *
+ * @param barAnchor the anchor
+ * @param row row index
+ */
+ public void addRow(@NotNull BarAnchor barAnchor, int row) {
+ map.get(barAnchor).add(row, new LinkedList<>());
+ }
+
+ /**
+ * adds a bar to the end of a row
+ *
+ * @param barAnchor the anchor
+ * @param row the row
+ * @param bar the bar to add
+ */
+ public void addBar(@NotNull BarAnchor barAnchor, int row, StatusBar bar) {
+ LinkedList<StatusBar> statusBars = map.get(barAnchor).get(row);
+ statusBars.add(bar);
+ bar.gridY = row;
+ bar.gridX = statusBars.lastIndexOf(bar); // optimization baby, start with the end!
+ bar.anchor = barAnchor;
+ }
+
+ /**
+ * adds a bar to the specified x in a row
+ *
+ * @param barAnchor the anchor
+ * @param row the row
+ * @param x the index in the row
+ * @param bar the bar to add
+ */
+ public void addBar(@NotNull BarAnchor barAnchor, int row, int x, StatusBar bar) {
+ LinkedList<StatusBar> statusBars = map.get(barAnchor).get(row);
+ statusBars.add(x, bar);
+ bar.gridY = row;
+ bar.gridX = statusBars.indexOf(bar);
+ bar.anchor = barAnchor;
+ }
+
+ /**
+ * removes the specified bar at x on the row. If it's row is empty after being removed, the row will be auto removed
+ *
+ * @param barAnchor the anchor
+ * @param row dah row
+ * @param x dah x
+ */
+ public void removeBar(@NotNull BarAnchor barAnchor, int row, int x) {
+ LinkedList<StatusBar> statusBars = map.get(barAnchor).get(row);
+ StatusBar remove = statusBars.remove(x);
+ remove.anchor = null;
+ for (int i = x; i < statusBars.size(); i++) {
+ statusBars.get(i).gridX--;
+ }
+ if (statusBars.isEmpty()) removeRow(barAnchor, row);
+ }
+
+ /**
+ * removes the specified bar on the row. If it's row is empty after being removed, the row will be auto removed
+ *
+ * @param barAnchor the anchor
+ * @param row dah row
+ * @param bar dah bar
+ */
+ public void removeBar(@NotNull BarAnchor barAnchor, int row, StatusBar bar) {
+ LinkedList<StatusBar> barRow = map.get(barAnchor).get(row);
+ int x = barRow.indexOf(bar);
+ if (x < 0) return; // probably a bad idea
+
+ barRow.remove(bar);
+ bar.anchor = null;
+ for (int i = x; i < barRow.size(); i++) {
+ barRow.get(i).gridX--;
+ }
+ if (barRow.isEmpty()) removeRow(barAnchor, row);
+ }
+
+ /**
+ * row must be empty
+ *
+ * @param barAnchor the anchor
+ * @param row the row to remove
+ */
+ public void removeRow(@NotNull BarAnchor barAnchor, int row) {
+ LinkedList<StatusBar> barRow = map.get(barAnchor).get(row);
+ if (!barRow.isEmpty())
+ throw new IllegalStateException("Can't remove a non-empty row (" + barAnchor + "," + row + ")");
+ map.get(barAnchor).remove(row);
+ for (int i = row; i < map.get(barAnchor).size(); i++) {
+ for (StatusBar statusBar : map.get(barAnchor).get(i)) {
+ statusBar.gridY--;
+ }
+ }
+ }
+
+
+ public LinkedList<StatusBar> getRow(@NotNull BarAnchor barAnchor, int row) {
+ return map.get(barAnchor).get(row);
+ }
+
+ public StatusBar getBar(@NotNull BarAnchor barAnchor, int row, int x) {
+ return map.get(barAnchor).get(row).get(x);
+ }
+
+ public boolean hasNeighbor(@NotNull BarAnchor barAnchor, int row, int x, boolean right) {
+ LinkedList<StatusBar> statusBars = map.get(barAnchor).get(row);
+ if (barAnchor.isRight()) {
+ return (right && x < statusBars.size() - 1) || (!right && x > 0);
+ } else {
+ return (right && x > 0) || (!right && x < statusBars.size() - 1);
+ }
+ }
+
+
+ public enum BarAnchor {
+ HOTBAR_LEFT(true, false,
+ (scaledWidth, scaledHeight) -> new ScreenPos(scaledWidth / 2 - 91 - 2, scaledHeight - 5),
+ SizeRule.freeSize(25, 2, 6)),
+
+ HOTBAR_RIGHT(true, true,
+ (scaledWidth, scaledHeight) -> new ScreenPos(scaledWidth / 2 + 91 + 2, scaledHeight - 5),
+ SizeRule.freeSize(25, 2, 6)),
+
+ HOTBAR_TOP(true, true,
+ (scaledWidth, scaledHeight) -> new ScreenPos(scaledWidth / 2 - 91, scaledHeight - 23),
+ SizeRule.targetSize(12, 182, 2),
+ anchorPosition -> new ScreenRect(anchorPosition.x(), anchorPosition.y() - 20, 182, 20)),
+
+ SCREEN_TOP_LEFT(false, true,
+ ((scaledWidth, scaledHeight) -> new ScreenPos(5, 5)),
+ SizeRule.freeSize(25, 2, 6)
+ ),
+ SCREEN_TOP_RIGHT(false, false,
+ ((scaledWidth, scaledHeight) -> new ScreenPos(scaledWidth - 5, 5)),
+ SizeRule.freeSize(25, 2, 6)
+ ),
+ SCREEN_BOTTOM_LEFT(true, true,
+ ((scaledWidth, scaledHeight) -> new ScreenPos(5, scaledHeight - 5)),
+ SizeRule.freeSize(25, 2, 6)
+ ),
+ SCREEN_BOTTOM_RIGHT(true, false,
+ ((scaledWidth, scaledHeight) -> new ScreenPos(scaledWidth - 5, scaledHeight - 5)),
+ SizeRule.freeSize(25, 2, 6)
+ );
+
+ private final AnchorPositionProvider positionProvider;
+ private final AnchorHitboxProvider hitboxProvider;
+ private final boolean up;
+ private final boolean right;
+ private final SizeRule sizeRule;
+
+ /**
+ * @param up whether the rows stack towards the top of the screen from the anchor (false is bottom)
+ * @param right whether the bars are line up towards the right of the screen from the anchor (false is left)
+ * @param positionProvider provides the position of the anchor for a give screen size
+ * @param sizeRule the rule the bars should follow. See {@link SizeRule}
+ * @param hitboxProvider provides the hitbox for when the anchor has no bars for the config screen
+ */
+ BarAnchor(boolean up, boolean right, AnchorPositionProvider positionProvider, SizeRule sizeRule, AnchorHitboxProvider hitboxProvider) {
+ this.positionProvider = positionProvider;
+ this.up = up;
+ this.right = right;
+ this.hitboxProvider = hitboxProvider;
+ this.sizeRule = sizeRule;
+ }
+
+ BarAnchor(boolean up, boolean right, AnchorPositionProvider positionProvider, SizeRule sizeRule) {
+ this(up, right, positionProvider, sizeRule,
+ anchorPosition -> new ScreenRect(anchorPosition.x() - (right ? 0 : 20), anchorPosition.y() - (up ? 20 : 0), 20, 20));
+ }
+
+ public ScreenPos getAnchorPosition(int scaledWidth, int scaledHeight) {
+ return positionProvider.getPosition(scaledWidth, scaledHeight);
+ }
+
+ public ScreenRect getAnchorHitbox(ScreenPos anchorPosition) {
+ return hitboxProvider.getHitbox(anchorPosition);
+ }
+
+ /**
+ * whether the rows stack towards the top of the screen from the anchor (false is bottom)
+ *
+ * @return true if towards the top, false otherwise
+ */
+ public boolean isUp() {
+ return up;
+ }
+
+ /**
+ * whether the bars are line up towards the right of the screen from the anchor (false is left)
+ *
+ * @return true if towards the right, false otherwise
+ */
+ public boolean isRight() {
+ return right;
+ }
+
+ public SizeRule getSizeRule() {
+ return sizeRule;
+ }
+
+ private static final List<BarAnchor> cached = List.of(values());
+
+ /**
+ * cached version of {@link BarAnchor#values()}
+ *
+ * @return the list of anchors
+ */
+ public static List<BarAnchor> allAnchors() {
+ return cached;
+ }
+ }
+
+ /**
+ * The rules the bars on an anchor should follow
+ *
+ * @param isTargetSize whether the bars went to fit to a target width
+ * @param targetSize the size of all the bars on a row should add up to this (target size)
+ * @param totalWidth the total width taken by all the bars on the row (target size)
+ * @param widthPerSize the width of each size "unit" (free size)
+ * @param minSize the minimum (free and target size)
+ * @param maxSize the maximum (free and target size, THIS SHOULD BE THE SAME AS {@code targetSize} FOR {@code isTargetSize = true})
+ */
+ public record SizeRule(boolean isTargetSize, int targetSize, int totalWidth, int widthPerSize, int minSize, int maxSize) {
+ public static SizeRule freeSize(int widthPerSize, int minSize, int maxSize) {
+ return new SizeRule(false, -1, -1, widthPerSize, minSize, maxSize);
+ }
+
+ public static SizeRule targetSize(int targetSize, int totalWidth, int minSize) {
+ return new SizeRule(true, targetSize, totalWidth, -1, minSize, targetSize);
+ }
+ }
+
+ /**
+ * A record representing a snapshot of a bar's position
+ *
+ * @param barAnchor
+ * @param x
+ * @param y the row
+ */
+ public record BarLocation(@Nullable BarAnchor barAnchor, int x, int y) {
+
+ public static final BarLocation NULL = new BarLocation(null, -1, -1);
+
+ public static BarLocation of(StatusBar bar) {
+ return new BarLocation(bar.anchor, bar.gridX, bar.gridY);
+ }
+
+ public boolean equals(BarAnchor barAnchor, int x, int y) {
+ return x == this.x && y == this.y && barAnchor == this.barAnchor;
+ }
+ }
+
+ /**
+ * provides the position of the anchor for a give screen size
+ */
+ @FunctionalInterface
+ interface AnchorPositionProvider {
+
+ ScreenPos getPosition(int scaledWidth, int scaledHeight);
+ }
+
+ @FunctionalInterface
+ interface AnchorHitboxProvider {
+
+ /**
+ * The hitbox, as in how large the area of "snapping" is if there are no bars on this anchor
+ *
+ * @param anchorPosition the position of the anchor
+ * @return