aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormsg-programs <msgdoesstuff@gmail.com>2023-04-02 14:58:42 +0200
committermsg-programs <msgdoesstuff@gmail.com>2023-04-02 14:58:42 +0200
commit9cbce39d68c1c766e20819101fbb803ad05e8fdc (patch)
tree164b9f11c4c572bdd632aa16b53fc8a9634f7331 /src
parent3074075c1561163606f441f1a8f582b79082c311 (diff)
downloadSkyblocker-9cbce39d68c1c766e20819101fbb803ad05e8fdc.tar.gz
Skyblocker-9cbce39d68c1c766e20819101fbb803ad05e8fdc.tar.bz2
Skyblocker-9cbce39d68c1c766e20819101fbb803ad05e8fdc.zip
Merging SkyHyTab into Skyblocker: Initial commit
Needs cleanup
Diffstat (limited to 'src')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java18
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java70
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonHubScreen.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonPlayerScreen.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonScreen.java37
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/FarmingServerScreen.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GardenScreen.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GenericInfoScreen.java51
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GenericServerScreen.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GuestPlayerScreen.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GuestServerScreen.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HomePlayerScreen.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HomeServerScreen.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HubServerScreen.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/MineServerScreen.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/ParkServerScreen.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/PlayerListScreen.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java250
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java50
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/StrMan.java64
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java53
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java53
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java42
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java83
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java51
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java35
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java56
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java97
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java27
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java48
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java56
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java60
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java59
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java41
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java135
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java42
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java61
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java35
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java42
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java63
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java29
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java38
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java183
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java29
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java49
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java53
-rw-r--r--src/main/resources/skyblocker.mixins.json4
64 files changed, 2903 insertions, 1 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java
index 3d713727..c866ea01 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java
@@ -9,6 +9,7 @@ import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud;
import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup;
import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
import me.xmrvizzy.skyblocker.utils.UpdateChecker;
import net.fabricmc.api.ClientModInitializer;
@@ -25,5 +26,6 @@ public class SkyblockerInitializer implements ClientModInitializer {
ChatMessageListener.init();
UpdateChecker.init();
DiscordRPCManager.init();
+ TabHud.init();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
index 21322c81..41ac7f2d 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
@@ -125,6 +125,8 @@ public class SkyblockerConfig implements ConfigData {
public final boolean enableUpdateNotification = true;
public final boolean backpackPreviewWithoutShift = false;
+ public boolean tabHudEnabled = true;
+
@ConfigEntry.Gui.Excluded
public String apiKey;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java
new file mode 100644
index 00000000..e96e4ede
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java
@@ -0,0 +1,18 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import java.util.Comparator;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.client.gui.hud.PlayerListHud;
+import net.minecraft.client.network.PlayerListEntry;
+
+@Mixin(PlayerListHud.class)
+public interface PlayerListHudAccessor {
+
+ @Accessor("ENTRY_ORDERING")
+ public static Comparator<PlayerListEntry> getOrdering() {
+ throw new AssertionError();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
new file mode 100644
index 00000000..36d4a375
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
@@ -0,0 +1,70 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import java.util.List;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.hud.PlayerListHud;
+import net.minecraft.client.network.ClientPlayNetworkHandler;
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraft.scoreboard.ScoreboardObjective;
+import net.minecraft.text.Text;
+
+@Environment(EnvType.CLIENT)
+@Mixin(PlayerListHud.class)
+public class PlayerListHudMixin {
+
+ @Shadow
+ private Text footer;
+
+ boolean ok = true;
+ @Inject(at = @At("HEAD"), method = "render(Lnet/minecraft/client/util/math/MatrixStack;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreboardObjective;)V", cancellable = true)
+ public void skyblocker$renderTabHud(MatrixStack ms, int scaledW, Scoreboard sb, ScoreboardObjective sbo, CallbackInfo info) {
+
+ if (!Utils.isOnSkyblock) {
+ return;
+ }
+
+ if (TabHud.defaultTgl.isPressed()) {
+ return;
+ }
+
+ MinecraftClient client = MinecraftClient.getInstance();
+ ClientPlayNetworkHandler nwH = client.getNetworkHandler();
+ if (nwH == null) {
+ return;
+ }
+
+ List<PlayerListEntry> list = nwH.getListedPlayerListEntries().stream().sorted(PlayerListHudAccessor.getOrdering()).toList();
+ int w = scaledW;
+ int h = MinecraftClient.getInstance().getWindow().getScaledHeight();
+
+ try {
+
+ Screen screen = Screen.getCorrect(w, h, list, footer);
+ if (screen != null) {
+ screen.render(ms);
+ info.cancel();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ SkyblockerConfig.get().general.tabHudEnabled = false;
+ MinecraftClient.getInstance().player.sendMessage(Text.of("The tab HUD has crashed due to some unexpected text, see log! :("));
+ }
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java
new file mode 100644
index 00000000..3cc75e00
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud;
+
+import org.lwjgl.glfw.GLFW;
+
+import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
+import net.minecraft.client.option.KeyBinding;
+import net.minecraft.client.util.InputUtil;
+
+public class TabHud {
+
+ public static KeyBinding playerTgl;
+ public static KeyBinding genericTgl;
+ // public static KeyBinding mapTgl;
+ public static KeyBinding defaultTgl;
+
+ public static void init() {
+
+ playerTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyhytab.playerTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_LEFT_SHIFT,
+ "key.categories.skyblocker"));
+ genericTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.tabhud.genericTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_LEFT_ALT,
+ "key.categories.skyblocker"));
+ // mapTgl = KeyBindingHelper.registerKeyBinding(
+ // new KeyBinding("key.tabhud.mapTgl",
+ // InputUtil.Type.KEYSYM,
+ // GLFW.GLFW_KEY_LEFT_ALT,
+ // "key.categories.skyblocker"));
+ defaultTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.tabhud.defaultTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_B,
+ "key.categories.skyblocker"));
+
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonHubScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonHubScreen.java
new file mode 100644
index 00000000..4bd48f89
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonHubScreen.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EssenceWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class DungeonHubScreen extends Screen{
+
+ public DungeonHubScreen(int w, int h, List<PlayerListEntry> list, Text footer) {
+ super(w, h);
+ ServerWidget sw = new ServerWidget(list);
+ EssenceWidget ew = new EssenceWidget(list);
+ centerW(sw);
+ centerW(ew);
+ stackWidgetsH(sw, ew);
+ this.addWidget(ew);
+ this.addWidget(sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonPlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonPlayerScreen.java
new file mode 100644
index 00000000..ce87a035
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonPlayerScreen.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonPlayerWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class DungeonPlayerScreen extends Screen {
+
+ public DungeonPlayerScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+ DungeonPlayerWidget dpw1 = new DungeonPlayerWidget(ple, 1);
+ DungeonPlayerWidget dpw2 = new DungeonPlayerWidget(ple, 2);
+ DungeonPlayerWidget dpw3 = new DungeonPlayerWidget(ple, 3);
+ DungeonPlayerWidget dpw4 = new DungeonPlayerWidget(ple, 4);
+ DungeonPlayerWidget dpw5 = new DungeonPlayerWidget(ple, 5);
+
+ offCenterL(dpw1);
+ offCenterL(dpw2);
+ offCenterL(dpw3);
+ offCenterR(dpw4);
+ offCenterR(dpw5);
+ stackWidgetsH(dpw1, dpw2, dpw3);
+ stackWidgetsH(dpw4, dpw5);
+ addWidgets(dpw1, dpw2, dpw3, dpw4, dpw5);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonScreen.java
new file mode 100644
index 00000000..bb80b01b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/DungeonScreen.java
@@ -0,0 +1,37 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonDeathWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonDownedWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonPuzzleWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonSecretWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonServerWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class DungeonScreen extends Screen {
+
+ public DungeonScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+ DungeonDownedWidget ddow = new DungeonDownedWidget(ple);
+ DungeonDeathWidget ddew = new DungeonDeathWidget(ple);
+ DungeonSecretWidget dscw = new DungeonSecretWidget(ple);
+ DungeonServerWidget dsrw = new DungeonServerWidget(ple);
+ DungeonPuzzleWidget dpuw = new DungeonPuzzleWidget(ple);
+
+ offCenterL(ddow);
+ offCenterL(ddew);
+ offCenterL(dscw);
+ offCenterR(dsrw);
+ offCenterR(dpuw);
+
+ stackWidgetsH(ddow, ddew, dscw);
+ stackWidgetsH(dsrw, dpuw);
+
+ addWidgets(ddow, ddew, dscw, dsrw, dpuw);
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java
new file mode 100644
index 00000000..07162834
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EmptyWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class EmptyScreen extends Screen {
+
+ public EmptyScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+ EmptyWidget ew = new EmptyWidget(ple);
+ this.center(ew);
+ this.addWidget(ew);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/FarmingServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/FarmingServerScreen.java
new file mode 100644
index 00000000..f66638a9
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/FarmingServerScreen.java
@@ -0,0 +1,23 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.TrapperWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class FarmingServerScreen extends Screen{
+
+ public FarmingServerScreen(int w, int h, List<PlayerListEntry> list, Text footer) {
+ super(w, h);
+ ServerWidget sw = new ServerWidget(list);
+ TrapperWidget tw = new TrapperWidget(list);
+ centerW(sw);
+ centerW(tw);
+ stackWidgetsH(sw, tw);
+ this.addWidgets(tw, sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GardenScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GardenScreen.java
new file mode 100644
index 00000000..30f4bbc3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GardenScreen.java
@@ -0,0 +1,23 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ComposterWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GardenServerWidget;
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class GardenScreen extends Screen{
+
+ public GardenScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+ GardenServerWidget gsw = new GardenServerWidget(ple);
+ ComposterWidget cw = new ComposterWidget(ple);
+
+ this.stackWidgetsH(gsw, cw);
+ this.centerW(gsw);
+ this.centerW(cw);
+ this.addWidgets(gsw, cw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GenericInfoScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GenericInfoScreen.java
new file mode 100644
index 00000000..01b159e2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GenericInfoScreen.java
@@ -0,0 +1,51 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CookieWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EffectWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ElectionWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EventWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ProfileWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.SkillsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.UpgradeWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class GenericInfoScreen extends Screen {
+
+ public GenericInfoScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ SkillsWidget sw = new SkillsWidget(ple);
+ EventWidget evw = new EventWidget(ple);
+ UpgradeWidget uw = new UpgradeWidget(f);
+
+ ProfileWidget pw = new ProfileWidget(ple);
+ EffectWidget efw = new EffectWidget(f);
+
+ ElectionWidget elw = new ElectionWidget(ple);
+ CookieWidget cw = new CookieWidget(f);
+
+ // goofy ahh layout code incoming
+ this.stackWidgetsH(sw, evw, uw);
+ this.stackWidgetsH(pw, efw);
+ this.stackWidgetsH(elw, cw);
+
+ this.centerW(sw);
+ this.centerW(evw);
+ this.centerW(uw);
+
+ this.collideAgainstL(pw, sw, evw, uw);
+ this.collideAgainstL(efw, sw, evw, uw);
+
+ this.collideAgainstR(elw, sw, evw, uw);
+ this.collideAgainstR(cw, sw, evw, uw);
+
+ this.addWidgets(sw, evw, uw, pw, efw, elw, cw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GenericServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GenericServerScreen.java
new file mode 100644
index 00000000..fee4a9f8
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GenericServerScreen.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class GenericServerScreen extends Screen {
+
+ public GenericServerScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+ ServerWidget sw = new ServerWidget(ple);
+ this.center(sw);
+ this.addWidget(sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GuestPlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GuestPlayerScreen.java
new file mode 100644
index 00000000..4647da7a
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GuestPlayerScreen.java
@@ -0,0 +1,23 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandGuestsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandOwnersWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class GuestPlayerScreen extends Screen{
+
+ public GuestPlayerScreen(int w, int h, List<PlayerListEntry> list, Text footer) {
+ super(w, h);
+ IslandGuestsWidget igw = new IslandGuestsWidget(list);
+ IslandOwnersWidget iow = new IslandOwnersWidget(list);
+ this.centerH(iow);
+ this.centerH(igw);
+ this.stackWidgetsW(igw, iow);
+ this.addWidgets(iow, igw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GuestServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GuestServerScreen.java
new file mode 100644
index 00000000..03e22292
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/GuestServerScreen.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GuestServerWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class GuestServerScreen extends Screen{
+
+ public GuestServerScreen(int w, int h, List<PlayerListEntry> list, Text footer) {
+ super(w, h);
+ GuestServerWidget gsw = new GuestServerWidget(list);
+ this.center(gsw);
+ this.addWidget(gsw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HomePlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HomePlayerScreen.java
new file mode 100644
index 00000000..506f773f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HomePlayerScreen.java
@@ -0,0 +1,22 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandGuestsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandSelfWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class HomePlayerScreen extends Screen {
+
+ public HomePlayerScreen(int w, int h, List<PlayerListEntry> list, Text footer) {
+ super(w, h);
+ IslandSelfWidget isw = new IslandSelfWidget(list);
+ IslandGuestsWidget igw = new IslandGuestsWidget(list);
+ this.centerH(isw);
+ this.centerH(igw);
+ this.stackWidgetsW(isw, igw);
+ this.addWidgets(isw, igw);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HomeServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HomeServerScreen.java
new file mode 100644
index 00000000..6d67f829
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HomeServerScreen.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandServerWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.MinionWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class HomeServerScreen extends Screen {
+
+ public HomeServerScreen(int w, int h, List<PlayerListEntry> list, Text footer) {
+ super(w, h);
+
+ IslandServerWidget isw = new IslandServerWidget(list);
+ MinionWidget mw = new MinionWidget(list);
+ this.centerH(isw);
+ this.centerH(mw);
+ this.stackWidgetsW(isw, mw);
+ this.addWidgets(isw, mw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HubServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HubServerScreen.java
new file mode 100644
index 00000000..15ac3183
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/HubServerScreen.java
@@ -0,0 +1,23 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.FireSaleWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class HubServerScreen extends Screen {
+
+ public HubServerScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+ ServerWidget sw = new ServerWidget(ple);
+ FireSaleWidget fsw = new FireSaleWidget(ple);
+ this.centerW(sw);
+ this.centerW(fsw);
+ this.stackWidgetsH(sw, fsw);
+ this.addWidgets(sw, fsw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/MineServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/MineServerScreen.java
new file mode 100644
index 00000000..0e581852
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/MineServerScreen.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CommsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ForgeWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.PowderWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class MineServerScreen extends Screen{
+
+ public MineServerScreen(int w, int h, List<PlayerListEntry> list, Text footer) {
+ super(w, h);
+ ServerWidget sw = new ServerWidget(list);
+ PowderWidget pw = new PowderWidget(list);
+ CommsWidget cw = new CommsWidget(list);
+ ForgeWidget fw = new ForgeWidget(list);
+ stackWidgetsH(sw, pw, cw);
+ centerH(fw);
+ offCenterL(sw);
+ offCenterL(pw);
+ offCenterL(cw);
+ offCenterR(fw);
+ this.addWidgets(fw, cw, pw, sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/ParkServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/ParkServerScreen.java
new file mode 100644
index 00000000..bb601251
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/ParkServerScreen.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ParkServerWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class ParkServerScreen extends Screen{
+
+ public ParkServerScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+ ParkServerWidget sw = new ParkServerWidget(ple);
+ this.center(sw);
+ this.addWidget(sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/PlayerListScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/PlayerListScreen.java
new file mode 100644
index 00000000..14adc322
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/PlayerListScreen.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.PlayerListWidget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class PlayerListScreen extends Screen {
+
+ public PlayerListScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+ PlayerListWidget plw = new PlayerListWidget(ple);
+ this.center(plw);
+ this.addWidget(plw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java
new file mode 100644
index 00000000..8e99698b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java
@@ -0,0 +1,250 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.Text;
+
+public class Screen {
+
+ private enum ScreenType {
+ DUNGEON,
+ GUEST_ISLAND,
+ HOME_ISLAND,
+ CRIMSON_ISLE,
+ DUNGEON_HUB,
+ FARMING_ISLAND,
+ PARK,
+ DWARVEN_MINES,
+ CRYSTAL_HOLLOWS,
+ END,
+ GOLD_MINE,
+ DEEP_CAVERNS,
+ HUB,
+ SPIDER_DEN,
+ JERRY,
+ GARDEN,
+ NONE
+ }
+
+ private ArrayList<Widget> widgets = new ArrayList<>();
+ private int w, h;
+
+ public Screen(int w, int h) {
+ this.w = w;
+ this.h = h;
+ }
+
+ public void addWidget(Widget w) {
+ widgets.add(w);
+ }
+
+ public void addWidgets(Widget... ws) {
+ for (Widget w : ws) {
+ widgets.add(w);
+ }
+ }
+
+ public void render(MatrixStack ms) {
+ for (Widget w : widgets) {
+ w.render(ms);
+ }
+ }
+
+ public void stackWidgetsH(Widget... list) {
+ int compHeight = -5;
+ for (Widget wid : list) {
+ compHeight += wid.getHeight() + 5;
+ }
+
+ int y = (h - compHeight) / 2;
+ for (Widget wid : list) {
+ wid.setY(y);
+ y += wid.getHeight() + 5;
+ }
+ }
+
+ public void stackWidgetsW(Widget... list) {
+ // TODO not centered
+ int compWidth = -5;
+ for (Widget wid : list) {
+ compWidth += wid.getWidth() + 5;
+ }
+
+ int x = (w - compWidth) / 2;
+ for (Widget wid : list) {
+ wid.setX(x);
+ x += wid.getWidth() + 5;
+ }
+ }
+
+ public void centerH(Widget wid) {
+ wid.setY((h - wid.getHeight()) / 2);
+ }
+
+ public void centerW(Widget wid) {
+ wid.setX((w - wid.getWidth()) / 2);
+ }
+
+ public void center(Widget wid) {
+ this.centerH(wid);
+ this.centerW(wid);
+ }
+
+ public void offCenterL(Widget wid) {
+ int wHalf = this.w / 2;
+ wid.setX(wHalf - 3 - wid.getWidth());
+ }
+
+ public void offCenterR(Widget wid) {
+ int wHalf = this.w / 2;
+ wid.setX(wHalf + 3);
+ }
+
+ public void collideAgainstL(Widget w, Widget... others) {
+ int yMin = w.getY();
+ int yMax = w.getY() + w.getHeight();
+
+ int xCor = this.w / 2;
+
+ // assume others to be sorted top-bottom.
+ for (Widget other : others) {
+ if (other.getY() + other.getHeight() + 5 < yMin) {
+ // too high, next one
+ continue;
+ }
+
+ if (other.getY() - 5 > yMax) {
+ // too low, no more collisions possible
+ break;
+ }
+
+ int xPos = other.getX() - 5 - w.getWidth();
+ xCor = Math.min(xCor, xPos);
+ }
+ w.setX(xCor);
+ }
+
+ public void collideAgainstR(Widget w, Widget... others) {
+ int yMin = w.getY();
+ int yMax = w.getY() + w.getHeight();
+
+ int xCor = this.w / 2;
+
+ // assume others to be sorted top-bottom.
+ for (Widget other : others) {
+ if (other.getY() + other.getHeight() + 5 < yMin) {
+ // too high, next one
+ continue;
+ }
+
+ if (other.getY() - 5 > yMax) {
+ // too low, no more collisions possible
+ break;
+ }
+
+ int xPos = other.getX() + other.getWidth() + 5;
+ xCor = Math.max(xCor, xPos);
+ }
+ w.setX(xCor);
+ }
+
+ public static Screen getCorrect(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ if (TabHud.genericTgl.isPressed()) {
+ return new GenericInfoScreen(w, h, ple, footer);
+ // } else if (TabHud.mapTgl.isPressed()) {
+ // return Screen.correctMapScrn(w, h, ple, footer);
+ } else if (TabHud.playerTgl.isPressed()) {
+ return Screen.correctPlayerScrn(w, h, ple, footer);
+ } else {
+ return Screen.correctMainScrn(w, h, ple, footer);
+ }
+ }
+
+ private static ScreenType getScreenType(List<PlayerListEntry> ple) {
+ String cat2Name = StrMan.strAt(ple, 40);
+
+ if (cat2Name.contains("Dungeon Stats")) {
+ return ScreenType.DUNGEON;
+ }
+
+ String areaDesciptor = StrMan.strAt(ple, 41).substring(6);
+ switch (areaDesciptor) {
+ case "Private Island":
+ if (ple.get(44).getDisplayName().getString().endsWith("Guest")) {
+ return ScreenType.GUEST_ISLAND;
+ } else {
+ return ScreenType.HOME_ISLAND;
+ }
+ case "Crimson Isle":
+ return ScreenType.CRIMSON_ISLE;
+ case "Dungeon Hub":
+ return ScreenType.DUNGEON_HUB;
+ case "The Farming Islands":
+ return ScreenType.FARMING_ISLAND;
+ case "The Park":
+ return ScreenType.PARK;
+ case "Dwarven Mines":
+ return ScreenType.DWARVEN_MINES;
+ case "Crystal Hollows":
+ return ScreenType.CRYSTAL_HOLLOWS;
+ case "The End":
+ return ScreenType.END;
+ case "Gold Mine":
+ return ScreenType.GOLD_MINE;
+ case "Deep Caverns":
+ return ScreenType.DEEP_CAVERNS;
+ case "Hub":
+ return ScreenType.HUB;
+ case "Spider's Den":
+ return ScreenType.SPIDER_DEN;
+ case "Jerry's Workshop":
+ return ScreenType.JERRY;
+ case "Garden":
+ return ScreenType.GARDEN;
+ default:
+ return ScreenType.NONE;
+ }
+ }
+
+ // private static Screen correctMapScrn(int w, int h, List<PlayerListEntry> list, Text footer) {
+ // // return switch (getScreenType(list)) {
+ // // case CRYSTAL_HOLLOWS -> null;
+ // // case DUNGEON -> null;
+ // // default -> new EmptyScreen(w, h, list, footer);
+ // // };
+ // return new EmptyScreen(w, h, list, footer);
+ // }
+
+ private static Screen correctPlayerScrn(int w, int h, List<PlayerListEntry> list, Text footer) {
+ return switch (getScreenType(list)) {
+ case GUEST_ISLAND -> new GuestPlayerScreen(w, h, list, footer); // ok
+ case HOME_ISLAND -> new HomePlayerScreen(w, h, list, footer); // ok
+ case DUNGEON -> new DungeonPlayerScreen(w, h, list, footer);
+ default -> new PlayerListScreen(w, h, list, footer); // ok
+ };
+ }
+
+ private static Screen correctMainScrn(int w, int h, List<PlayerListEntry> list, Text footer) {
+ return switch (getScreenType(list)) {
+ case PARK -> new ParkServerScreen(w, h, list, footer); // ok
+ case HUB -> new HubServerScreen(w, h, list, footer); // ok
+ case HOME_ISLAND -> new HomeServerScreen(w, h, list, footer); // ok
+ case GUEST_ISLAND -> new GuestServerScreen(w, h, list, footer); // ok
+ case CRYSTAL_HOLLOWS, DWARVEN_MINES -> new MineServerScreen(w, h, list, footer);
+ case FARMING_ISLAND -> new FarmingServerScreen(w, h, list, footer); // ok
+ case DUNGEON_HUB -> new DungeonHubScreen(w, h, list, footer); // ok
+ case DUNGEON -> new DungeonScreen(w, h, list, footer);
+ case CRIMSON_ISLE -> null; // TODO
+ case GARDEN -> new GardenScreen(w, h, list, footer);
+ default -> new GenericServerScreen(w, h, list, footer); // ok
+ };
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java
new file mode 100644
index 00000000..a9f8e22f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java
@@ -0,0 +1,50 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.util;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+
+public class Ico {
+ public static final ItemStack MAP = new ItemStack(Items.FILLED_MAP);
+ public static final ItemStack NTAG = new ItemStack(Items.NAME_TAG);
+ public static final ItemStack EMERALD = new ItemStack(Items.EMERALD);
+ public static final ItemStack CLOCK = new ItemStack(Items.CLOCK);
+ public static final ItemStack DIASWORD = new ItemStack(Items.DIAMOND_SWORD);
+ public static final ItemStack DBUSH = new ItemStack(Items.DEAD_BUSH);
+ public static final ItemStack VILLAGER = new ItemStack(Items.VILLAGER_SPAWN_EGG);
+ public static final ItemStack MOREGOLD = new ItemStack(Items.GOLDEN_APPLE);
+ public static final ItemStack COMPASS = new ItemStack(Items.COMPASS);
+ public static final ItemStack SUGAR = new ItemStack(Items.SUGAR);
+ public static final ItemStack HOE = new ItemStack(Items.IRON_HOE);
+ public static final ItemStack GOLD = new ItemStack(Items.GOLD_INGOT);
+ public static final ItemStack BONE = new ItemStack(Items.BONE);
+ public static final ItemStack SIGN = new ItemStack(Items.OAK_SIGN);
+ public static final ItemStack FISH_ROD = new ItemStack(Items.FISHING_ROD);
+ public static final ItemStack SWORD = new ItemStack(Items.IRON_SWORD);
+ public static final ItemStack LANTERN = new ItemStack(Items.LANTERN);
+ public static final ItemStack COOKIE = new ItemStack(Items.COOKIE);
+ public static final ItemStack POTION = new ItemStack(Items.POTION);
+ public static final ItemStack BARRIER = new ItemStack(Items.BARRIER);
+ public static final ItemStack PLAYER = new ItemStack(Items.PLAYER_HEAD);
+ public static final ItemStack WATER = new ItemStack(Items.WATER_BUCKET);
+ public static final ItemStack LEATHER = new ItemStack(Items.LEATHER);
+ public static final ItemStack MITHRIL = new ItemStack(Items.PRISMARINE_CRYSTALS);
+ public static final ItemStack REDSTONE = new ItemStack(Items.REDSTONE);
+ public static final ItemStack FIRE = new ItemStack(Items.CAMPFIRE);
+ public static final ItemStack STRING = new ItemStack(Items.STRING);
+ public static final ItemStack WITHER = new ItemStack(Items.WITHER_SKELETON_SKULL);
+ public static final ItemStack FLESH = new ItemStack(Items.ROTTEN_FLESH);
+ public static final ItemStack DRAGON = new ItemStack(Items.DRAGON_HEAD);
+ public static final ItemStack DIAMOND = new ItemStack(Items.DIAMOND);
+ public static final ItemStack ICE = new ItemStack(Items.ICE);
+ public static final ItemStack CHEST = new ItemStack(Items.CHEST);
+ public static final ItemStack COMMAND = new ItemStack(Items.COMMAND_BLOCK);
+ public static final ItemStack SKULL = new ItemStack(Items.SKELETON_SKULL);
+ public static final ItemStack BOOK = new ItemStack(Items.WRITABLE_BOOK);
+ public static final ItemStack FURNACE = new ItemStack(Items.FURNACE);
+ public static final ItemStack CHESTPLATE = new ItemStack(Items.IRON_CHESTPLATE);
+ public static final ItemStack B_ROD = new ItemStack(Items.BLAZE_ROD);
+ public static final ItemStack BOW = new ItemStack(Items.BOW);
+ public static final ItemStack COPPER = new ItemStack(Items.COPPER_INGOT);
+ public static final ItemStack COMPOSTER = new ItemStack(Items.COMPOSTER);
+ public static final ItemStack SAPLING = new ItemStack(Items.OAK_SAPLING);
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/StrMan.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/StrMan.java
new file mode 100644
index 00000000..8cd77ac2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/StrMan.java
@@ -0,0 +1,64 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.util;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class StrMan {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StrMan.class.getName());
+ private static final Text ERROR_TXT = Text.literal("[ERROR]").formatted(Formatting.RED, Formatting.BOLD);
+
+ public static Text stdEntry(List<PlayerListEntry> ple, int idx, String entryName, Formatting contentFmt) {
+ Text txt = ple.get(idx).getDisplayName();
+ if (txt == null) {
+ return ERROR_TXT;
+ }
+ String src = txt.getString();
+ src = src.substring(src.indexOf(':') + 1);
+ return StrMan.stdEntry(src, entryName, contentFmt);
+ }
+
+ public static Text stdEntry(String entryContent, String entryName, Formatting contentFmt) {
+ return Text.literal(entryName).append(Text.literal(entryContent).formatted(contentFmt));
+ }
+
+ public static Text plainEntry(List<PlayerListEntry> ple, int idx) {
+ Text txt = ple.get(idx).getDisplayName();
+ if (txt == null) {
+ return ERROR_TXT;
+ }
+ return Text.of(txt.getString().trim());
+ }
+
+ public static Matcher regexAt(List<PlayerListEntry> ple, int idx, Pattern p) {
+ Text txt = ple.get(idx).getDisplayName();
+ if (txt == null) {
+ return null;
+ }
+ String str = txt.getString();
+ Matcher m = p.matcher(str);
+ if (!m.matches()) {
+ LOGGER.error("ERROR: Regex {} failed for input \"{}\"", p.pattern(), str);
+ return null;
+ } else {
+ return m;
+ }
+ }
+
+ public static String strAt(List<PlayerListEntry> ple, int idx) {
+ Text txt = ple.get(idx).getDisplayName();
+ if (txt == null) {
+ return null;
+ }
+ return txt.getString();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java
new file mode 100644
index 00000000..ef86b8f0
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java
@@ -0,0 +1,53 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.math.MathHelper;
+
+// this widget shows the status of the king's commissions.
+// works in both the dwarven mines and the CH
+
+public class CommsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ // match a comm
+ // group 1: comm name
+ // group 2: comm progress (without "%" for comms that show a percentage)
+ private static final Pattern COMM_PATTERN = Pattern.compile(" (.*): (.*)%?");
+
+ public CommsWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ for (int i = 50; i <= 51; i++) {
+ Matcher m = StrMan.regexAt(list, i, COMM_PATTERN);
+
+ String g2 = m.group(2);
+ ProgressComponent pc;
+ if (g2.equals("DONE")) {
+ pc = new ProgressComponent(Ico.BOOK, Text.of(m.group(1)), Text.of(g2), 100f, pcntToCol(100));
+ } else {
+ float pcnt = Float.parseFloat(g2.substring(0, g2.length() - 1));
+ pc = new ProgressComponent(Ico.BOOK, Text.of(m.group(1)), pcnt, pcntToCol(pcnt));
+ }
+ this.addComponent(pc);
+ }
+ this.pack();
+ }
+
+ private int pcntToCol(float pcnt) {
+ return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java
new file mode 100644
index 00000000..2537b90a
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the garden's composter
+
+public class ComposterWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Composter").formatted(Formatting.GREEN,
+ Formatting.BOLD);
+
+
+ public ComposterWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.GREEN.getColorValue());
+
+ Text matter = StrMan.stdEntry(list, 48, "Organic Matter:", Formatting.YELLOW);
+ IcoTextComponent mat = new IcoTextComponent(Ico.SAPLING, matter);
+ this.addComponent(mat);
+
+ Text fuel = StrMan.stdEntry(list, 49, "Fuel:", Formatting.BLUE);
+ IcoTextComponent f = new IcoTextComponent(Ico.FURNACE, fuel);
+ this.addComponent(f);
+
+ Text timeLeft = StrMan.stdEntry(list, 50, "Time Left:", Formatting.RED);
+ IcoTextComponent time = new IcoTextComponent(Ico.CLOCK, timeLeft);
+ this.addComponent(time);
+
+ Text compost = StrMan.stdEntry(list, 51, "Stored Compost:", Formatting.DARK_GREEN);
+ IcoTextComponent comp = new IcoTextComponent(Ico.COMPOSTER, compost);
+ this.addComponent(comp);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java
new file mode 100644
index 00000000..86d9dd7f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java
@@ -0,0 +1,33 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about active super cookies
+// or not, if you're unwilling to buy one
+// TODO: test with active cookie. I'm not buying one.
+
+public class CookieWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Cookie Info").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public CookieWidget(String footertext) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ String interesting = footertext.split("Cookie Buff")[1];
+ String[] lines = interesting.split("\n");
+ if (lines[1].startsWith("Not")) {
+ this.addComponent(new IcoTextComponent(Ico.COOKIE, Text.of("Not active")));
+ } else {
+ Text cookie = Text.literal("Time Left: ").append(lines[2]);
+ this.addComponent(new IcoTextComponent(Ico.COOKIE, cookie));
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java
new file mode 100644
index 00000000..928d9f76
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java
@@ -0,0 +1,53 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows various dungeon info
+// deaths, healing, dmg taken, milestones
+
+public class DungeonDeathWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Death").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // match the deaths entry
+ // group 1: amount of deaths
+ private static final Pattern DEATH_PATTERN = Pattern.compile("\\S*: \\((\\d+)\\).*");
+
+ public DungeonDeathWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ Matcher m = StrMan.regexAt(list, 25, DEATH_PATTERN);
+ Formatting f = (m.group(1).equals("0")) ? Formatting.GREEN : Formatting.RED;
+ Text d = Text.literal("Deaths: ").append(Text.literal(m.group(1)).formatted(f));
+ IcoTextComponent deaths = new IcoTextComponent(Ico.SKULL, d);
+ this.addComponent(deaths);
+
+ Text dealt = StrMan.stdEntry(list, 26, "Damage Dealt:", Formatting.RED);
+ IcoTextComponent de = new IcoTextComponent(Ico.SWORD, dealt);
+ this.addComponent(de);
+
+ Text heal = StrMan.stdEntry(list, 27, "Healing Done:", Formatting.RED);
+ IcoTextComponent he = new IcoTextComponent(Ico.POTION, heal);
+ this.addComponent(he);
+
+ Text mile = StrMan.stdEntry(list, 28, "Milestone:", Formatting.YELLOW);
+ IcoTextComponent mi = new IcoTextComponent(Ico.NTAG, mile);
+ this.addComponent(mi);
+
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java
new file mode 100644
index 00000000..9146fdbb
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java
@@ -0,0 +1,42 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about... something?
+// TODO: test this.
+
+public class DungeonDownedWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Downed").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public DungeonDownedWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ Formatting format = Formatting.RED;
+ if (StrMan.strAt(list, 21).endsWith("NONE")) {
+ format = Formatting.GRAY;
+ }
+ Text downed = StrMan.stdEntry(list, 21, "Downed:", format);
+ IcoTextComponent down = new IcoTextComponent(Ico.SKULL, downed);
+ this.addComponent(down);
+
+ Text time = StrMan.stdEntry(list, 22, "Time:", Formatting.GRAY);
+ IcoTextComponent t = new IcoTextComponent(Ico.CLOCK, time);
+ this.addComponent(t);
+
+ Text revive = StrMan.stdEntry(list, 23, "Revive:", Formatting.GRAY);
+ IcoTextComponent rev = new IcoTextComponent(Ico.POTION, revive);
+ this.addComponent(rev);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java
new file mode 100644
index 00000000..ce7b13bd
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java
@@ -0,0 +1,83 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about a player in the current dungeon group
+// when inside a dungeon
+
+public class DungeonPlayerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Player").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // match a player entry
+ // group 1: name
+ // group 2: class (or literal "EMPTY" pre dungeon start)
+ // group 3: level (or nothing, if pre dungeon start)
+ // as a side effect, this regex keeps the iron man icon in the name
+ // not sure if that should be
+ private static final Pattern PLAYER_PATTERN = Pattern.compile("\\[\\d*\\] (.*) \\((\\S*) ?([XVI]*)\\)");
+
+ private static final HashMap<String, ItemStack> ICOS = new HashMap<>();
+ static {
+ ICOS.put("Tank", Ico.CHESTPLATE);
+ ICOS.put("Mage", Ico.B_ROD);
+ ICOS.put("Berserk", Ico.DIASWORD);
+ ICOS.put("Archer", Ico.BOW);
+ ICOS.put("Healer", Ico.POTION);
+ }
+
+ // title needs to be changeable here
+ public DungeonPlayerWidget(List<PlayerListEntry> list, int player) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ int start = 1 + (player - 1) * 4;
+
+ if (list.get(start).getDisplayName().getString().length() < 2) {
+ this.addComponent(
+ new IcoTextComponent(Ico.SIGN, Text.literal("PRESS A TO JOIN").formatted(Formatting.GRAY)));
+ } else {
+ Matcher m = StrMan.regexAt(list, start, PLAYER_PATTERN);
+
+ Text name = Text.literal("Name: ").append(Text.literal(m.group(1)).formatted(Formatting.YELLOW));
+ this.addComponent(new IcoTextComponent(Ico.PLAYER, name));
+
+ String cl = m.group(2);
+ Formatting clf = Formatting.GRAY;
+ ItemStack cli = Ico.BARRIER;
+ if (!cl.equals("EMPTY")) {
+ cli = ICOS.get(cl);
+ clf = Formatting.LIGHT_PURPLE;
+ cl += " " + m.group(3);
+ }
+
+ Text class_ = Text.literal("Class: ").append(Text.literal(cl).formatted(clf));
+ IcoTextComponent itclass = new IcoTextComponent(cli, class_);
+ this.addComponent(itclass);
+
+ Text ult = StrMan.stdEntry(list, start + 1, "Ult Cooldown:", Formatting.GOLD);
+ IcoTextComponent ul = new IcoTextComponent(Ico.CLOCK, ult);
+ this.addComponent(ul);
+
+ Text revive = StrMan.stdEntry(list, start + 2, "Revives:", Formatting.DARK_PURPLE);
+ IcoTextComponent re = new IcoTextComponent(Ico.POTION, revive);
+ this.addComponent(re);
+
+ }
+ this.pack();
+
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java
new file mode 100644
index 00000000..906d449b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java
@@ -0,0 +1,51 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about all puzzeles in the dungeon (name and status)
+// TODO: if puzzle was failed, show player name
+
+public class DungeonPuzzleWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Puzzles").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // match a puzzle entry
+ // group 1: name
+ // group 2: status
+ // yes, that " ?" is needed.
+ // the teleport maze has a trailing whitespace that messes with the regex
+ private static final Pattern PUZZLE_PATTERN = Pattern.compile(" (.*): \\[(.*)\\] ?");
+
+ public DungeonPuzzleWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ int pos = 48;
+
+ while (pos < 60) {
+ Matcher m = StrMan.regexAt(list, pos, PUZZLE_PATTERN);
+ if (m == null) {
+ break;
+ }
+ Text t = Text.literal(m.group(1) + ": ").append(Text.literal("[").formatted(Formatting.GRAY))
+ .append(m.group(2)).append(Text.literal("]").formatted(Formatting.GRAY));
+ IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, t);
+ this.addComponent(itc);
+ pos++;
+ }
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
new file mode 100644
index 00000000..fed6b0b2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
@@ -0,0 +1,35 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about secrets of the dungeon
+
+public class DungeonSecretWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public DungeonSecretWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ Text secrets = StrMan.stdEntry(list, 31, "Secrets:", Formatting.YELLOW);
+ IcoTextComponent sec = new IcoTextComponent(Ico.CHEST, secrets);
+ this.addComponent(sec);
+
+ Text crypts = StrMan.stdEntry(list, 32, "Crypts:", Formatting.YELLOW);
+ IcoTextComponent cry = new IcoTextComponent(Ico.SKULL, crypts);
+ this.addComponent(cry);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java
new file mode 100644
index 00000000..89d3b1ca
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java
@@ -0,0 +1,56 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows broad info about the current dungeon
+// opened/completed rooms, % of secrets found and time taken
+
+public class DungeonServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Dungeon Info").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // match the secrets text
+ // group 1: % of secrets found (without "%")
+ private static final Pattern SECRET_PATTERN = Pattern.compile(" Secrets Found: (.*)%");
+
+ public DungeonServerWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ Text name = StrMan.stdEntry(list, 41, "Name:", Formatting.AQUA);
+ IcoTextComponent na = new IcoTextComponent(Ico.NTAG, name);
+ this.addComponent(na);
+
+ Text open = StrMan.stdEntry(list, 42, "Rooms Visited:", Formatting.DARK_PURPLE);
+ IcoTextComponent op = new IcoTextComponent(Ico.SIGN, open);
+ this.addComponent(op);
+
+ Text compl = StrMan.stdEntry(list, 43, "Rooms Completed:", Formatting.LIGHT_PURPLE);
+ IcoTextComponent co = new IcoTextComponent(Ico.SIGN, compl);
+ this.addComponent(co);
+
+ Matcher m = StrMan.regexAt(list, 44, SECRET_PATTERN);
+ Text secrets = Text.of("Secrets found:");
+ ProgressComponent scp = new ProgressComponent(Ico.CHEST, secrets, Float.parseFloat(m.group(1)),
+ Formatting.DARK_PURPLE.getColorValue());
+ this.addComponent(scp);
+
+ Text time = StrMan.stdEntry(list, 45, "Time:", Formatting.GOLD);
+ IcoTextComponent ti = new IcoTextComponent(Ico.CLOCK, time);
+ this.addComponent(ti);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java
new file mode 100644
index 00000000..54b49465
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widgte shows, how many active effects you have
+// it also shows one of those in detail
+
+public class EffectWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Effect Info").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public EffectWidget(String footertext) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ String interesting = footertext.split("Active Effects")[1];
+ String[] lines = interesting.split("\n");
+
+ if (lines[1].startsWith("No")) {
+ Text txt = Text.literal("No effects active").formatted(Formatting.GRAY);
+ this.addComponent(new IcoTextComponent(Ico.POTION, txt));
+ } else {
+ String number = lines[1].substring("You have ".length());
+ number = number.substring(0, number.indexOf(' '));
+ Text active = Text.literal("Active Effects: ").append(Text.literal(number).formatted(Formatting.YELLOW));
+
+ IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, active,
+ Text.literal(lines[3]).formatted(Formatting.AQUA));
+ this.addComponent(iftc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java
new file mode 100644
index 00000000..e75b7152
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java
@@ -0,0 +1,97 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// XXX crashes when in garden, as this shows jacob's farming info there...
+// "starts in", then the three farmable things
+// this widget shows the status or results of the current election
+
+public class ElectionWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Election Info").formatted(Formatting.YELLOW,
+ Formatting.BOLD);
+
+ private static final HashMap<String, ItemStack> MAYOR_DATA = new HashMap<>();
+
+ private static final Text EL_OVER = Text.literal("Election ")
+ .append(Text.literal("over!").formatted(Formatting.RED));
+
+ // pattern matching a candidate while people are voting
+ // group 1: name
+ // group 2: % of votes
+ private static final Pattern VOTE_PATTERN = Pattern.compile(" (\\S*): \\|+ \\((\\d*)%\\)");
+
+ static {
+ MAYOR_DATA.put("Aatrox", Ico.DIASWORD);
+ MAYOR_DATA.put("Diana", Ico.BONE);
+ MAYOR_DATA.put("Diaz", Ico.GOLD);
+ MAYOR_DATA.put("Finnegan", Ico.HOE);
+ MAYOR_DATA.put("Foxy", Ico.SUGAR);
+ MAYOR_DATA.put("Paul", Ico.COMPASS);
+ MAYOR_DATA.put("Scorpius", Ico.MOREGOLD);
+ MAYOR_DATA.put("Jerry", Ico.VILLAGER);
+ MAYOR_DATA.put("Derpy", Ico.DBUSH);
+ MAYOR_DATA.put("Marina", Ico.FISH_ROD);
+ }
+
+ private static final Formatting[] COLS = { Formatting.GOLD, Formatting.RED, Formatting.LIGHT_PURPLE };
+
+ public ElectionWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ if (StrMan.strAt(list, 76).contains("Over!")) {
+ // election is over
+
+ IcoTextComponent over = new IcoTextComponent(Ico.BARRIER, EL_OVER);
+ this.addComponent(over);
+
+ String winnername = StrMan.strAt(list, 77).split(": ")[1];
+ Text winnertext = Text.literal("Winner: ")
+ .append(Text.literal(winnername).formatted(Formatting.GREEN));
+ IcoTextComponent winner = new IcoTextComponent(MAYOR_DATA.get(winnername), winnertext);
+ this.addComponent(winner);
+
+ Text participants = StrMan.stdEntry(list, 78, "Participants:", Formatting.AQUA);
+ IcoTextComponent part = new IcoTextComponent(Ico.PLAYER, participants);
+ this.addComponent(part);
+
+ Text year = StrMan.stdEntry(list, 79, "Year:", Formatting.LIGHT_PURPLE);
+ IcoTextComponent y = new IcoTextComponent(Ico.SIGN, year);
+ this.addComponent(y);
+
+ } else {
+ // election is going on
+ Text time = StrMan.stdEntry(list, 76, "End in:", Formatting.GOLD);
+ IcoTextComponent t = new IcoTextComponent(Ico.CLOCK, time);
+ this.addComponent(t);
+
+ for (int i = 77; i <= 79; i++) {
+ Matcher m = StrMan.regexAt(list, i, VOTE_PATTERN);
+ String g1 = m.group(1);
+ String g2 = m.group(2);
+ float pcnt = Float.parseFloat(g2);
+ Text candidate = Text.literal(g1).formatted(COLS[i - 77]);
+ ProgressComponent pc = new ProgressComponent(MAYOR_DATA.get(g1), candidate, pcnt,
+ COLS[i - 77].getColorValue());
+ this.addComponent(pc);
+
+ }
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java
new file mode 100644
index 00000000..584a593e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java
@@ -0,0 +1,27 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// empty widget for when nothing can be shown
+
+public class EmptyWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Empty").formatted(Formatting.RED,
+ Formatting.BOLD);
+
+ public EmptyWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.RED.getColorValue());
+
+ Text info = Text.of("No info for this area!");
+ PlainTextComponent inf = new PlainTextComponent(info);
+ this.addComponent(inf);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java
new file mode 100644
index 00000000..903c3a3d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java
@@ -0,0 +1,48 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows your dungeon essences
+
+public class EssenceWidget extends Widget {
+
+ private Text undead, wither, diamond, gold, dragon, spider, ice, crimson;
+
+ private static final MutableText TITLE = Text.literal("Essences").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public EssenceWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ wither = StrMan.stdEntry(list, 46, "Wither:", Formatting.DARK_PURPLE);
+ spider = StrMan.stdEntry(list, 47, "Spider:", Formatting.DARK_PURPLE);
+ undead = StrMan.stdEntry(list, 48, "Undead:", Formatting.DARK_PURPLE);
+ dragon = StrMan.stdEntry(list, 49, "Dragon:", Formatting.DARK_PURPLE);
+ gold = StrMan.stdEntry(list, 50, "Gold:", Formatting.DARK_PURPLE);
+ diamond = StrMan.stdEntry(list, 51, "Diamond:", Formatting.DARK_PURPLE);
+ ice = StrMan.stdEntry(list, 52, "Ice:", Formatting.DARK_PURPLE);
+ crimson = StrMan.stdEntry(list, 53, "Crimson:", Formatting.DARK_PURPLE);
+
+ TableComponent tc = new TableComponent(2, 4, Formatting.DARK_AQUA.getColorValue());
+
+ tc.addToCell(0, 0, new IcoTextComponent(Ico.WITHER, wither));
+ tc.addToCell(0, 1, new IcoTextComponent(Ico.STRING, spider));
+ tc.addToCell(0, 2, new IcoTextComponent(Ico.FLESH, undead));
+ tc.addToCell(0, 3, new IcoTextComponent(Ico.DRAGON, dragon));
+ tc.addToCell(1, 0, new IcoTextComponent(Ico.GOLD, gold));
+ tc.addToCell(1, 1, new IcoTextComponent(Ico.DIAMOND, diamond));
+ tc.addToCell(1, 2, new IcoTextComponent(Ico.ICE, ice));
+ tc.addToCell(1, 3, new IcoTextComponent(Ico.REDSTONE, crimson));
+ this.addComponent(tc);
+ this.pack();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java
new file mode 100644
index 00000000..051ce11c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java
@@ -0,0 +1,33 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about ongoing events (e.g. jacob's farming)
+
+public class EventWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Event Info").formatted(Formatting.YELLOW, Formatting.BOLD);
+
+ public EventWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ Text eventName = StrMan.stdEntry(list, 73, "Name:", Formatting.YELLOW);
+ IcoTextComponent event = new IcoTextComponent(Ico.NTAG, eventName);
+ this.addComponent(event);
+
+ // this could look better
+ Text time = StrMan.plainEntry(list, 74);
+ IcoTextComponent t = new IcoTextComponent(Ico.CLOCK, time);
+ this.addComponent(t);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java
new file mode 100644
index 00000000..7531b071
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java
@@ -0,0 +1,56 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// TODO: untested with active fire sales
+// this widget shows info about fire sales when in the hub
+// or not, if there isn't one going on
+
+public class FireSaleWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Fire Sale").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ // matches a fire sale item
+ // group 1: item name
+ // group 2: # items bought
+ // group 1: # items available in total (1 digit + "k")
+ private static final Pattern FIRE_PATTERN = Pattern.compile(" (.*): (\\d*)/(\\d)k");
+
+ public FireSaleWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ boolean found = false;
+
+ for (int i = 46;; i++) {
+ Matcher m = StrMan.regexAt(list, i, FIRE_PATTERN);
+ if (m == null ||!m.matches()) {
+ break;
+ }
+ found = true;
+ Text a = Text.literal(m.group(1));
+ Text b = Text.literal(m.group(2) + "/" + m.group(3) + "000");
+ float pcnt = (1 - (Float.parseFloat(m.group(2)) / (Float.parseFloat(m.group(3)) * 1000)))/100f;
+ ProgressComponent pc = new ProgressComponent(Ico.GOLD, a, b, pcnt, Formatting.LIGHT_PURPLE.getColorValue());
+ this.addComponent(pc);
+ }
+ if (!found) {
+ this.addComponent(new PlainTextComponent(Text.literal("No Fire Sale!").formatted(Formatting.GRAY)));
+ }
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
new file mode 100644
index 00000000..553a001f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
@@ -0,0 +1,60 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows what you're forging right now
+// for locked slots, the unlock requirement is shown
+
+public class ForgeWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Forge Status").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public ForgeWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ for (int i = 0; i < 5; i++) {
+ String fstr = StrMan.strAt(list, i + 54).substring(4);
+ Component c;
+ Text l1, l2;
+
+ switch (fstr) {
+ case "LOCKED":
+ l1 = Text.literal("Locked").formatted(Formatting.RED);
+ l2 = switch (i + 1) {
+ case 3 -> Text.literal("Req. HotM 3").formatted(Formatting.GRAY);
+ case 4 -> Text.literal("Req. HotM 4").formatted(Formatting.GRAY);
+ case 5 -> Text.literal("Req. PotM 2").formatted(Formatting.GRAY);
+ default ->
+ Text.literal("This message should not appear").formatted(Formatting.RED, Formatting.BOLD);
+ };
+ c = new IcoFatTextComponent(Ico.BARRIER, l1, l2);
+ break;
+ case "EMPTY":
+ l1 = Text.literal("Empty").formatted(Formatting.GRAY);
+ c = new IcoTextComponent(Ico.FURNACE, l1);
+ break;
+ default:
+ String[] parts = fstr.split(": ");
+ l1 = Text.of(parts[0]);
+ l2 = Text.literal(parts[1]).formatted(Formatting.YELLOW);
+ c = new IcoFatTextComponent(Ico.FIRE, l1, l2);
+ break;
+ }
+ this.addComponent(c);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java
new file mode 100644
index 00000000..c6c334d5
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java
@@ -0,0 +1,59 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the garden server
+
+public class GardenServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ private static final Pattern VISITOR_PATTERN = Pattern.compile(" Next Visitor: (.*)$");
+
+ public GardenServerWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ Text areaName = StrMan.stdEntry(list, 41, "Area:", Formatting.DARK_AQUA);
+ IcoTextComponent area = new IcoTextComponent(Ico.MAP, areaName);
+ this.addComponent(area);
+
+ Text serverName = StrMan.stdEntry(list, 42, "Server ID:", Formatting.GRAY);
+ IcoTextComponent server = new IcoTextComponent(Ico.NTAG, serverName);
+ this.addComponent(server);
+
+ Text amtGems = StrMan.stdEntry(list, 43, "Gems:", Formatting.GREEN);
+ IcoTextComponent gems = new IcoTextComponent(Ico.EMERALD, amtGems);
+ this.addComponent(gems);
+
+ Text copper = StrMan.stdEntry(list, 44, "Copper:", Formatting.GOLD);
+ IcoTextComponent co = new IcoTextComponent(Ico.COPPER, copper);
+ this.addComponent(co);
+
+ Matcher v = StrMan.regexAt(list, 45, VISITOR_PATTERN);
+ String vString = v.group(1);
+ Formatting col;
+ if (vString.equals("Not Unlocked!")) {
+ col = Formatting.RED;
+ } else {
+ col = Formatting.GREEN;
+ }
+ MutableText visitor = Text.literal("Next Visitor: ")
+ .append(Text.literal(vString).formatted(col));
+ IcoTextComponent vis = new IcoTextComponent(Ico.PLAYER, visitor);
+ this.addComponent(vis);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java
new file mode 100644
index 00000000..67b61667
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java
@@ -0,0 +1,41 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the private island you're visiting
+
+public class GuestServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public GuestServerWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ Text areaName = StrMan.stdEntry(list, 41, "Area:", Formatting.DARK_AQUA);
+ IcoTextComponent area = new IcoTextComponent(Ico.MAP, areaName);
+ this.addComponent(area);
+
+ Text serverName = StrMan.stdEntry(list, 42, "Server ID:", Formatting.GRAY);
+ IcoTextComponent server = new IcoTextComponent(Ico.NTAG, serverName);
+ this.addComponent(server);
+
+ Text owner = StrMan.stdEntry(list, 43, "Owner:", Formatting.GREEN);
+ IcoTextComponent own = new IcoTextComponent(Ico.SIGN, owner);
+ this.addComponent(own);
+
+ Text status = StrMan.stdEntry(list, 44, "Status:", Formatting.BLUE);
+ IcoTextComponent stat = new IcoTextComponent(Ico.SIGN, status);
+ this.addComponent(stat);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java
new file mode 100644
index 00000000..5814c15c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of all people visiting
+// the same private island as you
+
+public class IslandGuestsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Guests").formatted(Formatting.AQUA,
+ Formatting.BOLD);
+
+ // matches a player entry, removing their level and the hand icon
+ // group 1: player name
+ private static final Pattern GUEST_PATTERN = Pattern.compile("\\[\\d*\\] (.*) \\[.\\]");
+
+ public IslandGuestsWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.AQUA.getColorValue());
+ for (int i = 21; i < 40; i++) {
+ String str = list.get(i).getDisplayName().getString();
+ if (str.length() == 0) {
+ if (i == 21) {
+ this.addComponent(new PlainTextComponent(Text.literal("No Visitors!").formatted(Formatting.GRAY)));
+ }
+ break;
+ }
+ Matcher m = StrMan.regexAt(list, i, GUEST_PATTERN);
+ PlainTextComponent ptc = new PlainTextComponent(Text.of(m.group(1)));
+ this.addComponent(ptc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
new file mode 100644
index 00000000..a0056a06
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
@@ -0,0 +1,47 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of the owners of a private island
+// when you're visiting someone else
+
+public class IslandOwnersWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // matches an owner
+ // group 1: player name (cut off by hypixel for some reason)
+ // group 2: last seem
+ // TODO: test with owner online
+ private static final Pattern OWNER_PATTERN = Pattern.compile("(.*) \\((.*)\\)");
+
+ public IslandOwnersWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ for (int i = 1; i < 20; i++) {
+ if (list.get(i).getDisplayName().getString().length() == 0) {
+ break;
+ }
+
+ Matcher m = StrMan.regexAt(list, i, OWNER_PATTERN);
+ Text entry = Text.literal(m.group(1))
+ .append(
+ Text.literal(" (" + m.group(2) + ")")
+ .formatted(Formatting.GRAY));
+ PlainTextComponent ptc = new PlainTextComponent(entry);
+ this.addComponent(ptc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java
new file mode 100644
index 00000000..96ec8ba5
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of the owners of your home island
+
+public class IslandSelfWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // matches an owner
+ // group 1: player name
+ private static final Pattern OWNER_PATTERN = Pattern.compile("\\[\\d*\\] (.*)");
+
+ public IslandSelfWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ for (int i = 1; i < 20; i++) {
+ String str = list.get(i).getDisplayName().getString();
+ if (str.length() == 0) {
+ break;
+ }
+ Matcher m = StrMan.regexAt(list, i, OWNER_PATTERN);
+ PlainTextComponent ptc = new PlainTextComponent(Text.of(m.group(1)));
+ this.addComponent(ptc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java
new file mode 100644
index 00000000..1bbe0ed7
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java
@@ -0,0 +1,47 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about your home island
+
+public class IslandServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public IslandServerWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ Text areaName = StrMan.stdEntry(list, 41, "Area:", Formatting.DARK_AQUA);
+ IcoTextComponent area = new IcoTextComponent(Ico.MAP, areaName);
+ this.addComponent(area);
+
+ Text serverName = StrMan.stdEntry(list, 42, "Server ID:", Formatting.GRAY);
+ IcoTextComponent server = new IcoTextComponent(Ico.NTAG, serverName);
+ this.addComponent(server);
+
+ Text crystals = StrMan.stdEntry(list, 43, "Crystals:", Formatting.DARK_PURPLE);
+ IcoTextComponent crys = new IcoTextComponent(Ico.EMERALD, crystals);
+ this.addComponent(crys);
+
+ Text stash = StrMan.stdEntry(list, 44, "Stash:", Formatting.GREEN);
+ IcoTextComponent st = new IcoTextComponent(Ico.CHEST, stash);
+ this.addComponent(st);
+
+ Text minions = StrMan.stdEntry(list, 45, "Minions:", Formatting.BLUE);
+ IcoTextComponent min = new IcoTextComponent(Ico.COMMAND, minions);
+ this.addComponent(min);
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java
new file mode 100644
index 00000000..2ba94b48
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java
@@ -0,0 +1,135 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about minions placed on the home island
+
+public class MinionWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Minions").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ private static final HashMap<String, ItemStack> MIN_ICOS = new HashMap<>();
+
+ // hmm...
+ static {
+ MIN_ICOS.put("Blaze", new ItemStack(Items.BLAZE_ROD));
+ MIN_ICOS.put("Cave Spider", new ItemStack(Items.SPIDER_EYE));
+ MIN_ICOS.put("Creeper", new ItemStack(Items.GUNPOWDER));
+ MIN_ICOS.put("Enderman", new ItemStack(Items.ENDER_PEARL));
+ MIN_ICOS.put("Ghast", new ItemStack(Items.GHAST_TEAR));
+ MIN_ICOS.put("Magma Cube", new ItemStack(Items.MAGMA_CREAM));
+ MIN_ICOS.put("Skeleton", new ItemStack(Items.BONE));
+ MIN_ICOS.put("Slime", new ItemStack(Items.SLIME_BALL));
+ MIN_ICOS.put("Spider", new ItemStack(Items.STRING));
+ MIN_ICOS.put("Zombie", new ItemStack(Items.ROTTEN_FLESH));
+ MIN_ICOS.put("Cactus", new ItemStack(Items.CACTUS));
+ MIN_ICOS.put("Carrot", new ItemStack(Items.CARROT));
+ MIN_ICOS.put("Chicken", new ItemStack(Items.CHICKEN));
+ MIN_ICOS.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS));
+ MIN_ICOS.put("Cow", new ItemStack(Items.BEEF));
+ MIN_ICOS.put("Melon", new ItemStack(Items.MELON_SLICE));
+ MIN_ICOS.put("Mushroom", new ItemStack(Items.RED_MUSHROOM));
+ MIN_ICOS.put("Nether Wart", new ItemStack(Items.NETHER_WART));
+ MIN_ICOS.put("Pig", new ItemStack(Items.PORKCHOP));
+ MIN_ICOS.put("Potato", new ItemStack(Items.POTATO));
+ MIN_ICOS.put("Pumpkin", new ItemStack(Items.PUMPKIN));
+ MIN_ICOS.put("Rabbit", new ItemStack(Items.RABBIT));
+ MIN_ICOS.put("Sheep", new ItemStack(Items.WHITE_WOOL));
+ MIN_ICOS.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE));
+ MIN_ICOS.put("Wheat", new ItemStack(Items.WHEAT));
+ MIN_ICOS.put("Clay", new ItemStack(Items.CLAY));
+ MIN_ICOS.put("Fishing", new ItemStack(Items.FISHING_ROD));
+ MIN_ICOS.put("Coal", new ItemStack(Items.COAL));
+ MIN_ICOS.put("Cobblestone", new ItemStack(Items.COBBLESTONE));
+ MIN_ICOS.put("Diamond", new ItemStack(Items.DIAMOND));
+ MIN_ICOS.put("Emerald", new ItemStack(Items.EMERALD));
+ MIN_ICOS.put("End Stone", new ItemStack(Items.END_STONE));
+ MIN_ICOS.put("Glowstone", new ItemStack(Items.GLOWSTONE_DUST));
+ MIN_ICOS.put("Gold", new ItemStack(Items.GOLD_INGOT));
+ MIN_ICOS.put("Gravel", new ItemStack(Items.GRAVEL));
+ MIN_ICOS.put("Hard Stone", new ItemStack(Items.STONE));
+ MIN_ICOS.put("Ice", new ItemStack(Items.ICE));
+ MIN_ICOS.put("Iron", new ItemStack(Items.IRON_INGOT));
+ MIN_ICOS.put("Lapis", new ItemStack(Items.LAPIS_LAZULI));
+ MIN_ICOS.put("Mithril", new ItemStack(Items.PRISMARINE_CRYSTALS));
+ MIN_ICOS.put("Mycelium", new ItemStack(Items.MYCELIUM));
+ MIN_ICOS.put("Obsidian", new ItemStack(Items.OBSIDIAN));
+ MIN_ICOS.put("Quartz", new ItemStack(Items.QUARTZ));
+ MIN_ICOS.put("Red Sand", new ItemStack(Items.RED_SAND));
+ MIN_ICOS.put("Redstone", new ItemStack(Items.REDSTONE));
+ MIN_ICOS.put("Sand", new ItemStack(Items.SAND));
+ MIN_ICOS.put("Snow", new ItemStack(Items.SNOWBALL));
+ MIN_ICOS.put("Inferno", new ItemStack(Items.BLAZE_SPAWN_EGG));
+ MIN_ICOS.put("Revenant", new ItemStack(Items.ZOMBIE_SPAWN_EGG));
+ MIN_ICOS.put("Tarantula", new ItemStack(Items.SPIDER_SPAWN_EGG));
+ MIN_ICOS.put("Voidling", new ItemStack(Items.ENDERMAN_SPAWN_EGG));
+ MIN_ICOS.put("Acacia", new ItemStack(Items.ACACIA_LOG));
+ MIN_ICOS.put("Birch", new ItemStack(Items.BIRCH_LOG));
+ MIN_ICOS.put("Dark Oak", new ItemStack(Items.DARK_OAK_LOG));
+ MIN_ICOS.put("Flower", new ItemStack(Items.POPPY));
+ MIN_ICOS.put("Jungle", new ItemStack(Items.JUNGLE_LOG));
+ MIN_ICOS.put("Oak", new ItemStack(Items.OAK_LOG));
+ MIN_ICOS.put("Spruce", new ItemStack(Items.SPRUCE_LOG));
+ }
+
+ // matches a minion entry
+ // group 1: name
+ // group 2: level
+ // group 3: status
+ public static final Pattern MINION_PATTERN = Pattern.compile(" (.*) ([XVI]*) \\[(.*)\\]");
+
+ public MinionWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ for (int i = 48; i < 59; i++) {
+ Matcher m = StrMan.regexAt(list, i, MINION_PATTERN);
+ if (m != null) {
+
+ String min = m.group(1);
+ String lvl = m.group(2);
+ String stat = m.group(3);
+
+ MutableText mt = Text.literal(min + " " + lvl).append(Text.literal(": "));
+
+ Formatting format = Formatting.RED;
+ if (stat.equals("ACTIVE")) {
+ format = Formatting.GREEN;
+ } else if (stat.equals("SLOW")) {
+ format = Formatting.YELLOW;
+ }
+ // makes "BLOCKED" also red. in reality, it's some kind of crimson
+ mt.append(Text.literal(stat).formatted(format));
+
+ IcoTextComponent itc = new IcoTextComponent(MIN_ICOS.get(min), mt);
+ this.addComponent(itc);
+
+ } else {
+ break;
+ }
+ }
+
+ // if more minions are placed than the tab menu can display,
+ // a "And X more..." text is shown
+ // look for that and add it to the widget
+ if (list.get(59).getDisplayName() != null) {
+ String other = list.get(59).getDisplayName().getString();
+ this.addComponent(new PlainTextComponent(Text.of(other.trim())));
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java
new file mode 100644
index 00000000..a94b5948
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java
@@ -0,0 +1,42 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the park server
+
+public class ParkServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public ParkServerWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ Text areaName = StrMan.stdEntry(list, 41, "Area:", Formatting.DARK_AQUA);
+ IcoTextComponent area = new IcoTextComponent(Ico.MAP, areaName);
+ this.addComponent(area);
+
+ Text serverName = StrMan.stdEntry(list, 42, "Server ID:", Formatting.GRAY);
+ IcoTextComponent server = new IcoTextComponent(Ico.NTAG, serverName);
+ this.addComponent(server);
+
+ Text amtGems = StrMan.stdEntry(list, 43, "Gems:", Formatting.GREEN);
+ IcoTextComponent gems = new IcoTextComponent(Ico.EMERALD, amtGems);
+ this.addComponent(gems);
+
+ Text rain = StrMan.stdEntry(list, 44, "Rain:", Formatting.BLUE);
+ IcoTextComponent ra = new IcoTextComponent(Ico.WATER, rain);
+ this.addComponent(ra);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java
new file mode 100644
index 00000000..27c69567
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java
@@ -0,0 +1,61 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlayerComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of players with their skins
+// in most areas
+
+public class PlayerListWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Players").formatted(Formatting.GREEN,
+ Formatting.BOLD);
+
+ private ArrayList<PlayerListEntry> list = new ArrayList<>();
+
+ public PlayerListWidget(List<PlayerListEntry> l) {
+ super(TITLE, Formatting.GREEN.getColorValue());
+
+ // unintuitive int ceil division stolen from
+ // https://stackoverflow.com/questions/7139382/java-rounding-up-to-an-int-using-math-ceil#21830188
+ int tblW = ((l.size() - 80) - 1) / 20 + 1;
+
+ TableComponent tc = new TableComponent(tblW, (l.size() - 80 >= 20) ? 20 : l.size() - 80,
+ Formatting.GREEN.getColorValue());
+
+ for (int i = 80; i < l.size(); i++) {
+ list.add(l.get(i));
+ }
+
+ Collections.sort(list, new Comparator<PlayerListEntry>() {
+ @Override
+ public int compare(PlayerListEntry o1, PlayerListEntry o2) {
+ return o1.getProfile().getName().toLowerCase().compareTo(o2.getProfile().getName().toLowerCase());
+ }
+ });
+
+ int x = 0, y = 0;
+
+ for (PlayerListEntry ple : list) {
+ tc.addToCell(x, y, new PlayerComponent(ple));
+ y++;
+ if (y >= 20) {
+ y = 0;
+ x++;
+ }
+ }
+
+ this.addComponent(tc);
+ this.pack();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java
new file mode 100644
index 00000000..97485b4f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java
@@ -0,0 +1,35 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows how much mithril and gemstone powder you have
+
+public class PowderWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Powders").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public PowderWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ Text amtMith = StrMan.stdEntry(list, 46, "Mithril:", Formatting.AQUA);
+ IcoTextComponent mith = new IcoTextComponent(Ico.MITHRIL, amtMith);
+ this.addComponent(mith);
+
+ Text amtGem = StrMan.stdEntry(list, 47, "Gemstone:", Formatting.DARK_PURPLE);
+ IcoTextComponent gem = new IcoTextComponent(Ico.EMERALD, amtGem);
+ this.addComponent(gem);
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java
new file mode 100644
index 00000000..59342b9d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java
@@ -0,0 +1,42 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about your profile and bank
+
+public class ProfileWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.YELLOW, Formatting.BOLD);
+
+ public ProfileWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ Text profileType = StrMan.stdEntry(list, 61, "Type:", Formatting.GREEN);
+ IcoTextComponent profile = new IcoTextComponent(Ico.SIGN, profileType);
+ this.addComponent(profile);
+
+ Text petInfo = StrMan.stdEntry(list, 62, "Pet Sitter:", Formatting.AQUA);
+ IcoTextComponent pet = new IcoTextComponent(Ico.BONE, petInfo);
+ this.addComponent(pet);
+
+ Text bankAmt = StrMan.stdEntry(list, 63, "Balance:", Formatting.GOLD);
+ IcoTextComponent bank = new IcoTextComponent(Ico.EMERALD, bankAmt);
+ this.addComponent(bank);
+
+ Text interest = StrMan.stdEntry(list, 64, "Interest in:", Formatting.GOLD);
+ IcoTextComponent inter = new IcoTextComponent(Ico.CLOCK, interest);
+ this.addComponent(inter);
+ this.pack();
+ }
+
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java
new file mode 100644
index 00000000..ce7bc56f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about "generic" servers
+// a server is "generic", when only name, server ID and gems are shown
+// in the thrid column of the tab menu
+
+public class ServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public ServerWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ Text areaName = StrMan.stdEntry(list, 41, "Area:", Formatting.DARK_AQUA);
+ IcoTextComponent area = new IcoTextComponent(Ico.MAP, areaName);
+ this.addComponent(area);
+
+ Text serverName = StrMan.stdEntry(list, 42, "Server ID:", Formatting.GRAY);
+ IcoTextComponent server = new IcoTextComponent(Ico.NTAG, serverName);
+ this.addComponent(server);
+
+ Text amtGems = StrMan.stdEntry(list, 43, "Gems:", Formatting.GREEN);
+ IcoTextComponent gems = new IcoTextComponent(Ico.EMERALD, amtGems);
+ this.addComponent(gems);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java
new file mode 100644
index 00000000..85bd630b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java
@@ -0,0 +1,63 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about a skill and some stats
+
+public class SkillsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW,
+ Formatting.BOLD);
+
+ // match the skill entry
+ // group 1: skill name and level
+ // group 2: progress to next level (without "%")
+ private static final Pattern SKILL_PATTERN = Pattern.compile("\\S*: ([A-Za-z]* [0-9]*): (\\S*)%");
+
+ public SkillsWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ Matcher m = StrMan.regexAt(list, 66, SKILL_PATTERN);
+
+ float pcnt = Float.parseFloat(m.group(2));
+ String skill = m.group(1);
+
+ ProgressComponent pc = new ProgressComponent(Ico.LANTERN, Text.of(skill), pcnt, Formatting.GOLD.getColorValue());
+
+ this.addComponent(pc);
+
+ Text speed = StrMan.stdEntry(list, 67, "SPD", Formatting.WHITE);
+ IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed);
+ Text strength = StrMan.stdEntry(list, 68, "STR", Formatting.RED);
+ IcoTextComponent str = new IcoTextComponent(Ico.SWORD, strength);
+ Text critDmg = StrMan.stdEntry(list, 69, "CDG", Formatting.BLUE);
+ IcoTextComponent cdg = new IcoTextComponent(Ico.SWORD, critDmg);
+ Text critCh = StrMan.stdEntry(list, 70, "CCH", Formatting.BLUE);
+ IcoTextComponent cch = new IcoTextComponent(Ico.SWORD, critCh);
+ Text aSpeed = StrMan.stdEntry(list, 71, "ASP", Formatting.YELLOW);
+ IcoTextComponent asp = new IcoTextComponent(Ico.HOE, aSpeed);
+
+ TableComponent tc = new TableComponent(2, 3, Formatting.YELLOW.getColorValue());
+ tc.addToCell(0, 0, spd);
+ tc.addToCell(0, 1, str);
+ tc.addToCell(0, 2, asp);
+ tc.addToCell(1, 0, cdg);
+ tc.addToCell(1, 1, cch);
+ this.addComponent(tc);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java
new file mode 100644
index 00000000..981e4eef
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java
@@ -0,0 +1,29 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.StrMan;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows, how meny pelts you have (farming island only)
+
+public class TrapperWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Trapper").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public TrapperWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ Text amtPelts = StrMan.stdEntry(list, 46, "Pelts:", Formatting.AQUA);
+ IcoTextComponent pelts = new IcoTextComponent(Ico.LEATHER, amtPelts);
+ this.addComponent(pelts);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java
new file mode 100644
index 00000000..9f3be82a
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java
@@ -0,0 +1,38 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about current profile/account upgrades
+// or not, if there aren't any
+// TODO: not very pretty atm
+
+public class UpgradeWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Upgrade Info").formatted(Formatting.GOLD,
+ Formatting.BOLD);
+
+ public UpgradeWidget(String footertext) {
+ super(TITLE, Formatting.GOLD.getColorValue());
+ String[] interesting = footertext.split("Upgrades");
+ if (interesting.length < 2) {
+ this.addComponent(new PlainTextComponent(Text.of("Currently no upgrades...")));
+ this.pack();
+ return;
+ }
+ String[] lines = interesting[1].split("\n");
+ IcoTextComponent u1 = new IcoTextComponent(Ico.SIGN, Text.of(lines[1]));
+ this.addComponent(u1);
+ if (lines.length == 5) {
+ IcoTextComponent u2 = new IcoTextComponent(Ico.SIGN, Text.of(lines[2]));
+ this.addComponent(u2);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java
new file mode 100644
index 00000000..1fdad37a
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java
@@ -0,0 +1,183 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.ArrayList;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+
+public abstract class Widget {
+
+ private ArrayList<Component> components = new ArrayList<>();
+ private int w = 0, h = 0;
+ private int x = 0, y = 0;
+ private int color;
+ private Text title;
+
+ private static TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
+
+ static final int BORDER_SZE_N = txtRend.fontHeight + 4;
+ static final int BORDER_SZE_S = 4;
+ static final int BORDER_SZE_W = 4;
+ static final int BORDER_SZE_E = 4;
+ static final int COL_BG_BOX = 0xc00c0c0c;
+
+ public Widget(MutableText title, Integer colorValue) {
+ this.title = title;
+ this.color = 0xff000000 | colorValue;
+ }
+
+ public final void addComponent(Component c) {
+ components.add(c);
+ }
+
+ public final void pack() {
+ for (Component c : components) {
+ h += c.getHeight() + 4;
+ w = Math.max(w, c.getWidth());
+ }
+ h += BORDER_SZE_N + BORDER_SZE_S - 2;
+ w += BORDER_SZE_E + BORDER_SZE_W;
+
+ // min width is dependent on title
+ w = Math.max(w, BORDER_SZE_W + BORDER_SZE_E + Widget.txtRend.getWidth(title) + 4 + 4 + 1);
+ }
+
+ public final void setX(int x) {
+ this.x = x;
+ }
+
+ public final int getY() {
+ return this.y;
+ }
+
+ public final int getX() {
+ return this.x;
+ }
+
+ public final void setY(int y) {
+ this.y = y;
+ }
+
+ public final int getWidth() {
+ return this.w;
+ }
+
+ public final int getHeight() {
+ return this.h;
+ }
+
+ public final void render(MatrixStack ms) {
+
+ DrawableHelper.fill(ms, x + 1, y, x + w - 1, y + h, COL_BG_BOX);
+ DrawableHelper.fill(ms, x, y + 1, x + 1, y + h - 1, COL_BG_BOX);
+ DrawableHelper.fill(ms, x + w - 1, y + 1, x + w, y + h - 1, COL_BG_BOX);
+
+ int strHeightHalf = Widget.txtRend.fontHeight / 2;
+ int strAreaWidth = Widget.txtRend.getWidth(title) + 4;
+
+ txtRend.draw(ms, title, x + 8, y + 2, this.color);
+
+ DrawableHelper.fill(ms, x + 2, y + 1 + strHeightHalf, strAreaWidth, strHeightHalf, strAreaWidth);
+
+ this.drawHLine(ms, x + 2, y + 1 + strHeightHalf, 4);
+ this.drawHLine(ms, x + 2 + strAreaWidth + 4, y + 1 + strHeightHalf, w - 4 - 4 - strAreaWidth);
+ this.drawHLine(ms, x + 2, y + h - 2, w - 4);
+
+ this.drawVLine(ms, x + 1, y + 2 + strHeightHalf, h - 4 - strHeightHalf);
+ this.drawVLine(ms, x + w - 2, y + 2 + strHeightHalf, h - 4 - strHeightHalf);
+
+ int yOffs = y + BORDER_SZE_N;
+
+ for (Component c : components) {
+ c.render(ms, x + BORDER_SZE_W, yOffs);
+ yOffs += c.getHeight() + 4;
+ }
+ }
+
+ private void drawHLine(MatrixStack ms, int xpos, int ypos, int width) {
+ DrawableHelper.fill(ms, xpos, ypos, xpos + width, ypos + 1, this.color);
+ }
+
+ private void drawVLine(MatrixStack ms, int xpos, int ypos, int height) {
+ DrawableHelper.fill(ms, xpos, ypos, xpos + 1, ypos + height, this.color);
+ }
+
+ // static final int ICO_DIM = 16;
+ // static final int PAD_S = 2;
+ // static final int PAD_L = 4;
+ // static final int SKIN_ICO_DIM = 8;
+
+ // static final int TEXT_H = txtRend.fontHeight;
+ // static final int BAR_H = TEXT_H;
+
+ // static final int TITLED_BAR_H = TEXT_H + PAD_S + BAR_H + PAD_L;
+ // static final int ICO_LINE_H = ICO_DIM + PAD_L;
+
+ // public void drawRect(MatrixStack ms, int x, int y, int w, int h) {
+ // DrawableHelper.fill(ms, x + xpos + CONTENT_OFFS_X, y + ypos + CONTENT_OFFS_Y,
+ // x + xpos + CONTENT_OFFS_X + w,
+ // y + ypos + CONTENT_OFFS_Y + h, this.color);
+ // }
+
+ // void drawIcon(ItemStack ico, int x, int y) {
+ // itmRend.renderGuiItemIcon(ico, x + x + CONTENT_OFFS_X, y + y
+ // + CONTENT_OFFS_Y);
+ // }
+
+ // void drawText(MatrixStack ms, Text text, int x, int y, int color) {
+ // txtRend.draw(ms, text, x + x + CONTENT_OFFS_X, y + y +
+ // CONTENT_OFFS_Y, 0xff000000 | color);
+ // }
+
+ // void drawText(MatrixStack ms, Text text, int x, int y) {
+ // this.drawText(ms, text, x, y, 0xffffffff);
+ // }
+
+ // void drawIcoText(MatrixStack ms, ItemStack ico, Text text, int x, int y) {
+ // this.drawIcon(ico, x, y);
+ // this.drawText(ms, text, x + ICO_DIM + PAD_L, y + 5);
+ // }
+
+ // void fill(MatrixStack ms, int x1, int y1, int x2, int y2, int color) {
+ // DrawableHelper.fill(ms, x1 + x + CONTENT_OFFS_X, y1 + y +
+ // CONTENT_OFFS_Y,
+ // x2 + x + CONTENT_OFFS_X, y2 + y + CONTENT_OFFS_Y, 0xff000000
+ // | color);
+ // }
+
+ // void drawBar(MatrixStack ms, int x, int y, int width, float fillPcnt, int
+ // color) {
+ // this.fill(ms, x, y, x + width, y + 10, COL_BG_BAR);
+ // this.fill(ms, x, y, x + (int) (width * (fillPcnt / 100f)), y + 10, color);
+ // }
+
+ // void drawTitledIcoBar(MatrixStack ms, ItemStack ico, Text title, int width,
+ // float pcnt, int x, int y, int color) {
+ // final int ICO_OFFS = 3;
+ // this.drawIcon(ico, x, y + ICO_OFFS);
+ // this.drawText(ms, title, x + ICO_DIM + PAD_L, y);
+ // this.drawBar(ms, x + ICO_DIM + PAD_L, y + TEXT_H + PAD_S, width, pcnt,
+ // color);
+ // }
+
+ // void drawIcoFatText(MatrixStack ms, ItemStack ico, Text line1, Text line2,
+ // int x, int y) {
+ // final int ICO_OFFS = 1;
+ // this.drawIcon(ico, x, y + ICO_OFFS);
+ // this.drawText(ms, line1, x + ICO_DIM + PAD_L, y);
+ // this.drawText(ms, line2, x + ICO_DIM + PAD_L, y + TEXT_H + PAD_S);
+ // }
+
+ // public void drawPlayerIco(MatrixStack ms, int x, int y, Identifier
+ // skinTexture) {
+ // RenderSystem.setShaderTexture(0, skinTexture);
+ // PlayerSkinDrawer.draw(ms, x + x + CONTENT_OFFS_X, y + y +
+ // CONTENT_OFFS_Y, SKIN_ICO_DIM);
+ // }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java
new file mode 100644
index 00000000..92b7ed1b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java
@@ -0,0 +1,29 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.render.item.ItemRenderer;
+import net.minecraft.client.util.math.MatrixStack;
+
+public abstract class Component {
+
+ static final int ICO_DIM = 16;
+ static final int PAD_S = 2;
+ static final int PAD_L = 4;
+
+ static TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
+ static ItemRenderer itmRend = MinecraftClient.getInstance().getItemRenderer();
+
+ int width, height;
+
+ public abstract void render(MatrixStack ms, int x, int y);
+
+ public int getWidth() {
+ return this.width;
+ }
+
+ public int getHeight() {
+ return this.height;
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java
new file mode 100644
index 00000000..5865138c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+
+public class IcoFatTextComponent extends Component {
+
+ private static final int ICO_OFFS = 1;
+
+ private ItemStack ico;
+ private Text l1, l2;
+
+ public IcoFatTextComponent(ItemStack ico, Text l1, Text l2) {
+ this.ico = ico;
+ this.l1 = l1;
+ this.l2 = l2;
+
+ this.width = ICO_DIM + PAD_L + Math.max(txtRend.getWidth(l1), txtRend.getWidth(l2)) + PAD_S;
+ this.height = txtRend.fontHeight * 2 + PAD_S;
+ }
+
+ @Override
+ public void render(MatrixStack ms, int x, int y) {
+ itmRend.renderGuiItemIcon(ms, ico, x, y + ICO_OFFS);
+ txtRend.draw(ms, l1, x + ICO_DIM + PAD_L, y, 0xffffffff);
+ txtRend.draw(ms, l2, x + ICO_DIM + PAD_L, y + txtRend.fontHeight + PAD_S, 0xffffffff);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java
new file mode 100644
index 00000000..66f15479
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+
+public class IcoTextComponent extends Component {
+
+ private ItemStack ico;
+ private Text text;
+
+ public IcoTextComponent(ItemStack ico, Text text) {
+ this.ico = ico;
+ this.text = text;
+
+ this.width = ICO_DIM + PAD_L + txtRend.getWidth(text) + PAD_S;
+ this.height = ICO_DIM;
+ }
+
+ @Override
+ public void render(MatrixStack ms, int x, int y) {
+ itmRend.renderGuiItemIcon(ms, ico, x, y);
+ txtRend.draw(ms, text, x + ICO_DIM + PAD_L, y + 5, 0xffffffff);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java
new file mode 100644
index 00000000..b801f3aa
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java
@@ -0,0 +1,22 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.Text;
+
+public class PlainTextComponent extends Component {
+
+ private Text text;
+
+ public PlainTextComponent(Text text) {
+ this.text = text;
+
+ this.width = PAD_S + txtRend.getWidth(text) + PAD_S;
+ this.height = txtRend.fontHeight;
+ }
+
+ @Override
+ public void render(MatrixStack ms, int x, int y) {
+ txtRend.draw(ms, text, x + PAD_S, y, 0xffffffff);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java
new file mode 100644
index 00000000..7e1fd399
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java
@@ -0,0 +1,33 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+
+import net.minecraft.client.gui.PlayerSkinDrawer;
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.util.Identifier;
+
+public class PlayerComponent extends Component {
+
+ private static final int SKIN_ICO_DIM = 8;
+
+ private String name;
+ private Identifier tex;
+
+ public PlayerComponent(PlayerListEntry ple) {
+
+ name = ple.getProfile().getName();
+ tex = ple.getSkinTexture();
+
+ this.width = txtRend.getWidth(name) + PAD_S + SKIN_ICO_DIM;
+ this.height = txtRend.fontHeight;
+ }
+
+ @Override
+ public void render(MatrixStack ms, int x, int y) {
+ RenderSystem.setShaderTexture(0, tex);
+ PlayerSkinDrawer.draw(ms, x, y, SKIN_ICO_DIM);
+ txtRend.draw(ms, name, x + SKIN_ICO_DIM + PAD_S, y, 0xffffffff);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java
new file mode 100644
index 00000000..41539d1c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java
@@ -0,0 +1,49 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+
+public class ProgressComponent extends Component {
+
+ private static final int BAR_WIDTH = 100;
+ private static final int BAR_HEIGHT = txtRend.fontHeight + 3;
+ private static final int ICO_OFFS = 4;
+ private static final int COL_BG_BAR = 0xf0101010;
+
+ private ItemStack ico;
+ private Text desc, bar;
+ private float pcnt;
+ private int color;
+ private int barW;
+
+ public ProgressComponent(ItemStack ico, Text desc, Text bar, float pcnt, int color) {
+ this.ico = ico;
+ this.desc = desc;
+ this.bar = bar;
+ this.color = 0xff000000 | color;
+ this.pcnt = pcnt;
+
+ this.barW = BAR_WIDTH;
+ this.height = txtRend.fontHeight + PAD_S + 2 + txtRend.fontHeight + 2;
+ this.width = ICO_DIM + PAD_L + Math.max(this.barW, txtRend.getWidth(desc)) + PAD_S;
+ }
+
+ public ProgressComponent(ItemStack ico, Text text, float pcnt, int color) {
+ this(ico, text, Text.of(pcnt + "%"), pcnt, color);
+ }
+
+ @Override
+ public void render(MatrixStack ms, int x, int y) {
+ itmRend.renderGuiItemIcon(ms, ico, x, y + ICO_OFFS);
+ txtRend.draw(ms, desc, x + ICO_DIM + PAD_L, y, 0xffffffff);
+
+ int barX = x + ICO_DIM + PAD_L;
+ int barY = y + txtRend.fontHeight + PAD_S;
+ DrawableHelper.fill(ms, barX, barY, barX + this.barW, barY + BAR_HEIGHT, COL_BG_BAR);
+ DrawableHelper.fill(ms, barX, barY, barX + ((int) (this.barW * (this.pcnt / 100f))), barY + BAR_HEIGHT,
+ this.color);
+ txtRend.drawWithShadow(ms, bar, barX + 3, barY + 2, 0xffffffff);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java
new file mode 100644
index 00000000..66c1b73b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java
@@ -0,0 +1,53 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.util.math.MatrixStack;
+
+// FIXME: table isn't wide enough sometimes
+// FIXME: dividers drift when there are >2 cols
+public class TableComponent extends Component {
+
+ private Component[][] comps;
+ private int color;
+ private int tw, th;
+ private int cellW, cellH;
+
+ public TableComponent(int w, int h, int col) {
+ comps = new Component[w][h];
+ color = 0xff000000 | col;
+ tw = w;
+ th = h;
+ }
+
+ public void addToCell(int x, int y, Component c) {
+ comps[x][y] = c;
+
+ // are tables still too wide?
+ cellW = Math.max(cellW, c.width + PAD_S);
+
+ // assume all rows are equally high so overwriting doesn't matter
+ // if this wasn't the case, drawing would need more math
+ // not doing any of that if it's not needed
+ cellH = c.height;
+
+ this.width = cellW * tw;
+ this.height = (cellH + PAD_S) * th - PAD_S;
+
+ }
+
+ @Override
+ public void render(MatrixStack ms, int xpos, int ypos) {
+ for (int x = 0; x < tw; x++) {
+ for (int y = 0; y < th; y++) {
+ if (comps[x][y] != null) {
+ comps[x][y].render(ms, xpos + x * cellW + x * PAD_L, ypos + y * cellH + y * PAD_S);
+ }
+ }
+ if (x != tw - 1) {
+ DrawableHelper.fill(ms, xpos + ((x + 1) * (cellW + PAD_S)) - 1, ypos + 1,
+ xpos + ((x + 1) * (cellW + PAD_S)), ypos + this.height - 1, color);
+ }
+ }
+ }
+
+}
diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json
index fc37cfb0..6d3fd9ee 100644
--- a/src/main/resources/skyblocker.mixins.json
+++ b/src/main/resources/skyblocker.mixins.json
@@ -16,7 +16,9 @@
"HandledScreenMixin",
"InventoryScreenMixin",
"RecipeBookWidgetAccessor",
- "HandledScreenAccessor"
+ "HandledScreenAccessor",
+ "PlayerListHudAccessor",
+ "PlayerListHudMixin"
],
"injectors": {
"defaultRequire": 1