aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java18
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java68
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java59
-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/EmptyScreen.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java262
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GardenInfoScreen.java51
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java52
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonHubScreen.java25
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java38
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java25
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java31
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java31
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/PlayerListScreen.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java52
-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.java81
-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.java95
-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.java37
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java61
-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/GardenSkillsWidget.java63
-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/JacobsContestWidget.java56
-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.java189
-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/assets/skyblocker/lang/de_de.json4
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json8
-rw-r--r--src/main/resources/skyblocker.mixins.json4
70 files changed, 3214 insertions, 17 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 16a10f5e..96bab542 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
@@ -126,6 +126,8 @@ public class SkyblockerConfig implements ConfigData {
public boolean enableUpdateNotification = true;
public boolean backpackPreviewWithoutShift = false;
+ public boolean tabHudEnabled = true;
+
@ConfigEntry.Gui.Excluded
public String apiKey;
@@ -267,11 +269,29 @@ public class SkyblockerConfig implements ConfigData {
public static class DwarvenHud {
public boolean enabled = true;
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
+ @ConfigEntry.Gui.Tooltip(count = 3)
+ public Style style = Style.SIMPLE;
public boolean enableBackground = true;
public int x = 10;
public int y = 10;
}
+ public enum Style {
+ SIMPLE,
+ FANCY,
+ CLASSIC;
+
+ @Override
+ public String toString() {
+ return switch (this) {
+ case SIMPLE -> "Simple";
+ case FANCY -> "Fancy";
+ case CLASSIC -> "Classic";
+ };
+ }
+ }
+
public static class Messages {
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
public ChatFilterResult hideAbility = ChatFilterResult.PASS;
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..ca6c9b41
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
@@ -0,0 +1,68 @@
+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;
+
+ @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();
+ MinecraftClient.getInstance().player.sendMessage(Text.of("The tab HUD has encountered unexpected text, see log! :("));
+ }
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
index 86fe58fe..9f1e783c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
@@ -1,6 +1,7 @@
package me.xmrvizzy.skyblocker.skyblock.dwarven;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CommsWidget;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
@@ -55,17 +56,49 @@ public class DwarvenHud {
}
public static void render(MatrixStack matrixStack, int hudX, int hudY, List<Commission> commissions) {
- if (commissions.size() > 0){
- if (SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground)
- DrawableHelper.fill(matrixStack, hudX, hudY, hudX + 200, hudY + (20 * commissions.size()), 0x64000000);
- int y = 0;
- for (Commission commission : commissions) {
- client.textRenderer.drawWithShadow(matrixStack, Text.literal(commission.commission).styled(style -> style.withColor(Formatting.AQUA)).append(Text.literal(": " + commission.progression).styled(style -> style.withColor(Formatting.GREEN))), hudX + 5, hudY + y + 5, 0xFFFFFFFF);
- y += 20;
- }
+ if (commissions.size() <= 0) {
+ return;
+ }
+
+ switch(SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.style) {
+ case SIMPLE -> renderSimple(matrixStack, hudX, hudY, commissions);
+ case FANCY -> renderFancy(matrixStack, hudX, hudY, commissions);
+ case CLASSIC -> renderClassic(matrixStack, hudX, hudY, commissions);
+ }
+ }
+
+ public static void renderClassic(MatrixStack matrixStack, int hudX, int hudY, List<Commission> commissions) {
+ if (SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground) {
+ DrawableHelper.fill(matrixStack, hudX, hudY, hudX + 200, hudY + (20 * commissions.size()), 0x64000000);
+ }
+
+ int y = 0;
+ for (Commission commission : commissions) {
+ client.textRenderer
+ .drawWithShadow(matrixStack,
+ Text.literal(commission.commission + ": ")
+ .styled(style -> style.withColor(Formatting.AQUA))
+ .append(Text.literal(commission.progression)
+ .styled(style -> style.withColor(Formatting.GREEN))),
+ hudX + 5, hudY + y + 5, 0xFFFFFFFF);
+ y += 20;
}
}
+ public static void renderSimple(MatrixStack matrixStack, int hudX, int hudY, List<Commission> commissions) {
+ CommsWidget cw = new CommsWidget(commissions, false);
+ cw.setX(hudX);
+ cw.setY(hudY);
+ cw.render(matrixStack, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground);
+ }
+
+ public static void renderFancy(MatrixStack matrixStack, int hudX, int hudY, List<Commission> commissions) {
+ CommsWidget cw = new CommsWidget(commissions, true);
+ cw.setX(hudX);
+ cw.setY(hudY);
+ cw.render(matrixStack, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground);
+ }
+
public static void update() {
commissionList = new ArrayList<>();
if (client.player == null || !SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enabled) return;
@@ -83,13 +116,7 @@ public class DwarvenHud {
});
}
- public static class Commission{
- final String commission;
- final String progression;
+ // steamroller tactics to get visibility from outside classes
+ public static record Commission(String commission, String progression){}
- public Commission(String commission, String progression){
- this.commission = commission;
- this.progression = progression;
- }
- }
}
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..01c196f7
--- /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.skyblocker.playerTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_LEFT_SHIFT,
+ "key.categories.skyblocker"));
+ genericTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyblocker.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.skyblocker.defaultTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_B,
+ "key.categories.skyblocker"));
+
+ }
+}
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/Screen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java
new file mode 100644
index 00000000..0274f839
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java
@@ -0,0 +1,262 @@
+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.screens.genericInfo.*;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.*;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.*;
+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 Screen.correctGenericScrn(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 correctGenericScrn(int w, int h, List<PlayerListEntry> list, Text footer) {
+ return switch (getScreenType(list)) {
+ case GARDEN -> new GardenInfoScreen(w, h, list, footer); // ok
+ default -> new GenericInfoScreen(w, h, list, footer); // ok
+ };
+ }
+
+
+ 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/screens/genericInfo/GardenInfoScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GardenInfoScreen.java
new file mode 100644
index 00000000..639bc135
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GardenInfoScreen.java
@@ -0,0 +1,51 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CookieWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EffectWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EventWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GardenSkillsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.JacobsContestWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ProfileWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.UpgradeWidget;
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+public class GardenInfoScreen extends Screen {
+
+ public GardenInfoScreen(int w, int h, List<PlayerListEntry> ple, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ GardenSkillsWidget gsw = new GardenSkillsWidget(ple);
+ EventWidget evw = new EventWidget(ple, true);
+ UpgradeWidget uw = new UpgradeWidget(f);
+
+ ProfileWidget pw = new ProfileWidget(ple);
+ EffectWidget efw = new EffectWidget(f);
+
+ JacobsContestWidget jcw = new JacobsContestWidget(ple);
+ CookieWidget cw = new CookieWidget(f);
+
+ // goofy ahh layout code incoming
+ this.stackWidgetsH(gsw, evw, uw);
+ this.stackWidgetsH(pw, efw);
+ this.stackWidgetsH(jcw, cw);
+
+ this.centerW(gsw);
+ this.centerW(evw);
+ this.centerW(uw);
+
+ this.collideAgainstL(pw, gsw, evw, uw);
+ this.collideAgainstL(efw, gsw, evw, uw);
+
+ this.collideAgainstR(jcw, gsw, evw, uw);
+ this.collideAgainstR(cw, gsw, evw, uw);
+
+ this.addWidgets(gsw, evw, uw, pw, efw, jcw, cw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java
new file mode 100644
index 00000000..dda4b401
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java
@@ -0,0 +1,52 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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, false);
+ 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/main/DungeonHubScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonHubScreen.java
new file mode 100644
index 00000000..5279f196
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonHubScreen.java
@@ -0,0 +1,25 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/DungeonScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java
new file mode 100644
index 00000000..ed8160cb
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java
@@ -0,0 +1,38 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/FarmingServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java
new file mode 100644
index 00000000..ea374c6d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/GardenScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java
new file mode 100644
index 00000000..4ea11109
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/GenericServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java
new file mode 100644
index 00000000..c8cc60a6
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java
@@ -0,0 +1,20 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/GuestServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java
new file mode 100644
index 00000000..6a583c38
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java
@@ -0,0 +1,20 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/HomeServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java
new file mode 100644
index 00000000..3c2b8fac
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java
@@ -0,0 +1,25 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/HubServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java
new file mode 100644
index 00000000..0a488d1d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/MineServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java
new file mode 100644
index 00000000..0fe408bc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java
@@ -0,0 +1,31 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/main/ParkServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java
new file mode 100644
index 00000000..90ab9edb
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java
@@ -0,0 +1,20 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/playerList/DungeonPlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java
new file mode 100644
index 00000000..a7aa970d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java
@@ -0,0 +1,31 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/playerList/GuestPlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java
new file mode 100644
index 00000000..30c3a4ba
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/playerList/HomePlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java
new file mode 100644
index 00000000..08f44d8f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java
@@ -0,0 +1,23 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/playerList/PlayerListScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/PlayerListScreen.java
new file mode 100644
index 00000000..d40353b8
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/PlayerListScreen.java
@@ -0,0 +1,20 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+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/util/Ico.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java
new file mode 100644
index 00000000..120cabd8
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java
@@ -0,0 +1,52 @@
+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);
+ public static final ItemStack MILESTONE = new ItemStack(Items.LODESTONE);
+ public static final ItemStack PICKAXE = new ItemStack(Items.IRON_PICKAXE);
+}
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..120c53d8
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java
@@ -0,0 +1,81 @@
+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.dwarven.DwarvenHud.Commission;
+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.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;
+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();
+ }
+
+ // for the dwarven hud
+ public CommsWidget(List<Commission> commissions, boolean isFancy) {
+ super(TITLE, Formatting.AQUA.getColorValue());
+ for (Commission comm : commissions) {
+
+ Text c = Text.literal(comm.commission());
+
+ float p = 100f;
+ if (!comm.progression().contains("DONE")) {
+ p = Float.parseFloat(comm.progression().substring(0, comm.progression().length() - 1));
+ }
+
+ Component comp;
+ if (isFancy) {
+ comp = new ProgressComponent(Ico.BOOK, c, p, pcntToCol(p));
+ } else {
+ comp = new PlainTextComponent(
+ Text.literal(comm.commission() + ": ")
+ .append(Text.literal(comm.progression()).formatted(Formatting.GREEN)));
+ }
+ this.addComponent(comp);
+ }
+ 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..1c845467
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java
@@ -0,0 +1,95 @@
+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;
+
+// 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("Cole", Ico.PICKAXE);
+ 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..7fcbe1c1
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java
@@ -0,0 +1,37 @@
+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, boolean isInGarden) {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ // hypixel devs carefully inserting the most random edge cases #317:
+ // the event info is placed a bit differently when in the garden.
+ int offset = (isInGarden) ? -1 : 0;
+
+ Text eventName = StrMan.stdEntry(list, 73 + offset, "Name:", Formatting.YELLOW);
+ IcoTextComponent event = new IcoTextComponent(Ico.NTAG, eventName);
+ this.addComponent(event);
+
+ // this could look better
+ Text time = StrMan.plainEntry(list, 74 + offset);
+ 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..97b0e8a2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java
@@ -0,0 +1,61 @@
+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.math.MathHelper;
+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, pcntToCol(pcnt));
+ this.addComponent(pc);
+ }
+ if (!found) {
+ this.addComponent(new PlainTextComponent(Text.literal("No Fire Sale!").formatted(Formatting.GRAY)));
+ }
+ 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/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/GardenSkillsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java
new file mode 100644
index 00000000..99011616
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.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;
+
+
+public class GardenSkillsWidget 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*)%");
+ // same with leading space
+ // TODO: make better, maybe
+ private static final Pattern MS_PATTERN = Pattern.compile(" \\S*: ([A-Za-z]* [0-9]*): (\\S*)%");
+
+ public GardenSkillsWidget(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 farmfort = StrMan.stdEntry(list, 68, "FFO", Formatting.GOLD);
+ IcoTextComponent ffo = new IcoTextComponent(Ico.HOE, farmfort);
+
+ TableComponent tc = new TableComponent(2, 1, Formatting.YELLOW.getColorValue());
+ tc.addToCell(0, 0, spd);
+ tc.addToCell(1, 0, ffo);
+ this.addComponent(tc);
+
+ m = StrMan.regexAt(list, 69, MS_PATTERN);
+ pcnt = Float.parseFloat(m.group(2));
+ skill = m.group(1);
+
+ ProgressComponent pc2 = new ProgressComponent(Ico.MILESTONE, Text.of(skill), pcnt, Formatting.GREEN.getColorValue());
+ this.addComponent(pc2);
+
+ 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/JacobsContestWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java
new file mode 100644
index 00000000..11817662
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java
@@ -0,0 +1,56 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+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.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class JacobsContestWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Jacob's Contest").formatted(Formatting.YELLOW,
+ Formatting.BOLD);
+
+ private static final HashMap<String, ItemStack> FARM_DATA = new HashMap<>();
+
+ // again, there HAS to be a better way to do this
+ static {
+ FARM_DATA.put("Wheat", new ItemStack(Items.WHEAT));
+ FARM_DATA.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE));
+ FARM_DATA.put("Carrot", new ItemStack(Items.CARROT));
+ FARM_DATA.put("Potato", new ItemStack(Items.POTATO));
+ FARM_DATA.put("Melon", new ItemStack(Items.MELON_SLICE));
+ FARM_DATA.put("Pumpkin", new ItemStack(Items.PUMPKIN));
+ FARM_DATA.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS));
+ FARM_DATA.put("Nether Wart", new ItemStack(Items.NETHER_WART));
+ FARM_DATA.put("Cactus", new ItemStack(Items.CACTUS));
+ FARM_DATA.put("Mushroom", new ItemStack(Items.RED_MUSHROOM));
+ }
+
+ public JacobsContestWidget(List<PlayerListEntry> list) {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ Text time = StrMan.stdEntry(list, 76, "Starts in:", Formatting.GOLD);
+ IcoTextComponent t = new IcoTextComponent(Ico.CLOCK, time);
+ this.addComponent(t);
+
+ TableComponent tc = new TableComponent(1, 3, Formatting.YELLOW .getColorValue());
+
+ for (int i = 77; i < 80; i++) {
+ String item = StrMan.strAt(list, i).trim();
+ tc.addToCell(0, i - 77, new IcoTextComponent(FARM_DATA.get(item), Text.of(item)));
+ }
+ this.addComponent(tc);
+
+ 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..63a72563
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java
@@ -0,0 +1,189 @@
+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) {
+ this.render(ms, true);
+ }
+
+ public final void render(MatrixStack ms, boolean hasBG) {
+
+ if (hasBG) {
+ 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/assets/skyblocker/lang/de_de.json b/src/main/resources/assets/skyblocker/lang/de_de.json
index a74b39ff..c678b353 100644
--- a/src/main/resources/assets/skyblocker/lang/de_de.json
+++ b/src/main/resources/assets/skyblocker/lang/de_de.json
@@ -1,10 +1,14 @@
{
"key.categories.skyblocker": "Skyblocker",
"key.hotbarSlotLock": "Schlitzsperre (Hotbar)",
+ "key.skyblocker.playerTgl": "Tab-HUD auf Spielerliste umstellen",
+ "key.skyblocker.defaultTgl": "Tab-HUD auf Standard umstellen",
+ "key.skyblocker.genericTgl": "Tab-HUD auf allgemeine Infos umstellen",
"text.autoconfig.skyblocker.title": "Skyblocker-Einstellungen",
"text.autoconfig.skyblocker.category.general": "Allgemein",
+ "text.autoconfig.skyblocker.option.general.tabHudEnabled": "Schöneres Tab-HUD aktivieren",
"text.autoconfig.skyblocker.option.general.bars": "Gesundheits-, Mana-, Verteidigungs- und XP-Balken",
"text.autoconfig.skyblocker.option.general.bars.enableBars": "Balken aktivieren",
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index ddfcdfee..613a89c9 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -1,6 +1,9 @@
{
"key.categories.skyblocker": "Skyblocker",
"key.hotbarSlotLock": "Slot Lock (Hotbar)",
+ "key.skyblocker.playerTgl": "Switch tab HUD to player list",
+ "key.skyblocker.defaultTgl": "Switch tab HUD to default view",
+ "key.skyblocker.genericTgl": "Switch tab HUD to general info",
"key.wikiLookup": "Wiki Lookup",
"text.autoconfig.skyblocker.title": "Skyblocker Settings",
@@ -20,6 +23,7 @@
"text.autoconfig.skyblocker.option.general.quicknav": "Quicknav",
"text.autoconfig.skyblocker.option.general.quicknav.enableQuicknav": "Enable Quicknav",
"text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift": "View backpack preview without holding Shift",
+ "text.autoconfig.skyblocker.option.general.tabHudEnabled": "Enable fancy tab HUD",
"text.autoconfig.skyblocker.option.general.itemTooltip": "Item Tooltip",
"text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice": "Enable NPC Price",
"text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN": "Enable Avg. BIN Price",
@@ -83,6 +87,10 @@
"text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "Solve Puzzler Puzzle",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud": "Dwarven HUD",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled": "Enabled",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style": "Style for HUD",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]": "Simple: Shows name and percentage.",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[1]": "Fancy: Shows name, percentage, progress bar and an icon.",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]": "Classic: Shows name and percentage in a very simple box.",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enableBackground": "Enable Background",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.x": "X",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.y": "Y",
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