aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java15
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java18
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java57
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java63
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java16
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java225
-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.java48
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/CrimsonIsleScreen.java32
-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.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java21
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java29
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java25
-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.java55
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java111
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java89
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java91
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java48
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java45
-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.java99
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java55
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java64
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java103
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java68
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java79
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java53
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java78
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java28
-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.java60
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java37
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java60
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java133
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java73
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java25
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java29
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java67
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java75
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java57
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java202
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java45
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java36
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java70
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java59
-rw-r--r--src/main/resources/assets/skyblocker/lang/de_de.json7
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json11
-rw-r--r--src/main/resources/skyblocker.mixins.json3
76 files changed, 3665 insertions, 20 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
index 2ab1b45e..e0f950d0 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
@@ -17,6 +17,8 @@ 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.quicknav.QuickNav;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
import me.xmrvizzy.skyblocker.utils.MessageScheduler;
import me.xmrvizzy.skyblocker.utils.Scheduler;
import me.xmrvizzy.skyblocker.utils.UpdateChecker;
@@ -26,7 +28,9 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.MinecraftClient;
/**
- * Main class for Skyblocker which initializes features, registers events, and manages ticks. This class will be instantiated by Fabric. Do not instantiate this class.
+ * Main class for Skyblocker which initializes features, registers events, and
+ * manages ticks. This class will be instantiated by Fabric. Do not instantiate
+ * this class.
*/
public class SkyblockerMod implements ClientModInitializer {
public static final String NAMESPACE = "skyblocker";
@@ -51,7 +55,9 @@ public class SkyblockerMod implements ClientModInitializer {
}
/**
- * Register {@link #tick(MinecraftClient)} to {@link ClientTickEvents#END_CLIENT_TICK}, initialize all features, and schedule tick events.
+ * Register {@link #tick(MinecraftClient)} to
+ * {@link ClientTickEvents#END_CLIENT_TICK}, initialize all features, and
+ * schedule tick events.
*/
@Override
public void onInitializeClient() {
@@ -71,6 +77,7 @@ public class SkyblockerMod implements ClientModInitializer {
DiscordRPCManager.init();
LividColor.init();
FishingHelper.init();
+ TabHud.init();
containerSolverManager.init();
scheduler.scheduleCyclic(Utils::sbChecker, 20);
scheduler.scheduleCyclic(DiscordRPCManager::update, 100);
@@ -78,10 +85,12 @@ public class SkyblockerMod implements ClientModInitializer {
scheduler.scheduleCyclic(LividColor::update, 10);
scheduler.scheduleCyclic(BackpackPreview::tick, 50);
scheduler.scheduleCyclic(DwarvenHud::update, 40);
+ scheduler.scheduleCyclic(PlayerListMgr::updateList, 20);
}
/**
- * Ticks the scheduler. Called once at the end of every client tick through {@link ClientTickEvents#END_CLIENT_TICK}.
+ * Ticks the scheduler. Called once at the end of every client tick through
+ * {@link ClientTickEvents#END_CLIENT_TICK}.
*
* @param client the Minecraft client.
*/
diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
index c87f0ad9..7fdd7254 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
@@ -135,6 +135,10 @@ public class SkyblockerConfig implements ConfigData {
public boolean backpackPreviewWithoutShift = false;
public boolean hideEmptyTooltips = true;
+ @ConfigEntry.Category("tabHud")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public TabHudConf tabHud = new TabHudConf();
+
@ConfigEntry.Gui.Excluded
public String apiKey;
@@ -162,6 +166,14 @@ public class SkyblockerConfig implements ConfigData {
public List<Integer> lockedSlots = new ArrayList<>();
}
+ public static class TabHudConf {
+ public boolean tabHudEnabled = true;
+
+ @ConfigEntry.BoundedDiscrete(min=10, max=200)
+ @ConfigEntry.Gui.Tooltip()
+ public int tabHudScale = 100;
+ }
+
public static class Bars {
public boolean enableBars = true;
@@ -298,11 +310,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 Barn {
public boolean solveHungryHiker = true;
public boolean solveTreasureHunter = true;
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..db329775
--- /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..b5403ae2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
@@ -0,0 +1,57 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+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.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()
+ || !SkyblockerConfig.get().general.tabHud.tabHudEnabled
+ || TabHud.defaultTgl.isPressed()) {
+ return;
+ }
+
+ MinecraftClient client = MinecraftClient.getInstance();
+ ClientPlayNetworkHandler nwH = client.getNetworkHandler();
+ if (nwH == null) {
+ return;
+ }
+
+ int w = scaledW;
+ int h = MinecraftClient.getInstance().getWindow().getScaledHeight();
+ try {
+ Screen screen = Screen.getCorrect(w, h, footer);
+ screen.render(ms);
+ info.cancel();
+ } catch (Exception e) {
+ TabHud.LOGGER.error("Drawing default hud. Reason: Screen exception {}", e);
+ }
+ }
+
+} \ No newline at end of file
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..15f9e58b 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;
@@ -49,23 +50,57 @@ public class DwarvenHud {
})))));
HudRenderCallback.EVENT.register((matrixStack, tickDelta) -> {
- if (!SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enabled || client.player == null || commissionList.isEmpty()) return;
+ if (!SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enabled
+ || client.options.playerListKey.isPressed()
+ || client.player == null
+ || commissionList.isEmpty()) {
+ return;
+ }
render(matrixStack, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.x, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.y, commissionList);
});
}
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;
- }
+
+ 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 +118,7 @@ public class DwarvenHud {
});
}
- public static class Commission{
- final String commission;
- final String progression;
+ // steamroller tactics to get visibility from outside classes (CommsWidget)
+ 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..6d90b269
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud;
+
+import org.lwjgl.glfw.GLFW;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Tab HUD");
+
+ public static void init() {
+
+ playerTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyblocker.playerTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_B,
+ "key.categories.skyblocker"));
+ genericTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyblocker.genericTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_N,
+ "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_M,
+ "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..5c302eb3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java
@@ -0,0 +1,16 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EmptyWidget;
+
+import net.minecraft.text.Text;
+
+public class EmptyScreen extends Screen {
+
+ public EmptyScreen(int w, int h, Text footer) {
+ super(w, h);
+ EmptyWidget ew = new EmptyWidget();
+ 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..43b7f956
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java
@@ -0,0 +1,225 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.ArrayList;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo.GardenInfoScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo.GenericInfoScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.CrimsonIsleScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.DungeonHubScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.DungeonScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.FarmingServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.GardenScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.GenericServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.GuestServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.HomeServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.HubServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.MineServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.ParkServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.DungeonPlayerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.GuestPlayerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.HomePlayerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.PlayerListScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerLocator;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.Text;
+
+public class Screen {
+
+ private ArrayList<Widget> widgets = new ArrayList<>();
+ private int w, h;
+
+ public Screen(int w, int h) {
+ float scale = SkyblockerConfig.get().general.tabHud.tabHudScale / 100f;
+ this.w = (int) (w / scale);
+ this.h = (int) (h / scale);
+ }
+
+ public static Screen getCorrect(int w, int h, Text footer) {
+ if (TabHud.genericTgl.isPressed()) {
+ return Screen.correctGenericScrn(w, h, footer);
+ } else if (TabHud.playerTgl.isPressed()) {
+ return Screen.correctPlayerScrn(w, h, footer);
+ } else {
+ return Screen.correctMainScrn(w, h, footer);
+ }
+ }
+
+ private static Screen correctGenericScrn(int w, int h, Text footer) {
+ return switch (PlayerLocator.getPlayerLocation()) {
+ case GARDEN -> new GardenInfoScreen(w, h, footer); // ok
+ case UNKNOWN -> new EmptyScreen(w, h, footer); // ok
+ default -> new GenericInfoScreen(w, h, footer); // ok
+ };
+ }
+
+ private static Screen correctPlayerScrn(int w, int h, Text footer) {
+ return switch (PlayerLocator.getPlayerLocation()) {
+ case GUEST_ISLAND -> new GuestPlayerScreen(w, h, footer); // ok
+ case HOME_ISLAND, GARDEN -> new HomePlayerScreen(w, h, footer); // ok for 1 player
+ case DUNGEON -> new DungeonPlayerScreen(w, h, footer);
+ case UNKNOWN -> new EmptyScreen(w, h, footer); // ok
+ default -> new PlayerListScreen(w, h, footer); // ok
+ };
+ }
+
+ private static Screen correctMainScrn(int w, int h, Text footer) {
+ return switch (PlayerLocator.getPlayerLocation()) {
+ case PARK -> new ParkServerScreen(w, h, footer); // ok
+ case HUB -> new HubServerScreen(w, h, footer); // ok when fire sale incoming
+ case HOME_ISLAND -> new HomeServerScreen(w, h, footer); // ok
+ case GUEST_ISLAND -> new GuestServerScreen(w, h, footer); // ok
+ case CRYSTAL_HOLLOWS, DWARVEN_MINES -> new MineServerScreen(w, h, footer); // ok, TODO active forge
+ case FARMING_ISLAND -> new FarmingServerScreen(w, h, footer);
+ case DUNGEON_HUB -> new DungeonHubScreen(w, h, footer); // ok
+ case DUNGEON -> new DungeonScreen(w, h, footer); // ok
+ case CRIMSON_ISLE -> new CrimsonIsleScreen(w, h, footer);
+ case GARDEN -> new GardenScreen(w, h, footer); // ok
+ case UNKNOWN -> new EmptyScreen(w, h, footer); // ok
+ default -> new GenericServerScreen(w, h, footer); // ok
+ };
+ }
+
+ /**
+ * Add a widget to this screen
+ */
+ public void addWidget(Widget w) {
+ widgets.add(w);
+ }
+
+ /**
+ * Add many widgets to this screen
+ */
+ public void addWidgets(Widget... ws) {
+ for (Widget w : ws) {
+ widgets.add(w);
+ }
+ }
+
+ public void render(MatrixStack ms) {
+ for (Widget w : widgets) {
+ w.render(ms);
+ }
+ }
+
+ /**
+ * Stack these widgets on top of each other as determined by the lists's order
+ */
+ 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;
+ }
+ }
+
+ /**
+ * Arrange these widgets next to each other as determined by the lists's order
+ */
+ 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;
+ }
+ }
+
+ /**
+ * Center a widget vertically, keeping X pos
+ */
+ public void centerH(Widget wid) {
+ wid.setY((h - wid.getHeight()) / 2);
+ }
+
+ /**
+ * Center a widget horizontally, keeping Y pos
+ */
+ public void centerW(Widget wid) {
+ wid.setX((w - wid.getWidth()) / 2);
+ }
+
+ /**
+ * Center a widget vertically and horizontally
+ */
+ public void center(Widget wid) {
+ this.centerH(wid);
+ this.centerW(wid);
+ }
+
+ /**
+ * Let a widget's left border be on the screen's center, keeping Y pos
+ */
+ public void offCenterL(Widget wid) {
+ int wHalf = this.w / 2;
+ wid.setX(wHalf - 3 - wid.getWidth());
+ }
+
+ /**
+ * Let a widget's right border be on the screen's center, keeping Y pos
+ */
+ 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);
+ }
+}
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..0bb12c8e
--- /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 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.text.Text;
+
+public class GardenInfoScreen extends Screen {
+
+ public GardenInfoScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ GardenSkillsWidget gsw = new GardenSkillsWidget();
+ EventWidget evw = new EventWidget(true);
+ UpgradeWidget uw = new UpgradeWidget(f);
+
+ ProfileWidget pw = new ProfileWidget();
+ EffectWidget efw = new EffectWidget(f);
+
+ JacobsContestWidget jcw = new JacobsContestWidget();
+ CookieWidget cw = new CookieWidget(f);
+
+ // 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..046a9313
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java
@@ -0,0 +1,48 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo;
+
+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.text.Text;
+
+public class GenericInfoScreen extends Screen {
+
+ public GenericInfoScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ SkillsWidget sw = new SkillsWidget();
+ EventWidget evw = new EventWidget(false);
+ UpgradeWidget uw = new UpgradeWidget(f);
+
+ ProfileWidget pw = new ProfileWidget();
+ EffectWidget efw = new EffectWidget(f);
+
+ ElectionWidget elw = new ElectionWidget();
+ CookieWidget cw = new CookieWidget(f);
+
+ 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/CrimsonIsleScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/CrimsonIsleScreen.java
new file mode 100644
index 00000000..6e6f563b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/CrimsonIsleScreen.java
@@ -0,0 +1,32 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.QuestWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ReputationWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.VolcanoWidget;
+
+import net.minecraft.text.Text;
+
+public class CrimsonIsleScreen extends Screen {
+
+ public CrimsonIsleScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ ReputationWidget rw = new ReputationWidget();
+ QuestWidget qw = new QuestWidget();
+ VolcanoWidget vw = new VolcanoWidget();
+
+ this.stackWidgetsH(sw, rw);
+ this.stackWidgetsH(qw, vw);
+ this.offCenterL(sw);
+ this.offCenterL(rw);
+ this.offCenterR(vw);
+ this.offCenterR(qw);
+ this.addWidgets(sw, rw, qw, vw);
+ }
+
+}
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..5db461af
--- /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 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.text.Text;
+
+public class DungeonHubScreen extends Screen{
+
+ public DungeonHubScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ EssenceWidget ew = new EssenceWidget();
+
+ this.centerW(sw);
+ this.centerW(ew);
+ this.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..852ee876
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonBuffWidget;
+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.text.Text;
+
+public class DungeonScreen extends Screen {
+
+ public DungeonScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ DungeonDownedWidget ddow = new DungeonDownedWidget();
+ DungeonDeathWidget ddew = new DungeonDeathWidget();
+ DungeonSecretWidget dscw = new DungeonSecretWidget();
+ DungeonServerWidget dsrw = new DungeonServerWidget();
+ DungeonPuzzleWidget dpuw = new DungeonPuzzleWidget();
+ DungeonBuffWidget dbw = new DungeonBuffWidget(f);
+
+ this.offCenterL(ddow);
+ this.offCenterL(ddew);
+ this.offCenterL(dbw);
+ this.offCenterR(dsrw);
+ this.offCenterR(dpuw);
+ this.offCenterR(dscw);
+
+ this.stackWidgetsH(ddow, ddew, dbw);
+ this.stackWidgetsH(dsrw, dpuw, dscw);
+
+ this.addWidgets(ddow, ddew, dscw, dsrw, dpuw, dbw);
+
+ }
+
+}
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..02c81f23
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+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.text.Text;
+
+public class FarmingServerScreen extends Screen{
+
+ public FarmingServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ TrapperWidget tw = new TrapperWidget();
+
+ this.centerW(sw);
+ this.centerW(tw);
+ this.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..ae5b642f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java
@@ -0,0 +1,23 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+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.text.Text;
+
+public class GardenScreen extends Screen{
+
+ public GardenScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ GardenServerWidget gsw = new GardenServerWidget();
+ ComposterWidget cw = new ComposterWidget();
+
+ 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..a89563db
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java
@@ -0,0 +1,21 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+import net.minecraft.text.Text;
+
+public class GenericServerScreen extends Screen {
+
+ public GenericServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+
+ 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..57d7a199
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java
@@ -0,0 +1,22 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GuestServerWidget;
+
+
+import net.minecraft.text.Text;
+
+public class GuestServerScreen extends Screen{
+
+ public GuestServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ GuestServerWidget gsw = new GuestServerWidget();
+
+ 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..e61ba4b0
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+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.text.Text;
+
+public class HomeServerScreen extends Screen {
+
+ public HomeServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ IslandServerWidget isw = new IslandServerWidget();
+ MinionWidget mw = new MinionWidget();
+
+ 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..e2857f7e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+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.text.Text;
+
+public class HubServerScreen extends Screen {
+
+ public HubServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ FireSaleWidget fsw = new FireSaleWidget();
+
+ 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..616c3e82
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+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.text.Text;
+
+public class MineServerScreen extends Screen {
+
+ public MineServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ PowderWidget pw = new PowderWidget();
+ CommsWidget cw = new CommsWidget();
+ ForgeWidget fw = new ForgeWidget();
+
+ this.stackWidgetsH(sw, cw);
+ this.stackWidgetsH(fw, pw);
+ this.offCenterL(sw);
+ this.offCenterL(cw);
+ this.offCenterR(pw);
+ this.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..aa65d946
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ParkServerWidget;
+
+import net.minecraft.text.Text;
+
+public class ParkServerScreen extends Screen{
+
+ public ParkServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ParkServerWidget sw = new ParkServerWidget();
+
+ 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..2567da13
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java
@@ -0,0 +1,29 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonPlayerWidget;
+import net.minecraft.text.Text;
+
+public class DungeonPlayerScreen extends Screen {
+
+ public DungeonPlayerScreen(int w, int h, Text footer) {
+
+ super(w, h);
+
+ DungeonPlayerWidget dpw1 = new DungeonPlayerWidget(1);
+ DungeonPlayerWidget dpw2 = new DungeonPlayerWidget(2);
+ DungeonPlayerWidget dpw3 = new DungeonPlayerWidget(3);
+ DungeonPlayerWidget dpw4 = new DungeonPlayerWidget(4);
+ DungeonPlayerWidget dpw5 = new DungeonPlayerWidget(5);
+
+ this.offCenterL(dpw1);
+ this.offCenterL(dpw2);
+ this.offCenterL(dpw3);
+ this.offCenterR(dpw4);
+ this.offCenterR(dpw5);
+ this.stackWidgetsH(dpw1, dpw2, dpw3);
+ this.stackWidgetsH(dpw4, dpw5);
+ this.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..5a9733cc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+
+
+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.text.Text;
+
+public class GuestPlayerScreen extends Screen{
+
+ public GuestPlayerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ IslandGuestsWidget igw = new IslandGuestsWidget();
+ IslandOwnersWidget iow = new IslandOwnersWidget();
+
+ 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..2a159ecc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java
@@ -0,0 +1,25 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+
+
+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.text.Text;
+
+public class HomePlayerScreen extends Screen {
+
+ public HomePlayerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ IslandSelfWidget isw = new IslandSelfWidget();
+ IslandGuestsWidget igw = new IslandGuestsWidget();
+
+ 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..5db01512
--- /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 me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.PlayerListWidget;
+
+import net.minecraft.text.Text;
+
+public class PlayerListScreen extends Screen {
+
+ public PlayerListScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ PlayerListWidget plw = new PlayerListWidget();
+
+ 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..ca767617
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java
@@ -0,0 +1,55 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.util;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+
+/**
+ * Stores convenient shorthands for common ItemStack definitions
+ */
+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/PlayerListMgr.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java
new file mode 100644
index 00000000..60915bc1
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java
@@ -0,0 +1,111 @@
+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 me.xmrvizzy.skyblocker.mixin.PlayerListHudAccessor;
+
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayNetworkHandler;
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.Text;
+
+/**
+ * This class may be used to get data from the player list.
+ * It doesn't get its data every frame, instead, a scheduler is used to
+ * update the data this class is holding periodically.
+ * The list is sorted like in the vanilla game.
+ */
+public class PlayerListMgr {
+
+ public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Regex");
+
+ private static List<PlayerListEntry> playerList;
+
+ public static void updateList() {
+
+ if (!Utils.isOnSkyblock()) {
+ return;
+ }
+
+ ClientPlayNetworkHandler cpnwh = MinecraftClient.getInstance().getNetworkHandler();
+
+ // check is needed, else game crash on server leave
+ if (cpnwh != null) {
+ playerList = cpnwh.getPlayerList()
+ .stream()
+ .sorted(PlayerListHudAccessor.getOrdering())
+ .toList();
+ }
+ }
+
+ /**
+ * Get the display name at some index of the player list and apply a pattern to
+ * it
+ *
+ * @return the matcher if p fully matches, else null
+ */
+ public static Matcher regexAt(int idx, Pattern p) {
+
+ String str = PlayerListMgr.strAt(idx);
+
+ if (str == null) {
+ return null;
+ }
+
+ Matcher m = p.matcher(str);
+ if (!m.matches()) {
+ LOGGER.error("no match: \"{}\" against \"{}\"", str, p);
+ return null;
+ } else {
+ return m;
+ }
+ }
+
+ /**
+ * Get the display name at some index of the player list as string
+ *
+ * @return the string or null, if the display name is null, empty or whitespace
+ * only
+ */
+ public static String strAt(int idx) {
+
+ if (playerList == null) {
+ return null;
+ }
+
+ if (playerList.size() <= idx) {
+ return null;
+ }
+
+ Text txt = playerList.get(idx).getDisplayName();
+ if (txt == null) {
+ return null;
+ }
+ String str = txt.getString().trim();
+ if (str.length() == 0) {
+ return null;
+ }
+ return str;
+ }
+
+ /**
+ * Get the display name at some index of the player list as Text as seen in the
+ * game
+ *
+ * @return the PlayerListEntry at that index
+ */
+ public static PlayerListEntry getRaw(int idx) {
+ return playerList.get(idx);
+ }
+
+ public static int getSize() {
+ return playerList.size();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java
new file mode 100644
index 00000000..d7abdfc2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java
@@ -0,0 +1,89 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.util;
+
+import me.xmrvizzy.skyblocker.utils.Utils;
+
+/**
+ * Uses data from the player list to determine the area the player is in.
+ */
+public class PlayerLocator {
+
+ public static enum Location {
+ 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,
+ INSTANCED,
+ UNKNOWN
+ }
+
+ public static Location getPlayerLocation() {
+
+ if (!Utils.isOnSkyblock()) {
+ return Location.UNKNOWN;
+ }
+
+ String areaDesciptor = PlayerListMgr.strAt(41);
+
+ if (areaDesciptor == null || areaDesciptor.length() < 6) {
+ return Location.UNKNOWN;
+ }
+
+ if (areaDesciptor.startsWith("Dungeon")) {
+ return Location.DUNGEON;
+ }
+
+ switch (areaDesciptor.substring(6)) {
+ case "Private Island":
+ String islandType = PlayerListMgr.strAt(44);
+ if (islandType == null) {
+ return Location.UNKNOWN;
+ } else if (islandType.endsWith("Guest")) {
+ return Location.GUEST_ISLAND;
+ } else {
+ return Location.HOME_ISLAND;
+ }
+ case "Crimson Isle":
+ return Location.CRIMSON_ISLE;
+ case "Dungeon Hub":
+ return Location.DUNGEON_HUB;
+ case "The Farming Islands":
+ return Location.FARMING_ISLAND;
+ case "The Park":
+ return Location.PARK;
+ case "Dwarven Mines":
+ return Location.DWARVEN_MINES;
+ case "Crystal Hollows":
+ return Location.CRYSTAL_HOLLOWS;
+ case "The End":
+ return Location.END;
+ case "Gold Mine":
+ return Location.GOLD_MINE;
+ case "Deep Caverns":
+ return Location.DEEP_CAVERNS;
+ case "Hub":
+ return Location.HUB;
+ case "Spider's Den":
+ return Location.SPIDER_DEN;
+ case "Jerry's Workshop":
+ return Location.JERRY;
+ case "Garden":
+ return Location.GARDEN;
+ case "Instanced":
+ return Location.INSTANCED;
+ default:
+ return Location.UNKNOWN;
+ }
+ }
+}
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..de90cf30
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java
@@ -0,0 +1,91 @@
+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.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+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.
+// (dwarven mines and crystal hollows)
+
+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("(?<name>.*): (?<progress>.*)%?");
+
+ public CommsWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ for (int i = 50; i <= 53; i++) {
+ Matcher m = PlayerListMgr.regexAt(i, COMM_PATTERN);
+ // end of comms found?
+ if (m == null) {
+ if (i == 50) {
+ this.addComponent(new IcoTextComponent());
+ }
+ break;
+ }
+
+ ProgressComponent pc;
+
+ String name = m.group("name");
+ String progress = m.group("progress");
+
+ if (progress.equals("DONE")) {
+ pc = new ProgressComponent(Ico.BOOK, Text.of(name), Text.of(progress), 100f, pcntToCol(100));
+ } else {
+ float pcnt = Float.parseFloat(progress.substring(0, progress.length() - 1));
+ pc = new ProgressComponent(Ico.BOOK, Text.of(name), 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..5922fcbc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+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() {
+ super(TITLE, Formatting.GREEN.getColorValue());
+
+ this.addSimpleIcoText(Ico.SAPLING, "Organic Matter:", Formatting.YELLOW, 48);
+ this.addSimpleIcoText(Ico.FURNACE, "Fuel:", Formatting.BLUE, 49);
+ this.addSimpleIcoText(Ico.CLOCK, "Time Left:", Formatting.RED, 50);
+ this.addSimpleIcoText(Ico.COMPOSTER, "Stored Compost:", Formatting.DARK_GREEN, 51);
+
+ 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..48cb90bd
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java
@@ -0,0 +1,48 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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
+
+public class CookieWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Cookie Info").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ private static final Pattern COOKIE_PATTERN = Pattern.compile(".*\\nCookie Buff\\n(?<buff>.*)\\n");
+
+ public CookieWidget(String footertext) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ if (footertext == null || !footertext.contains("Cookie Buff")) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ Matcher m = COOKIE_PATTERN.matcher(footertext);
+ if (!m.find() || m.group("buff") == null) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ String buff = m.group("buff");
+ if (buff.startsWith("Not")) {
+ this.addComponent(new IcoTextComponent(Ico.COOKIE, Text.of("Not active")));
+ } else {
+ Text cookie = Text.literal("Time Left: ").append(buff);
+ this.addComponent(new IcoTextComponent(Ico.COOKIE, cookie));
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
new file mode 100644
index 00000000..6ad5268e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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 a list of obtained dungeon buffs
+// TODO: could be more pretty, can't be arsed atm
+
+public class DungeonBuffWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Dungeon Buffs").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public DungeonBuffWidget(String footertext) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ if (footertext == null || !footertext.contains("Dungeon Buffs")) {
+ this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ String interesting = footertext.split("Dungeon Buffs")[1];
+ String[] lines = interesting.split("\n");
+
+ if (!lines[1].startsWith("Blessing")) {
+ this.addComponent(new PlainTextComponent(Text.literal("No buffs found!").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ for (int i = 1; i < lines.length; i++) {
+ if (lines[i].length() < 3) { // empty line is §s
+ break;
+ }
+ this.addComponent(new PlainTextComponent(Text.of(lines[i])));
+ }
+
+ 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..78e1aeae
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java
@@ -0,0 +1,45 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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.PlayerListMgr;
+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 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*: \\((?<deathnum>\\d+)\\).*");
+
+ public DungeonDeathWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ Matcher m = PlayerListMgr.regexAt(25, DEATH_PATTERN);
+ if (m == null) {
+ this.addComponent(new IcoTextComponent());
+ } else {
+ Formatting f = (m.group("deathnum").equals("0")) ? Formatting.GREEN : Formatting.RED;
+ Text d = Widget.simpleEntryText(m.group("deathnum"), "Deaths: ", f);
+ IcoTextComponent deaths = new IcoTextComponent(Ico.SKULL, d);
+ this.addComponent(deaths);
+ }
+
+ this.addSimpleIcoText(Ico.SWORD, "Damage Dealt:", Formatting.RED, 26);
+ this.addSimpleIcoText(Ico.POTION, "Healing Done:", Formatting.RED, 27);
+ this.addSimpleIcoText(Ico.NTAG, "Milestone:", Formatting.YELLOW, 28);
+
+ 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..9bb250f7
--- /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 me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+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... something?
+// related to downed people in dungeons, not sure what this is supposed to show
+
+public class DungeonDownedWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Downed").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public DungeonDownedWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ String down = PlayerListMgr.strAt(21);
+ if (down == null) {
+ this.addComponent(new IcoTextComponent());
+ } else {
+
+ Formatting format = Formatting.RED;
+ if (down.endsWith("NONE")) {
+ format = Formatting.GRAY;
+ }
+ int idx = down.indexOf(": ");
+ Text downed = (idx == -1) ? null
+ : Widget.simpleEntryText(down.substring(idx + 2), "Downed: ", format);
+ IcoTextComponent d = new IcoTextComponent(Ico.SKULL, downed);
+ this.addComponent(d);
+ }
+
+ this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GRAY, 22);
+ this.addSimpleIcoText(Ico.POTION, "Revive:", Formatting.GRAY, 23);
+ 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..c1f9e235
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java
@@ -0,0 +1,99 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+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.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+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
+
+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*\\] (?<name>.*) \\((?<class>\\S*) ?(?<level>[LXVI]*)\\)");
+
+ private static final HashMap<String, ItemStack> ICOS = new HashMap<>();
+ private static final ArrayList<String> MSGS = new ArrayList<>();
+ 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);
+
+ MSGS.add("PRESS A TO JOIN");
+ MSGS.add("Invite a friend!");
+ MSGS.add("But nobody came.");
+ MSGS.add("More is better!");
+ }
+
+ // title needs to be changeable here
+ public DungeonPlayerWidget(int player) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ int start = 1 + (player - 1) * 4;
+
+ if (PlayerListMgr.strAt(start) == null) {
+ int idx = player - 2;
+ IcoTextComponent noplayer = new IcoTextComponent(Ico.SIGN,
+ Text.literal(MSGS.get(idx)).formatted(Formatting.GRAY));
+ this.addComponent(noplayer);
+ this.pack();
+ return;
+ }
+ Matcher m = PlayerListMgr.regexAt(start, PLAYER_PATTERN);
+ if (m == null) {
+ this.addComponent(new IcoTextComponent());
+ this.addComponent(new IcoTextComponent());
+ } else {
+
+ Text name = Text.literal("Name: ").append(Text.literal(m.group("name")).formatted(Formatting.YELLOW));
+ this.addComponent(new IcoTextComponent(Ico.PLAYER, name));
+
+ String cl = m.group("class");
+ String level = m.group("level");
+
+ if (level == null) {
+ PlainTextComponent ptc = new PlainTextComponent(
+ Text.literal("Player is dead").formatted(Formatting.RED));
+ this.addComponent(ptc);
+ } else {
+
+ Formatting clf = Formatting.GRAY;
+ ItemStack cli = Ico.BARRIER;
+ if (!cl.equals("EMPTY")) {
+ cli = ICOS.get(cl);
+ clf = Formatting.LIGHT_PURPLE;
+ cl += " " + m.group("level");
+ }
+
+ Text clazz = Text.literal("Class: ").append(Text.literal(cl).formatted(clf));
+ IcoTextComponent itclass = new IcoTextComponent(cli, clazz);
+ this.addComponent(itclass);
+ }
+ }
+
+ this.addSimpleIcoText(Ico.CLOCK, "Ult Cooldown:", Formatting.GOLD, start + 1);
+ this.addSimpleIcoText(Ico.POTION, "Revives:", Formatting.DARK_PURPLE, start + 2);
+
+ 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..2529e876
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java
@@ -0,0 +1,55 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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.PlayerListMgr;
+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 all puzzeles in the dungeon (name and status)
+
+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
+ // " ?.*" to diescard the solver's name if present
+ // the teleport maze has a trailing whitespace that messes with the regex
+ private static final Pattern PUZZLE_PATTERN = Pattern.compile("(?<name>.*): \\[(?<status>.*)\\] ?.*");
+
+ public DungeonPuzzleWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ int pos = 48;
+
+ while (pos < 60) {
+ Matcher m = PlayerListMgr.regexAt(pos, PUZZLE_PATTERN);
+ if (m == null) {
+ break;
+ }
+ Text t = Text.literal(m.group("name") + ": ")
+ .append(Text.literal("[").formatted(Formatting.GRAY))
+ .append(m.group("status"))
+ .append(Text.literal("]").formatted(Formatting.GRAY));
+ IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, t);
+ this.addComponent(itc);
+ pos++;
+ // code points for puzzle status chars unsolved and solved: 10022, 10004
+ // not sure which one is which
+ // still need to find out codepoint for the puzzle failed char
+ }
+ if (pos == 48) {
+ this.addComponent(
+ new IcoTextComponent(Ico.BARRIER, Text.literal("No puzzles!").formatted(Formatting.GRAY)));
+ }
+ 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..93eb69de
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the 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() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31);
+ this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32);
+
+ 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..81b8f907
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java
@@ -0,0 +1,47 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+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: (?<secnum>.*)%");
+
+ public DungeonServerWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.AQUA, 41);
+ this.addSimpleIcoText(Ico.SIGN, "Rooms Visited:", Formatting.DARK_PURPLE, 42);
+ this.addSimpleIcoText(Ico.SIGN, "Rooms Completed:", Formatting.LIGHT_PURPLE, 43);
+
+ Matcher m = PlayerListMgr.regexAt(44, SECRET_PATTERN);
+ if (m == null) {
+ this.addComponent(new ProgressComponent());
+ } else {
+ ProgressComponent scp = new ProgressComponent(Ico.CHEST, Text.of("Secrets found:"),
+ Float.parseFloat(m.group("secnum")),
+ Formatting.DARK_PURPLE.getColorValue());
+ this.addComponent(scp);
+ }
+
+ this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GOLD, 45);
+
+ 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..cd39a25a
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java
@@ -0,0 +1,64 @@
+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.
+// the parsing is super suspect and should be replaced by some regexes sometime later
+
+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());
+
+ if (footertext == null || !footertext.contains("Active Effects")) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+
+ }
+
+ String[] lines = footertext.split("Active Effects")[1].split("\n");
+ if (lines.length < 2) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ if (lines[1].startsWith("No")) {
+ Text txt = Text.literal("No effects active").formatted(Formatting.GRAY);
+ this.addComponent(new IcoTextComponent(Ico.POTION, txt));
+ } else if (lines[1].contains("God")) {
+ String timeleft = lines[1].split("! ")[1];
+ Text godpot = Text.literal("God potion!").formatted(Formatting.RED);
+ Text txttleft = Text.literal(timeleft).formatted(Formatting.LIGHT_PURPLE);
+ IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, godpot, txttleft);
+ this.addComponent(iftc);
+ } else {
+ String number = lines[1].substring("You have ".length());
+ int idx = number.indexOf(' ');
+ if (idx == -1 || lines.length < 4) {
+ this.addComponent(new IcoFatTextComponent());
+ this.pack();
+ return;
+ }
+ number = number.substring(0, idx);
+ 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..ed07982c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java
@@ -0,0 +1,103 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+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.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+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("(?<mayor>\\S*): \\|+ \\((?<pcnt>\\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() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ String status = PlayerListMgr.strAt(76);
+ if (status == null) {
+ this.addComponent(new IcoTextComponent());
+ this.addComponent(new IcoTextComponent());
+ this.addComponent(new IcoTextComponent());
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ if (status.contains("Over!")) {
+ // election is over
+ IcoTextComponent over = new IcoTextComponent(Ico.BARRIER, EL_OVER);
+ this.addComponent(over);
+
+ String win = PlayerListMgr.strAt(77);
+ if (win == null || !win.contains(": ")) {
+ this.addComponent(new IcoTextComponent());
+ } else {
+ String winnername = win.split(": ")[1];
+ Text winnertext = Widget.simpleEntryText(winnername, "Winner: ", Formatting.GREEN);
+ IcoTextComponent winner = new IcoTextComponent(MAYOR_DATA.get(winnername), winnertext);
+ this.addComponent(winner);
+ }
+
+ this.addSimpleIcoText(Ico.PLAYER, "Participants:", Formatting.AQUA, 78);
+ this.addSimpleIcoText(Ico.SIGN, "Year:", Formatting.LIGHT_PURPLE, 79);
+
+ } else {
+ // election is going on
+ this.addSimpleIcoText(Ico.CLOCK, "End in:", Formatting.GOLD, 76);
+
+ for (int i = 77; i <= 79; i++) {
+ Matcher m = PlayerListMgr.regexAt(i, VOTE_PATTERN);
+ if (m == null) {
+ this.addComponent(new ProgressComponent());
+ } else {
+
+ String mayorname = m.group("mayor");
+ String pcntstr = m.group("pcnt");
+ float pcnt = Float.parseFloat(pcntstr);
+ Text candidate = Text.literal(mayorname).formatted(COLS[i - 77]);
+ ProgressComponent pc = new ProgressComponent(MAYOR_DATA.get(mayorname), 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..52d6cfbd
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+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() {
+ 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..fc0780e1
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java
@@ -0,0 +1,44 @@
+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.TableComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows your dungeon essences (dungeon hub only)
+
+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() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ wither = Widget.simpleEntryText(46, "Wither:", Formatting.DARK_PURPLE);
+ spider = Widget.simpleEntryText(47, "Spider:", Formatting.DARK_PURPLE);
+ undead = Widget.simpleEntryText(48, "Undead:", Formatting.DARK_PURPLE);
+ dragon = Widget.simpleEntryText(49, "Dragon:", Formatting.DARK_PURPLE);
+ gold = Widget.simpleEntryText(50, "Gold:", Formatting.DARK_PURPLE);
+ diamond = Widget.simpleEntryText(51, "Diamond:", Formatting.DARK_PURPLE);
+ ice = Widget.simpleEntryText(52, "Ice:", Formatting.DARK_PURPLE);
+ crimson = Widget.simpleEntryText(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..1b46e621
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java
@@ -0,0 +1,30 @@
+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 ongoing events (e.g. election)
+
+public class EventWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Event Info").formatted(Formatting.YELLOW, Formatting.BOLD);
+
+ public EventWidget(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;
+
+ this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.YELLOW, 73 + offset);
+
+ // this could look better
+ Text time = Widget.plainEntryText(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..ddf51f32
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java
@@ -0,0 +1,68 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.Formatting;
+
+// 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 available
+ // group 3: # items available in total (1 digit + "k")
+ private static final Pattern FIRE_PATTERN = Pattern.compile("(?<item>.*): (?<avail>\\d*)/(?<total>[0-9.]*)k");
+
+ public FireSaleWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ String event = PlayerListMgr.strAt(46);
+
+ if (event == null) {
+ this.addComponent(new PlainTextComponent(Text.literal("No Fire Sale!").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ if (event.contains("Starts In")) {
+ this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.DARK_AQUA, 46);
+ this.pack();
+ return;
+ }
+
+ for (int i = 46;; i++) {
+ Matcher m = PlayerListMgr.regexAt( i, FIRE_PATTERN);
+ if (m == null) {
+ break;
+ }
+ String avail = m.group("avail");
+ Text itemTxt = Text.literal(m.group("item"));
+ float total = Float.parseFloat(m.group("total")) * 1000;
+ Text prgressTxt = Text.literal(String.format("%s/%.0f", avail, total));
+ float pcnt = (Float.parseFloat(avail) / (total)) * 100f;
+ ProgressComponent pc = new ProgressComponent(Ico.GOLD, itemTxt, prgressTxt, pcnt, pcntToCol(pcnt));
+ this.addComponent(pc);
+ }
+ this.pack();
+
+ }
+
+ private int pcntToCol(float pcnt) {
+ return MathHelper.hsvToRgb( pcnt / 300f, 0.9f, 0.9f);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
new file mode 100644
index 00000000..da1ba6c5
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
@@ -0,0 +1,79 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+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.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() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ int forgestart = 54;
+ // why is it forges and not looms >:(
+ String pos = PlayerListMgr.strAt(53);
+ if (pos == null) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ if (!pos.startsWith("Forges")) {
+ forgestart += 2;
+ }
+
+ for (int i = forgestart, slot = 1; i < forgestart + 5 && i < 60; i++, slot++) {
+ String fstr = PlayerListMgr.strAt(i);
+ if (fstr == null || fstr.length() < 3) {
+ if (i == forgestart) {
+ this.addComponent(new IcoTextComponent());
+ }
+ break;
+ }
+ Component c;
+ Text l1, l2;
+
+ switch (fstr.substring(3)) {
+ case "LOCKED":
+ l1 = Text.literal("Locked").formatted(Formatting.RED);
+ l2 = switch (slot) {
+ case 3 -> Text.literal("Needs HotM 3").formatted(Formatting.GRAY);
+ case 4 -> Text.literal("Needs HotM 4").formatted(Formatting.GRAY);
+ case 5 -> Text.literal("Needs 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(": ");
+ if (parts.length != 2) {
+ c = new IcoFatTextComponent();
+ } else {
+ l1 = Text.literal(parts[0].substring(3)).formatted(Formatting.YELLOW);
+ l2 = Text.literal("Done in: ").formatted(Formatting.GRAY).append(Text.literal(parts[1]).formatted(Formatting.WHITE));
+ 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..b0fc160f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java
@@ -0,0 +1,53 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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.PlayerListMgr;
+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 the garden server
+
+public class GardenServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ // match the next visitor in the garden
+ // group 1: visitor name
+ private static final Pattern VISITOR_PATTERN = Pattern.compile("Next Visitor: (?<vis>.*)");
+
+ public GardenServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43);
+ this.addSimpleIcoText(Ico.COPPER, "Copper:", Formatting.GOLD, 44);
+
+ Matcher m = PlayerListMgr.regexAt(45, VISITOR_PATTERN);
+ if (m == null ) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ String vis = m.group("vis");
+ Formatting col;
+ if (vis.equals("Not Unlocked!")) {
+ col = Formatting.RED;
+ } else {
+ col = Formatting.GREEN;
+ }
+ Text visitor = Widget.simpleEntryText(vis, "Next Visitor: ", col);
+ IcoTextComponent v = new IcoTextComponent(Ico.PLAYER, visitor);
+ this.addComponent(v);
+ 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..26e29ce2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java
@@ -0,0 +1,78 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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.PlayerListMgr;
+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.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about your skills while in the garden
+
+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*: (?<skill>[A-Za-z]* [0-9]*): (?<progress>\\S*)%");
+ // same, but with leading space
+ private static final Pattern MS_PATTERN = Pattern.compile("\\S*: (?<skill>[A-Za-z]* [0-9]*): (?<progress>\\S*)%");
+
+ public GardenSkillsWidget() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ ProgressComponent pc;
+ Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN);
+ if (m == null) {
+ pc = new ProgressComponent();
+ } else {
+
+ String strpcnt = m.group("progress");
+ String skill = m.group("skill");
+
+ float pcnt = Float.parseFloat(strpcnt);
+ pc = new ProgressComponent(Ico.LANTERN, Text.of(skill), pcnt,
+ Formatting.GOLD.getColorValue());
+ }
+
+ this.addComponent(pc);
+
+ Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE);
+ IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed);
+ Text farmfort = Widget.simpleEntryText(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);
+
+ ProgressComponent pc2;
+ m = PlayerListMgr.regexAt(69, MS_PATTERN);
+ if (m == null) {
+ pc2 = new ProgressComponent();
+ } else {
+ String strpcnt = m.group("progress");
+ String skill = m.group("skill");
+
+ float pcnt = Float.parseFloat(strpcnt);
+ 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..cb208e92
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+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() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.SIGN, "Owner:", Formatting.GREEN, 43);
+ this.addSimpleIcoText(Ico.SIGN, "Status:", Formatting.BLUE, 44);
+
+ 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..e0f5f1a3
--- /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.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+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 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() {
+ super(TITLE, Formatting.AQUA.getColorValue());
+ for (int i = 21; i < 40; i++) {
+ String str = PlayerListMgr.strAt(i);
+ if (str == null) {
+ if (i == 21) {
+ this.addComponent(new PlainTextComponent(Text.literal("No Visitors!").formatted(Formatting.GRAY)));
+ }
+ break;
+ }
+ Matcher m = PlayerListMgr.regexAt( i, GUEST_PATTERN);
+ if (m == null) {
+ this.addComponent(new PlainTextComponent(Text.of("???")));
+ } else {
+ this.addComponent(new PlainTextComponent(Text.of(m.group(1))));
+ }
+ }
+ 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..6c2d6b47
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
@@ -0,0 +1,60 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+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 a list of the owners of a home island while guesting
+
+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
+ // group 2: last seen, if owner not online
+ private static final Pattern OWNER_PATTERN = Pattern
+ .compile("^(?<nameA>.*) \\((?<lastseen>.*)\\)$|^\\[\\d*\\] (?<nameB>.*)$|^(?<nameC>.*)$");
+
+ public IslandOwnersWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ for (int i = 1; i < 20; i++) {
+ Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN);
+ if (m == null) {
+ break;
+ }
+
+ String name = null, lastseen = null;
+ Formatting format = null;
+ if (m.group("nameA") != null) {
+ name = m.group("nameA");
+ lastseen = m.group("lastseen");
+ format = Formatting.GRAY;
+ } else if (m.group("nameB")!=null){
+ name = m.group("nameB");
+ lastseen = "Online";
+ format = Formatting.WHITE;
+ } else {
+ name = m.group("nameC");
+ lastseen = "Online";
+ format = Formatting.WHITE;
+ }
+
+ Text entry = Text.literal(name)
+ .append(
+ Text.literal(" (" + lastseen + ")")
+ .formatted(format));
+ 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..4324dad9
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java
@@ -0,0 +1,37 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+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 a list of the owners while on 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, optionally offline time
+ private static final Pattern OWNER_PATTERN = Pattern.compile("^\\[\\d*\\] (.*)$|^(.*)$");
+
+ public IslandSelfWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ for (int i = 1; i < 20; i++) {
+ Matcher m = PlayerListMgr.regexAt( i, OWNER_PATTERN);
+ if (m == null) {
+ break;
+ }
+ 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..2b02c514
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+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() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Crystals:", Formatting.DARK_PURPLE, 43);
+ this.addSimpleIcoText(Ico.CHEST, "Stash:", Formatting.GREEN, 44);
+ this.addSimpleIcoText(Ico.COMMAND, "Minions:", Formatting.BLUE, 45);
+
+ 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..8d49efaa
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java
@@ -0,0 +1,60 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+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 the current jacob's contest (garden only)
+
+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() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.GOLD, 76);
+
+ TableComponent tc = new TableComponent(1, 3, Formatting.YELLOW .getColorValue());
+
+ for (int i = 77; i < 80; i++) {
+ String item = PlayerListMgr.strAt(i);
+ IcoTextComponent itc;
+ if (item == null) {
+ itc = new IcoTextComponent();
+ } else {
+ itc = new IcoTextComponent(FARM_DATA.get(item), Text.of(item));
+ }
+ tc.addToCell(0, i - 77, itc);
+ }
+ 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..22082202
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java
@@ -0,0 +1,133 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+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("(?<name>.*) (?<level>[XVI]*) \\[(?<status>.*)\\]");
+
+ public MinionWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ for (int i = 48; i < 59; i++) {
+ Matcher m = PlayerListMgr.regexAt(i, MINION_PATTERN);
+ if (m != null) {
+
+ String min = m.group("name");
+ String lvl = m.group("level");
+ String stat = m.group("status");
+
+ 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
+ String more = PlayerListMgr.strAt(59);
+ if (more != null) {
+ this.addComponent(new PlainTextComponent(Text.of(more)));
+ }
+ 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..4148ed77
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+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() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43);
+ this.addSimpleIcoText(Ico.WATER, "Rain:", Formatting.BLUE, 44);
+
+ 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..2cd710eb
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java
@@ -0,0 +1,73 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+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.
+// responsible for non-private-island 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() {
+ super(TITLE, Formatting.GREEN.getColorValue());
+
+ // hard cap to 4x20 entries.
+ // 5x20 is too wide (and not possible in theory. in reality however...)
+ int listlen = Math.min(PlayerListMgr.getSize(), 160);
+
+ // list isn't fully loaded, so our hack won't work...
+ if (listlen < 80) {
+ this.addComponent(new PlainTextComponent(Text.literal("List loading...").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ // unintuitive int ceil division stolen from
+ // https://stackoverflow.com/questions/7139382/java-rounding-up-to-an-int-using-math-ceil#21830188
+ int tblW = ((listlen - 80) - 1) / 20 + 1;
+
+ TableComponent tc = new TableComponent(tblW, (listlen - 80 >= 20) ? 20 : listlen - 80,
+ Formatting.GREEN.getColorValue());
+
+ for (int i = 80; i < listlen; i++) {
+ list.add(PlayerListMgr.getRaw(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..459e3de2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+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
+// (dwarven mines and crystal hollows)
+
+public class PowderWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Powders").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public PowderWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MITHRIL, "Mithril:", Formatting.AQUA, 46);
+ this.addSimpleIcoText(Ico.EMERALD, "Gemstone:", Formatting.DARK_PURPLE, 47);
+
+ 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..a6d9e82d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java
@@ -0,0 +1,25 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+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() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61);
+ this.addSimpleIcoText(Ico.BONE, "Pet Sitter:", Formatting.AQUA, 62);
+ this.addSimpleIcoText(Ico.EMERALD, "Balance:", Formatting.GOLD, 63);
+ this.addSimpleIcoText(Ico.CLOCK, "Interest in:", Formatting.GOLD, 64);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java
new file mode 100644
index 00000000..5c89964e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java
@@ -0,0 +1,29 @@
+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 your crimson isle faction quests
+
+public class QuestWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Faction Quests").formatted(Formatting.AQUA,
+ Formatting.BOLD);
+
+ public QuestWidget() {
+ super(TITLE, Formatting.AQUA.getColorValue());
+
+ for (int i = 51; i < 56; i++) {
+ Text q = Widget.plainEntryText(i);
+ IcoTextComponent itc = new IcoTextComponent(Ico.BOOK, q);
+ this.addComponent(itc);
+ }
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java
new file mode 100644
index 00000000..c0379623
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java
@@ -0,0 +1,67 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows your faction status (crimson isle)
+
+public class ReputationWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Faction Status").formatted(Formatting.AQUA,
+ Formatting.BOLD);
+
+ // matches your faction alignment progress
+ // group 1: percentage to next alignment level
+ private static final Pattern PROGRESS_PATTERN = Pattern.compile("\\|+ \\((?<prog>[0-9.]*)%\\)");
+
+ // matches alignment level names
+ // group 1: left level name
+ // group 2: right level name
+ private static final Pattern STATE_PATTERN = Pattern.compile("(?<from>\\S*) *(?<to>\\S*)");
+
+ public ReputationWidget() {
+ super(TITLE, Formatting.AQUA.getColorValue());
+
+ String fracstr = PlayerListMgr.strAt(45);
+
+ int spaceidx;
+ IcoTextComponent faction;
+ if (fracstr == null || (spaceidx = fracstr.indexOf(' ')) == -1) {
+ faction = new IcoTextComponent();
+ } else {
+ String fname = fracstr.substring(0, spaceidx);
+ if (fname.equals("Mage")) {
+ faction = new IcoTextComponent(Ico.POTION, Text.literal(fname).formatted(Formatting.DARK_AQUA));
+ } else {
+ faction = new IcoTextComponent(Ico.SWORD, Text.literal(fname).formatted(Formatting.RED));
+ }
+ }
+ this.addComponent(faction);
+
+ Text rep = Widget.plainEntryText(46);
+ Matcher prog = PlayerListMgr.regexAt(47, PROGRESS_PATTERN);
+ Matcher state = PlayerListMgr.regexAt(48, STATE_PATTERN);
+
+ if (prog == null || state == null) {
+ this.addComponent(new ProgressComponent());
+ } else {
+ float pcnt = Float.parseFloat(prog.group("prog"));
+ ProgressComponent pc = new ProgressComponent(Ico.LANTERN,
+ Text.of(state.group("from") + " -> " + state.group("to")), rep, pcnt,
+ Formatting.AQUA.getColorValue());
+ this.addComponent(pc);
+ }
+
+ 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..2d8d1c63
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+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 third column of the tab HUD
+
+public class ServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public ServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43);
+ 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..88ba8022
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java
@@ -0,0 +1,75 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+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.PlayerListMgr;
+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 me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+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,
+// as seen in the rightmost column of the default HUD
+
+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]*): ([0-9.MAX]*)%?");
+
+ public SkillsWidget() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN);
+ Component progress;
+ if (m == null) {
+ progress = new ProgressComponent();
+ } else {
+
+ String skill = m.group(1);
+ String pcntStr = m.group(2);
+
+ if (!pcntStr.equals("MAX")) {
+ float pcnt = Float.parseFloat(pcntStr);
+ progress = new ProgressComponent(Ico.LANTERN, Text.of(skill),
+ Text.of(pcntStr + "%"), pcnt, Formatting.GOLD.getColorValue());
+ } else {
+ progress = new IcoFatTextComponent(Ico.LANTERN, Text.of(skill),
+ Text.literal(pcntStr).formatted(Formatting.RED));
+ }
+ }
+
+ this.addComponent(progress);
+
+ Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE);
+ IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed);
+ Text strength = Widget.simpleEntryText(68, "STR", Formatting.RED);
+ IcoTextComponent str = new IcoTextComponent(Ico.SWORD, strength);
+ Text critDmg = Widget.simpleEntryText(69, "CCH", Formatting.BLUE);
+ IcoTextComponent cdg = new IcoTextComponent(Ico.SWORD, critDmg);
+ Text critCh = Widget.simpleEntryText(70, "CDG", Formatting.BLUE);
+ IcoTextComponent cch = new IcoTextComponent(Ico.SWORD, critCh);
+ Text aSpeed = Widget.simpleEntryText(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..d47849c3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java
@@ -0,0 +1,22 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+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)
+
+public class TrapperWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Trapper").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public TrapperWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.LEATHER, "Pelts:", Formatting.AQUA, 46);
+ 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..ef7c21d0
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java
@@ -0,0 +1,47 @@
+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 ongoing 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());
+ if (footertext == null) {
+ this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ if (!footertext.contains("Upgrades")) {
+ this.addComponent(new PlainTextComponent(Text.of("Currently no upgrades...")));
+ this.pack();
+ return;
+ }
+
+ String interesting = footertext.split("Upgrades")[1];
+ String[] lines = interesting.split("\n");
+
+ for (int i = 1; i < lines.length; i++) {
+ if (lines[i].trim().length() < 3) { // empty line is §s
+ break;
+ }
+ IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, Text.of(lines[i]));
+ this.addComponent(itc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java
new file mode 100644
index 00000000..ec6a35fb
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java
@@ -0,0 +1,57 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+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;
+import net.minecraft.util.Pair;
+
+// shows the volcano status (crimson isle)
+
+public class VolcanoWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Volcano Status").formatted(Formatting.AQUA,
+ Formatting.BOLD);
+
+ private static final HashMap<String, Pair<ItemStack, Formatting>> BOOM_TYPE = new HashMap<>();
+
+ static {
+ BOOM_TYPE.put("INACTIVE",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.BARRIER), Formatting.DARK_GRAY));
+ BOOM_TYPE.put("CHILL",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.ICE), Formatting.AQUA));
+ BOOM_TYPE.put("LOW",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.FLINT_AND_STEEL), Formatting.GRAY));
+ BOOM_TYPE.put("DISRUPTIVE",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.CAMPFIRE), Formatting.WHITE));
+ BOOM_TYPE.put("MEDIUM",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.LAVA_BUCKET), Formatting.YELLOW));
+ BOOM_TYPE.put("HIGH",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.FIRE_CHARGE), Formatting.GOLD));
+ BOOM_TYPE.put("EXPLOSIVE",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.TNT), Formatting.RED));
+ BOOM_TYPE.put("CATACLYSMIC",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.SKELETON_SKULL), Formatting.DARK_RED));
+ }
+
+ public VolcanoWidget() {
+ super(TITLE, Formatting.AQUA.getColorValue());
+
+ String s = PlayerListMgr.strAt(58);
+ if (s == null) {
+ this.addComponent(new IcoTextComponent());
+ } else {
+ Pair<ItemStack, Formatting> p = BOOM_TYPE.get(s);
+ this.addComponent(new IcoTextComponent(p.getLeft(), Text.literal(s).formatted(p.getRight())));
+ }
+
+ 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..6b96c151
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java
@@ -0,0 +1,202 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.ArrayList;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+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.item.ItemStack;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Abstract base class for a Widget.
+ * Widgets are containers for components with a border and a title.
+ * Their size is dependent on the components inside,
+ * the position may be changed after construction.
+ */
+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);
+ }
+
+ /**
+ * Shorthand function for simple components.
+ * If the entry at idx has the format "<textA>: <textB>", an IcoTextComponent is added as such:
+ * [ico] [string] [textB.formatted(fmt)]
+ */
+ public final void addSimpleIcoText(ItemStack ico, String string, Formatting fmt, int idx) {
+ Text txt = Widget.simpleEntryText(idx, string, fmt);
+ this.addComponent(new IcoTextComponent(ico, txt));
+ }
+
+ /**
+ * Calculate the size of this widget.
+ * <b>Must be called before returning from the widget constructor and after all components are added!</b>
+ */
+ public final void pack() {
+ for (Component c : components) {
+ h += c.getHeight() + Component.PAD_L;
+ w = Math.max(w, c.getWidth() + Component.PAD_S);
+ }
+
+ h -= Component.PAD_L / 2; // less padding after lowest/last component
+ 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;
+ }
+
+ /**
+ * Draw this widget with a background
+ */
+ public final void render(MatrixStack ms) {
+ this.render(ms, true);
+ }
+
+ /**
+ * Draw this widget, possibly with a background
+ */
+ public final void render(MatrixStack ms, boolean hasBG) {
+
+ // not sure if this is the way to go, but it fixes Z-layer issues
+ // like blocks being rendered behind the BG and the hotbar clipping into things
+ RenderSystem.enableDepthTest();
+ ms.push();
+
+ float scale = SkyblockerConfig.get().general.tabHud.tabHudScale / 100f;
+ ms.scale(scale, scale, 1);
+
+ // move above other UI elements
+ ms.translate(0, 0, 200);
+ 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);
+ }
+ // move above background (if exists)
+ ms.translate(0, 0, 100);
+
+ int strHeightHalf = Widget.txtRend.fontHeight / 2;
+ int strAreaWidth = Widget.txtRend.getWidth(title) + 4;
+
+ txtRend.draw(ms, title, x + 8, y + 2, this.color);
+
+ 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() + Component.PAD_L;
+ }
+ // pop manipulations above
+ ms.pop();
+ RenderSystem.disableDepthTest();
+ }
+
+ 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);
+ }
+
+ /**
+ * If the entry at idx has the format "[textA]: [textB]", the following is returned:
+ * [entryName] [textB.formatted(contentFmt)]
+ */
+ public static Text simpleEntryText(int idx, String entryName, Formatting contentFmt) {
+
+ String src = PlayerListMgr.strAt(idx);
+
+ if (src == null) {
+ return null;
+ }
+
+ int cidx = src.indexOf(':');
+ if (cidx == -1) {
+ return null;
+ }
+
+ src = src.substring(src.indexOf(':') + 1);
+ return Widget.simpleEntryText(src, entryName, contentFmt);
+ }
+
+ /**
+ * @return [entryName] [entryContent.formatted(contentFmt)]
+ */
+ public static Text simpleEntryText(String entryContent, String entryName, Formatting contentFmt) {
+ return Text.literal(entryName).append(Text.literal(entryContent).formatted(contentFmt));
+ }
+
+ /**
+ * @return the entry at idx as unformatted Text
+ */
+ public static Text plainEntryText(int idx) {
+ String str = PlayerListMgr.strAt(idx);
+ if (str == null) {
+ return null;
+ }
+ return Text.of(str);
+ }
+}
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..671b1f41
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java
@@ -0,0 +1,33 @@
+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;
+
+/**
+ * Abstract base class for a component that may be added to a Widget.
+ */
+public abstract class Component {
+
+ static final int ICO_DIM = 16;
+ public static final int PAD_S = 2;
+ public static final int PAD_L = 4;
+
+ static TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
+ static ItemRenderer itmRend = MinecraftClient.getInstance().getItemRenderer();
+
+ // these should always be the content dimensions without any padding.
+ 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..f845eba5
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java
@@ -0,0 +1,45 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Component that consists of an icon and two lines of text
+ */
+public class IcoFatTextComponent extends Component {
+
+ private static final int ICO_OFFS = 1;
+
+ private ItemStack ico;
+ private Text line1, line2;
+
+ public IcoFatTextComponent(ItemStack ico, Text l1, Text l2) {
+ this.ico = (ico == null) ? Ico.BARRIER : ico;
+ this.line1 = l1;
+ this.line2 = l2;
+
+ if (l1 == null || l2 == null) {
+ this.ico = Ico.BARRIER;
+ this.line1 = Text.literal("No data").formatted(Formatting.GRAY);
+ this.line2 = Text.literal("No data").formatted(Formatting.GRAY);
+ }
+
+ this.width = ICO_DIM + PAD_L + Math.max(txtRend.getWidth(this.line1), txtRend.getWidth(this.line2));
+ this.height = txtRend.fontHeight + PAD_S + txtRend.fontHeight;
+ }
+
+ public IcoFatTextComponent() {
+ this(null, null, null);
+ }
+
+ @Override
+ public void render(MatrixStack ms, int x, int y) {
+ itmRend.renderGuiItemIcon(ms, ico, x, y + ICO_OFFS);
+ txtRend.draw(ms, line1, x + ICO_DIM + PAD_L, y, 0xffffffff);
+ txtRend.draw(ms, line2, 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..7a495a13
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Component that consists of an icon and a line of text.
+ */
+public class IcoTextComponent extends Component {
+
+ private ItemStack ico;
+ private Text text;
+
+ public IcoTextComponent(ItemStack ico, Text txt) {
+ this.ico = (ico == null) ? Ico.BARRIER : ico;
+ this.text = txt;
+
+ if (txt == null) {
+ this.ico = Ico.BARRIER;
+ this.text = Text.literal("No data").formatted(Formatting.GRAY);
+ }
+
+ this.width = ICO_DIM + PAD_L + txtRend.getWidth(this.text);
+ this.height = ICO_DIM;
+ }
+
+ public IcoTextComponent() {
+ this(null, null);
+ }
+
+ @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..265d11f1
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Component that consists of a line of text.
+ */
+public class PlainTextComponent extends Component {
+
+ private Text text;
+
+ public PlainTextComponent(Text txt) {
+ this.text = txt;
+
+ if (txt == null) {
+ this.text = Text.literal("No data").formatted(Formatting.GRAY);
+ }
+
+ this.width = PAD_S + txtRend.getWidth(this.text); // looks off without padding
+ 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..18859080
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java
@@ -0,0 +1,36 @@
+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;
+
+/**
+ * Component that consists of a player's skin icon and their name
+ */
+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 = SKIN_ICO_DIM + PAD_S + txtRend.getWidth(name);
+ 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..b9ebc0e9
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java
@@ -0,0 +1,70 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+
+/**
+ * Component that consists of an icon, some text and a progress bar.
+ * The progress bar either shows the fill percentage or custom text.
+ * NOTICE: pcnt is 0-100, not 0-1!
+ */
+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 d, Text b, float pcnt, int color) {
+ this.ico = (ico == null) ? Ico.BARRIER : ico;
+ this.desc = d;
+ this.bar = b;
+ this.color = 0xff000000 | color;
+ this.pcnt = pcnt;
+
+ if (d == null || b == null) {
+ this.ico = Ico.BARRIER;
+ this.desc = Text.literal("No data").formatted(Formatting.GRAY);
+ this.bar = Text.literal("---").formatted(Formatting.GRAY);
+ this.pcnt = 100f;
+ this.color = 0xff000000 | Formatting.DARK_GRAY.getColorValue();
+ }
+
+ this.barW = BAR_WIDTH;
+ this.width = ICO_DIM + PAD_L + Math.max(this.barW, txtRend.getWidth(this.desc));
+ this.height = txtRend.fontHeight + PAD_S + 2 + txtRend.fontHeight + 2;
+ }
+
+ public ProgressComponent(ItemStack ico, Text text, float pcnt, int color) {
+ this(ico, text, Text.of(pcnt + "%"), pcnt, color);
+ }
+
+ public ProgressComponent() {
+ this(null, null, null, 100, 0);
+ }
+
+ @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;
+ int endOffsX = ((int) (this.barW * (this.pcnt / 100f)));
+ DrawableHelper.fill(ms, barX + endOffsX, barY, barX + this.barW, barY + BAR_HEIGHT, COL_BG_BAR);
+ DrawableHelper.fill(ms, barX, barY, barX + endOffsX, 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..5d49be2c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java
@@ -0,0 +1,59 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.util.math.MatrixStack;
+
+/**
+ * Meta-Component that consists of a grid of other components
+ * Grid cols are separated by lines.
+ */
+public class TableComponent extends Component {
+
+ private Component[][] comps;
+ private int color;
+ private int cols, rows;
+ private int cellW, cellH;
+
+ public TableComponent(int w, int h, int col) {
+ comps = new Component[w][h];
+ color = 0xff000000 | col;
+ cols = w;
+ rows = h;
+ }
+
+ public void addToCell(int x, int y, Component c) {
+ this.comps[x][y] = c;
+
+ // pad extra to add a vertical line later
+ this.cellW = Math.max(this.cellW, c.width + PAD_S + PAD_L);
+
+ // 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
+ this.cellH = c.height + PAD_S;
+
+ this.width = this.cellW * this.cols;
+ this.height = (this.cellH * this.rows) - PAD_S / 2;
+
+ }
+
+ @Override
+ public void render(MatrixStack ms, int xpos, int ypos) {
+ for (int x = 0; x < cols; x++) {
+ for (int y = 0; y < rows; y++) {
+ if (comps[x][y] != null) {
+ comps[x][y].render(ms, xpos + (x * cellW), ypos + y * cellH);
+ }
+ }
+ // add a line before the col if we're not drawing the first one
+ if (x != 0) {
+ int lineX1 = xpos + (x * cellW) - PAD_S - 1;
+ int lineX2 = xpos + (x * cellW) - PAD_S;
+ int lineY1 = ypos + 1;
+ int lineY2 = ypos + this.height - PAD_S - 1; // not sure why but it looks correct
+ DrawableHelper.fill(ms, lineX1, lineY1, lineX2, lineY2, this.color);
+ }
+ }
+ }
+
+}
diff --git a/src/main/resources/assets/skyblocker/lang/de_de.json b/src/main/resources/assets/skyblocker/lang/de_de.json
index 56f0fb14..4a84719c 100644
--- a/src/main/resources/assets/skyblocker/lang/de_de.json
+++ b/src/main/resources/assets/skyblocker/lang/de_de.json
@@ -4,6 +4,10 @@
"text.autoconfig.skyblocker.title": "Skyblocker-Einstellungen",
"text.autoconfig.skyblocker.category.general": "Allgemein",
"text.autoconfig.skyblocker.option.general.bars": "Gesundheits-, Mana-, Verteidigungs- und XP-Balken",
+ "text.autoconfig.skyblocker.option.general.tabHud": "Schöneres Tab-HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled": "Schöneres Tab-HUD aktivieren",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale": "Skalierungsfaktor für schöners Tab-HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "Wert in %, relativ zur Skalierung des Vanilla-GUIs",
"text.autoconfig.skyblocker.option.general.bars.enableBars": "Balken aktivieren",
"text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "Leere Item-Tooltips in Menüs verstecken",
@@ -29,6 +33,9 @@
"text.autoconfig.skyblocker.option.messages.hideMoltenWave": "Nachricht über Molten Wave ausblenden",
"text.autoconfig.skyblocker.option.messages.hideAds": "Werbung im öffentlichen Chat ausblenden",
"key.wikiLookup": "Wiki-Abfrage",
+ "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.option.general.bars.barpositions.RIGHT": "Rechts",
"text.autoconfig.skyblocker.option.general.quicknav": "Schnellnavigation",
"text.autoconfig.skyblocker.option.general.quicknav.enableQuicknav": "Schnellnavigation Einschalten"
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index 17b9cddd..dd6d777c 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",
@@ -22,6 +25,10 @@
"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.tabHud": "Fancy tab HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled": "Enable fancy tab HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale": "Scale factor of fancy tab HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "Value in %, relative to your vanilla GUI scale",
"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",
@@ -178,6 +185,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 1744b5ba..6fa2596a 100644
--- a/src/main/resources/skyblocker.mixins.json
+++ b/src/main/resources/skyblocker.mixins.json
@@ -16,6 +16,9 @@
"LeverBlockMixin",
"MinecraftClientMixin",
"RecipeBookWidgetAccessor",
+ "HandledScreenAccessor",
+ "PlayerListHudAccessor",
+ "PlayerListHudMixin",
"ScreenMixin"
],
"injectors": {