diff options
| author | Kevin <92656833+kevinthegreat1@users.noreply.github.com> | 2024-04-21 16:10:15 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-21 16:10:15 -0400 |
| commit | 01b4ff489368182d164e2d38560c7a06183018b8 (patch) | |
| tree | 92f88214b70e824aa87bf2dc9eb899696134bd7d | |
| parent | 95fa171cd0f981f77a9a64aa77d9d69b81503253 (diff) | |
| parent | c43370daf17ea547f80908cf9391fac1d7756b45 (diff) | |
| download | Skyblocker-01b4ff489368182d164e2d38560c7a06183018b8.tar.gz Skyblocker-01b4ff489368182d164e2d38560c7a06183018b8.tar.bz2 Skyblocker-01b4ff489368182d164e2d38560c7a06183018b8.zip | |
Merge pull request #654 from viciscat/fancier-bars
Fancier bars
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 representi |
