aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/me
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/me')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java14
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java75
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java14
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java18
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java139
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java51
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java136
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java275
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java427
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java461
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java132
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java64
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java17
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java7
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java17
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java181
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java144
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java83
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java153
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java14
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java94
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java115
-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.java230
-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/genericInfo/GenericRiftInfoScreen.java38
-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.java30
-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/main/RiftScreen.java28
-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/PlayerListMgr.java13
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java120
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/ScreenConst.java13
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java34
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java10
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java12
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java8
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java13
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ErrorWidget.java (renamed from src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java)20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java7
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java37
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java11
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java12
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java10
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java12
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java43
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java3
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java73
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java7
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java12
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java104
106 files changed, 3171 insertions, 1230 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
index c30dab62..8033e4a2 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
@@ -7,17 +7,16 @@ import me.xmrvizzy.skyblocker.skyblock.*;
import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonBlaze;
import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap;
import me.xmrvizzy.skyblocker.skyblock.dungeon.LividColor;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.TicTacToe;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets;
import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud;
-import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorDyeColors;
-import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorTrims;
-import me.xmrvizzy.skyblocker.skyblock.item.CustomItemNames;
-import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
-import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup;
+import me.xmrvizzy.skyblocker.skyblock.item.*;
import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
import me.xmrvizzy.skyblocker.skyblock.quicknav.QuickNav;
import me.xmrvizzy.skyblocker.skyblock.rift.TheRift;
import me.xmrvizzy.skyblocker.skyblock.shortcut.Shortcuts;
import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster;
import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
import me.xmrvizzy.skyblocker.utils.NEURepo;
import me.xmrvizzy.skyblocker.utils.Utils;
@@ -90,17 +89,22 @@ public class SkyblockerMod implements ClientModInitializer {
FishingHelper.init();
TabHud.init();
DungeonMap.init();
+ DungeonSecrets.init();
DungeonBlaze.init();
TheRift.init();
TitleContainer.init();
+ ScreenMaster.init();
OcclusionCulling.init();
TeleportOverlay.init();
CustomItemNames.init();
CustomArmorDyeColors.init();
CustomArmorTrims.init();
+ TicTacToe.init();
containerSolverManager.init();
+ statusBarTracker.init();
scheduler.scheduleCyclic(Utils::update, 20);
scheduler.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 100);
+ scheduler.scheduleCyclic(TicTacToe::tick, 4);
scheduler.scheduleCyclic(LividColor::update, 10);
scheduler.scheduleCyclic(BackpackPreview::tick, 50);
scheduler.scheduleCyclic(DwarvenHud::update, 40);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
index c9a67ade..55fffa9c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
@@ -12,8 +12,8 @@ import me.shedaniel.autoconfig.annotation.ConfigEntry;
import me.shedaniel.autoconfig.serializer.ConfigSerializer;
import me.shedaniel.autoconfig.serializer.GsonConfigSerializer;
import me.xmrvizzy.skyblocker.SkyblockerMod;
-import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult;
import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorTrims;
+import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult;
import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
@@ -79,7 +79,7 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Category("button6")
@ConfigEntry.Gui.CollapsibleObject()
- public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), "Storage", "/storage");
+ public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), "(?:Rift )?Storage(?: \\(1/2\\))?", "/storage");
@ConfigEntry.Category("button7")
@ConfigEntry.Gui.CollapsibleObject()
@@ -407,22 +407,46 @@ public class SkyblockerConfig implements ConfigData {
}
public static class Dungeons {
+ @ConfigEntry.Gui.CollapsibleObject
+ public SecretWaypoints secretWaypoints = new SecretWaypoints();
@ConfigEntry.Gui.Tooltip()
public boolean croesusHelper = true;
public boolean enableMap = true;
public float mapScaling = 1f;
public int mapX = 2;
public int mapY = 2;
+ @ConfigEntry.Gui.Tooltip
+ public boolean starredMobGlow = false;
public boolean solveThreeWeirdos = true;
@ConfigEntry.Gui.Tooltip
public boolean blazesolver = true;
public boolean solveTrivia = true;
+ @ConfigEntry.Gui.Tooltip
+ public boolean solveTicTacToe = true;
@ConfigEntry.Gui.CollapsibleObject
public LividColor lividColor = new LividColor();
@ConfigEntry.Gui.CollapsibleObject()
public Terminals terminals = new Terminals();
}
+ public static class SecretWaypoints {
+
+ public boolean enableSecretWaypoints = true;
+ @ConfigEntry.Gui.Tooltip()
+ public boolean noInitSecretWaypoints = false;
+ public boolean enableEntranceWaypoints = true;
+ public boolean enableSuperboomWaypoints = true;
+ public boolean enableChestWaypoints = true;
+ public boolean enableItemWaypoints = true;
+ public boolean enableBatWaypoints = true;
+ public boolean enableWitherWaypoints = true;
+ public boolean enableLeverWaypoints = true;
+ public boolean enableFairySoulWaypoints = true;
+ public boolean enableStonkWaypoints = true;
+ @ConfigEntry.Gui.Tooltip()
+ public boolean enableDefaultWaypoints = true;
+ }
+
public static class LividColor {
@ConfigEntry.Gui.Tooltip()
public boolean enableLividColor = true;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
index 2d109524..f52e2f7f 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
@@ -1,23 +1,40 @@
package me.xmrvizzy.skyblocker.mixin;
import com.llamalad7.mixinextras.injector.WrapWithCondition;
-
+import com.llamalad7.mixinextras.sugar.Local;
import dev.cbyrne.betterinject.annotations.Inject;
import me.xmrvizzy.skyblocker.skyblock.FishingHelper;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets;
import me.xmrvizzy.skyblocker.utils.Utils;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
+import net.minecraft.entity.ItemEntity;
+import net.minecraft.entity.LivingEntity;
import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
import org.slf4j.Logger;
+import org.spongepowered.asm.mixin.Final;
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.ModifyVariable;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin {
+ @Shadow
+ @Final
+ private MinecraftClient client;
+
@Inject(method = "onPlaySound", at = @At("RETURN"))
private void skyblocker$onPlaySound(PlaySoundS2CPacket packet) {
FishingHelper.onSound(packet);
}
+ @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "STORE", ordinal = 0))
+ private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity, @Local LivingEntity collector) {
+ DungeonSecrets.onItemPickup(itemEntity, collector, collector == client.player);
+ return itemEntity;
+ }
+
@WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false))
private boolean skyblocker$cancelEntityPassengersWarning(Logger instance, String msg) {
return !Utils.isOnHypixel();
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
index cd0ccefd..6c059741 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
@@ -12,12 +12,14 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
+import net.minecraft.util.Formatting;
import net.minecraft.util.math.ColorHelper;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
-import java.awt.*;
+
+import java.awt.Color;
import java.util.regex.Pattern;
@Mixin(DrawContext.class)
@@ -31,39 +33,58 @@ public abstract class DrawContextMixin {
@Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD"))
public void skyblocker$renderItemBar(@Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y) {
- if (Utils.isOnSkyblock() && SkyblockerConfig.get().locations.dwarvenMines.enableDrillFuel) {
- if (!stack.isEmpty()) {
- NbtCompound tag = stack.getNbt();
- if (tag != null && tag.contains("ExtraAttributes")) {
- if (tag.getCompound("ExtraAttributes").contains("drill_fuel")) {
- float current = 3000.0F;
- float max = 3000.0F;
+ if (!Utils.isOnSkyblock() || !SkyblockerConfig.get().locations.dwarvenMines.enableDrillFuel || stack.isEmpty()) {
+ return;
+ }
- for (String line : ItemUtils.getTooltipStrings(stack)) {
- if (line.contains("Fuel: ")) {
- String clear = Pattern.compile("[^0-9 /]").matcher(line).replaceAll("").trim();
- String[] split = clear.split("/");
- current = Integer.parseInt(split[0]);
- max = Integer.parseInt(split[1]) * 1000;
- break;
- }
- }
+ NbtCompound tag = stack.getNbt();
+ if (tag == null || !tag.contains("ExtraAttributes")) {
+ return;
+ }
- matrices.push();
- matrices.translate(0f, 0f, 200f);
- RenderSystem.disableDepthTest();
+ NbtCompound extraAttributes = tag.getCompound("ExtraAttributes");
+ if (!extraAttributes.contains("drill_fuel") && !extraAttributes.getString("id").equals("PICKONIMBUS")) {
+ return;
+ }
+ matrices.push();
+ matrices.translate(0f, 0f, 200f);
+ RenderSystem.disableDepthTest();
- float hue = Math.max(0.0F, 1.0F - (max - current) / max);
- int width = Math.round(current / max * 13.0F);
- Color color = Color.getHSBColor(hue / 3.0F, 1.0F, 1.0F);
- this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 15, y + 15, 0xFF000000);
- this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 2 + width, y + 14, ColorHelper.Argb.getArgb(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()));
+ float current = 0.0F;
+ float max = 0.0F;
+ String clearFormatting = "";
- matrices.pop();
- RenderSystem.enableDepthTest();
+ for (String line : ItemUtils.getTooltipStrings(stack)) {
+ clearFormatting = Formatting.strip(line);
+ if (line.contains("Fuel: ")) {
+ if (clearFormatting != null) {
+ String clear = Pattern.compile("[^0-9 /]").matcher(clearFormatting).replaceAll("").trim();
+ String[] split = clear.split("/");
+ current = Integer.parseInt(split[0]);
+ max = Integer.parseInt(split[1]) * 1000;
+ }
+ break;
+ } else if (line.contains("uses.")) {
+ if (clearFormatting != null) {
+ int startIndex = clearFormatting.lastIndexOf("after") + 6;
+ int endIndex = clearFormatting.indexOf("uses", startIndex);
+ if (startIndex >= 0 && endIndex > startIndex) {
+ String usesString = clearFormatting.substring(startIndex, endIndex).trim();
+ current = Integer.parseInt(usesString);
+ max = 5000;
}
}
+ break;
}
}
+
+ float hue = Math.max(0.0F, 1.0F - (max - current) / max);
+ int width = Math.round(current / max * 13.0F);
+ Color color = Color.getHSBColor(hue / 3.0F, 1.0F, 1.0F);
+ this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 15, y + 15, 0xFF000000);
+ this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 2 + width, y + 14, ColorHelper.Argb.getArgb(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()));
+
+ matrices.pop();
+ RenderSystem.enableDepthTest();
}
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
index 4cda73aa..bc3df266 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
@@ -5,14 +5,12 @@ import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.skyblock.FancyStatusBars;
import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock;
-import me.xmrvizzy.skyblocker.skyblock.StatusBarTracker;
import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap;
import me.xmrvizzy.skyblocker.utils.Utils;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
-import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -27,8 +25,6 @@ public abstract class InGameHudMixin {
@Unique
private static final Identifier SLOT_LOCK = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/slot_lock.png");
@Unique
- private final StatusBarTracker statusBarTracker = SkyblockerMod.getInstance().statusBarTracker;
- @Unique
private final FancyStatusBars statusBars = new FancyStatusBars();
@Shadow
@@ -36,22 +32,6 @@ public abstract class InGameHudMixin {
@Shadow
private int scaledWidth;
- @Shadow
- public abstract void setOverlayMessage(Text message, boolean tinted);
-
- @Inject(method = "setOverlayMessage(Lnet/minecraft/text/Text;Z)V", at = @At("HEAD"), cancellable = true)
- private void skyblocker$onSetOverlayMessage(Text message, boolean tinted, CallbackInfo ci) {
- if (!Utils.isOnSkyblock() || !SkyblockerConfig.get().general.bars.enableBars || Utils.isInTheRift())
- return;
- String msg = message.getString();
- String res = statusBarTracker.update(msg, SkyblockerConfig.get().messages.hideMana);
- if (!msg.equals(res)) {
- if (res != null)
- setOverlayMessage(Text.of(res), tinted);
- ci.cancel();
- }
- }
-
@Inject(method = "renderHotbar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;renderHotbarItem(Lnet/minecraft/client/gui/DrawContext;IIFLnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;I)V", ordinal = 0))
public void skyblocker$renderHotbarItemLock(float tickDelta, DrawContext context, CallbackInfo ci, @Local(ordinal = 4, name = "m") int index, @Local(ordinal = 5, name = "n") int x, @Local(ordinal = 6, name = "o") int y) {
if (Utils.isOnSkyblock() && HotbarSlotLock.isLocked(index)) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
index 819ddb4b..ec7b6ddd 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
@@ -2,7 +2,8 @@ package me.xmrvizzy.skyblocker.mixin;
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.skyblock.tabhud.screenbuilder.ScreenMaster;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
import me.xmrvizzy.skyblocker.utils.Utils;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@@ -37,9 +38,16 @@ public class PlayerListHudMixin {
}
int h = MinecraftClient.getInstance().getWindow().getScaledHeight();
+ float scale = SkyblockerConfig.get().general.tabHud.tabHudScale / 100f;
+ w = (int) (w / scale);
+ h = (int) (h / scale);
+
+ PlayerListMgr.updateFooter(footer);
+
try {
- Screen screen = Screen.getCorrect(w, h, footer);
- screen.render(context);
+ ScreenMaster.render(context, w,h);
+ // Screen screen = Screen.getCorrect(w, h, footer);
+ // screen.render(context);
info.cancel();
} catch (Exception e) {
TabHud.LOGGER.error("[Skyblocker] Encountered unknown exception while drawing default hud", e);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java
new file mode 100644
index 00000000..3b796c38
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java
@@ -0,0 +1,33 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.sugar.Local;
+import com.llamalad7.mixinextras.sugar.Share;
+import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.StarredMobGlow;
+import net.minecraft.client.render.WorldRenderer;
+import net.minecraft.entity.Entity;
+
+@Mixin(WorldRenderer.class)
+public class WorldRendererMixin {
+
+ @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;hasOutline(Lnet/minecraft/entity/Entity;)Z"))
+ private boolean skyblocker$shouldStarredMobGlow(boolean original, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) {
+ boolean isAStarredMobThatShouldGlow = SkyblockerConfig.get().locations.dungeons.starredMobGlow && StarredMobGlow.shouldMobGlow(entity);
+
+ isGlowingStarredMob.set(isAStarredMobThatShouldGlow);
+
+ return original || isAStarredMobThatShouldGlow;
+ }
+
+ @ModifyVariable(method = "render", at = @At("STORE"), ordinal = 0)
+ private int skyblocker$modifyGlowColor(int color, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) {
+ return isGlowingStarredMob.get() ? StarredMobGlow.getGlowColor(entity) : color;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java
index 96165ce8..aeee9978 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java
@@ -1,7 +1,11 @@
package me.xmrvizzy.skyblocker.skyblock;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.text.Text;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -16,6 +20,10 @@ public class StatusBarTracker {
private Resource mana = new Resource(100, 100, 0);
private int defense = 0;
+ public void init() {
+ ClientReceiveMessageEvents.MODIFY_GAME.register(this::onOverlayMessage);
+ }
+
public Resource getHealth() {
return this.health;
}
@@ -57,6 +65,13 @@ public class StatusBarTracker {
return str;
}
+ private Text onOverlayMessage(Text text, boolean overlay) {
+ if (!overlay || !Utils.isOnSkyblock() || !SkyblockerConfig.get().general.bars.enableBars || Utils.isInTheRift()) {
+ return text;
+ }
+ return Text.of(update(text.getString(), SkyblockerConfig.get().messages.hideMana));
+ }
+
public String update(String actionBar, boolean filterManaUse) {
var sb = new StringBuilder();
Matcher matcher = STATUS_HEALTH.matcher(actionBar);
@@ -89,5 +104,6 @@ public class StatusBarTracker {
return res.isEmpty() ? null : res;
}
- public record Resource(int value, int max, int overflow) {}
+ public record Resource(int value, int max, int overflow) {
+ }
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
index 0e56ece0..1e949cd1 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
@@ -25,7 +25,7 @@ public class CroesusHelper extends ContainerSolver {
List<ColorHighlight> highlights = new ArrayList<>();
for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) {
ItemStack stack = entry.getValue();
- if (stack != null && stack.getNbt() != null && stack.getNbt().toString().contains("No more Chests to open!")) {
+ if (stack != null && stack.getNbt() != null && stack.getNbt().toString().contains("Opened Chest:")) {
highlights.add(ColorHighlight.gray(entry.getKey()));
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
index ef94d9ec..09bef802 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
@@ -8,8 +8,10 @@ import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.Entity;
+import net.minecraft.entity.decoration.ArmorStandEntity;
+import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import org.slf4j.Logger;
@@ -19,89 +21,132 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
+/**
+ * This class provides functionality to render outlines around Blaze entities
+ */
public class DungeonBlaze {
private static final Logger LOGGER = LoggerFactory.getLogger(DungeonBlaze.class.getName());
private static final float[] GREEN_COLOR_COMPONENTS = {0.0F, 1.0F, 0.0F};
- private static final float[] WHITE_COLOR_COMPONENTS = { 1.0f, 1.0f, 1.0f };
- static Entity highestBlaze = null;
- static Entity lowestBlaze = null;
- static Entity nextHighestBlaze = null;
- static Entity nextLowestBlaze = null;
+ private static final float[] WHITE_COLOR_COMPONENTS = {1.0f, 1.0f, 1.0f};
+
+ private static ArmorStandEntity highestBlaze = null;
+ private static ArmorStandEntity lowestBlaze = null;
+ private static ArmorStandEntity nextHighestBlaze = null;
+ private static ArmorStandEntity nextLowestBlaze = null;
public static void init() {
SkyblockerMod.getInstance().scheduler.scheduleCyclic(DungeonBlaze::update, 10);
WorldRenderEvents.BEFORE_DEBUG_RENDER.register(DungeonBlaze::blazeRenderer);
}
+ /**
+ * Updates the state of Blaze entities and triggers the rendering process if necessary.
+ */
public static void update() {
- if (!SkyblockerConfig.get().locations.dungeons.blazesolver) return;
ClientWorld world = MinecraftClient.getInstance().world;
- if (world == null || !Utils.isInDungeons()) return;
- Iterable<Entity> entities = world.getEntities();
- List<ObjectIntPair<Entity>> blazes = new ArrayList<>();
-
- for (Entity entity : entities) {
- String blazeName = entity.getName().getString();
+ ClientPlayerEntity player = MinecraftClient.getInstance().player;
+ if (world == null || player == null || !Utils.isInDungeons()) return;
+ List<ObjectIntPair<ArmorStandEntity>> blazes = getBlazesInWorld(world, player);
+ sortBlazes(blazes);
+ updateBlazeEntities(blazes);
+ }
+ /**
+ * Retrieves Blaze entities in the world and parses their health information.
+ *
+ * @param world The client world to search for Blaze entities.
+ * @return A list of Blaze entities and their associated health.
+ */
+ private static List<ObjectIntPair<ArmorStandEntity>> getBlazesInWorld(ClientWorld world, ClientPlayerEntity player) {
+ List<ObjectIntPair<ArmorStandEntity>> blazes = new ArrayList<>();
+ for (ArmorStandEntity blaze : world.getEntitiesByClass(ArmorStandEntity.class, player.getBoundingBox().expand(500D), EntityPredicates.NOT_MOUNTED)) {
+ String blazeName = blaze.getName().getString();
if (blazeName.contains("Blaze") && blazeName.contains("/")) {
try {
int health = Integer.parseInt(blazeName.substring(blazeName.indexOf("/") + 1, blazeName.length() - 1));
-
- blazes.add(ObjectIntPair.of(entity, health));
+ blazes.add(ObjectIntPair.of(blaze, health));
} catch (NumberFormatException e) {
- LOGGER.error("[Skyblocker DungeonBlazeSolver] Failed to parse blaze health: " + blazeName, e);
+ handleException(e);
}
}
}
+ return blazes;
+ }
- // Order the blazes in the list from the lowest health to the highest health
+ /**
+ * Sorts the Blaze entities based on their health values.
+ *
+ * @param blazes The list of Blaze entities to be sorted.
+ */
+ private static void sortBlazes(List<ObjectIntPair<ArmorStandEntity>> blazes) {
blazes.sort(Comparator.comparingInt(ObjectIntPair::rightInt));
+ }
- // Ensure that there are blazes in the list
+ /**
+ * Updates information about Blaze entities based on sorted list.
+ *
+ * @param blazes The sorted list of Blaze entities with associated health values.
+ */
+ private static void updateBlazeEntities(List<ObjectIntPair<ArmorStandEntity>> blazes) {
if (!blazes.isEmpty()) {
lowestBlaze = blazes.get(0).left();
-
int highestIndex = blazes.size() - 1;
highestBlaze = blazes.get(highestIndex).left();
-
- // If there's more than 1 blaze
if (blazes.size() > 1) {
- nextLowestBlaze = blazes.get(1).left();
- nextHighestBlaze = blazes.get(highestIndex - 1).left();
+ nextLowestBlaze = blazes.get(1).left();
+ nextHighestBlaze = blazes.get(highestIndex - 1).left();
}
}
-
}
- public static void blazeRenderer(WorldRenderContext context) {
+ /**
+ * Renders outlines for Blaze entities based on health and position.
+ *
+ * @param wrc The WorldRenderContext used for rendering.
+ */
+ public static void blazeRenderer(WorldRenderContext wrc) {
try {
- if (SkyblockerConfig.get().locations.dungeons.blazesolver && highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive()) {
- /* Outline */
+ if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfig.get().locations.dungeons.blazesolver) {
if (highestBlaze.getY() < 69) {
- Box blaze = highestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderHelper.renderOutline(context, blaze, GREEN_COLOR_COMPONENTS, 5f);
-
- if (nextHighestBlaze != null && nextHighestBlaze.isAlive() && nextHighestBlaze != highestBlaze) {
- Box nextBlaze = nextHighestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderHelper.renderOutline(context, nextBlaze, WHITE_COLOR_COMPONENTS, 5f);
- RenderHelper.renderLinesFromPoints(context, new Vec3d[] { blaze.getCenter(), nextBlaze.getCenter() }, WHITE_COLOR_COMPONENTS, 1f, 5f);
- }
+ renderBlazeOutline(highestBlaze, nextHighestBlaze, wrc);
}
-
- /* Outline */
if (lowestBlaze.getY() > 69) {
- Box blaze = lowestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderHelper.renderOutline(context, blaze, GREEN_COLOR_COMPONENTS, 5f);
-
- if (nextLowestBlaze != null && nextLowestBlaze.isAlive() && nextLowestBlaze != lowestBlaze) {
- Box nextBlaze = nextLowestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderHelper.renderOutline(context, nextBlaze, WHITE_COLOR_COMPONENTS, 5f);
- RenderHelper.renderLinesFromPoints(context, new Vec3d[] { blaze.getCenter(), nextBlaze.getCenter() }, WHITE_COLOR_COMPONENTS, 1f, 5f);
- }
+ renderBlazeOutline(lowestBlaze, nextLowestBlaze, wrc);
}
}
} catch (Exception e) {
- LOGGER.warn("[Skyblocker DungeonBlazeRenderer] Failed to render blaze boxes", e);
+ handleException(e);
+ }
+ }
+
+ /**
+ * Renders outlines for Blaze entities and connections between them.
+ *
+ * @param blaze The Blaze entity for which to render an outline.
+ * @param nextBlaze The next Blaze entity for connection rendering.
+ * @param wrc The WorldRenderContext used for rendering.
+ */
+ private static void renderBlazeOutline(ArmorStandEntity blaze, ArmorStandEntity nextBlaze, WorldRenderContext wrc) {
+ Box blazeBox = blaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
+ RenderHelper.renderOutline(wrc, blazeBox, GREEN_COLOR_COMPONENTS, 5f);
+
+ if (nextBlaze != null && nextBlaze.isAlive() && nextBlaze != blaze) {
+ Box nextBlazeBox = nextBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
+ RenderHelper.renderOutline(wrc, nextBlazeBox, WHITE_COLOR_COMPONENTS, 5f);
+
+ Vec3d blazeCenter = blazeBox.getCenter();
+ Vec3d nextBlazeCenter = nextBlazeBox.getCenter();
+
+ RenderHelper.renderLinesFromPoints(wrc, new Vec3d[]{blazeCenter, nextBlazeCenter}, WHITE_COLOR_COMPONENTS, 1f, 5f);
}
}
-} \ No newline at end of file
+
+ /**
+ * Handles exceptions by logging and printing stack traces.
+ *
+ * @param e The exception to handle.
+ */
+ private static void handleException(Exception e) {
+ LOGGER.warn("[Skyblocker BlazeRenderer] Encountered an unknown exception", e);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java
new file mode 100644
index 00000000..64a28712
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java
@@ -0,0 +1,51 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.render.culling.OcclusionCulling;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.decoration.ArmorStandEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.predicate.entity.EntityPredicates;
+import net.minecraft.util.math.Box;
+
+public class StarredMobGlow {
+
+ public static boolean shouldMobGlow(Entity entity) {
+ Box box = entity.getBoundingBox();
+
+ if (Utils.isInDungeons() && !entity.isInvisible() && OcclusionCulling.isVisible(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)) {
+ //Minibosses
+ if (entity instanceof PlayerEntity) {
+ switch (entity.getName().getString()) {
+ case "Lost Adventurer": return true;
+ case "Shadow Assassin": return true;
+ case "Diamond Guy": return true;
+ }
+ }
+
+ //Regular Mobs
+ if (!(entity instanceof ArmorStandEntity)) {
+ Box searchBox = box.expand(0, 2, 0);
+ List<ArmorStandEntity> armorStands = entity.getWorld().getEntitiesByClass(ArmorStandEntity.class, searchBox, EntityPredicates.NOT_MOUNTED);
+
+ if (!armorStands.isEmpty() && armorStands.get(0).getName().getString().contains("✯")) return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static int getGlowColor(Entity entity) {
+ if (entity instanceof PlayerEntity) {
+ switch (entity.getName().getString()) {
+ case "Lost Adventurer": return 0xfee15c;
+ case "Shadow Assassin": return 0x5b2cb2;
+ case "Diamond Guy": return 0x57c2f7;
+ }
+ }
+
+ return 0xf57738;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java
new file mode 100644
index 00000000..a97b6752
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java
@@ -0,0 +1,136 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.tictactoe.TicTacToeUtils;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+import net.minecraft.block.Block;
+import net.minecraft.block.Blocks;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.decoration.ItemFrameEntity;
+import net.minecraft.item.FilledMapItem;
+import net.minecraft.item.map.MapState;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Box;
+import net.minecraft.util.math.Direction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Thanks to Danker for a reference implementation!
+ */
+public class TicTacToe {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TicTacToe.class);
+ private static final float[] RED_COLOR_COMPONENTS = {1.0F, 0.0F, 0.0F};
+ private static Box nextBestMoveToMake = null;
+
+ public static void init() {
+ WorldRenderEvents.BEFORE_DEBUG_RENDER.register(TicTacToe::solutionRenderer);
+ }
+
+ public static void tick() {
+ MinecraftClient client = MinecraftClient.getInstance();
+ ClientWorld world = client.world;
+ ClientPlayerEntity player = client.player;
+
+ nextBestMoveToMake = null;
+
+ if (world == null || player == null || !Utils.isInDungeons()) return;
+
+ //Search within 21 blocks for item frames that contain maps
+ Box searchBox = new Box(player.getX() - 21, player.getY() - 21, player.getZ() - 21, player.getX() + 21, player.getY() + 21, player.getZ() + 21);
+ List<ItemFrameEntity> itemFramesThatHoldMaps = world.getEntitiesByClass(ItemFrameEntity.class, searchBox, ItemFrameEntity::containsMap);
+
+ try {
+ //Only attempt to solve if its the player's turn
+ if (itemFramesThatHoldMaps.size() != 9 && itemFramesThatHoldMaps.size() % 2 == 1) {
+ char[][] board = new char[3][3];
+ BlockPos leftmostRow = null;
+ int sign = 1;
+ char facing = 'X';
+
+ for (ItemFrameEntity itemFrame : itemFramesThatHoldMaps) {
+ MapState mapState = world.getMapState(FilledMapItem.getMapName(itemFrame.getMapId().getAsInt()));
+
+ if (mapState == null) continue;
+
+ int column = 0, row;
+ sign = 1;
+
+ //Find position of the item frame relative to where it is on the tic tac toe board
+ if (itemFrame.getHorizontalFacing() == Direction.SOUTH || itemFrame.getHorizontalFacing() == Direction.WEST) sign = -1;
+ BlockPos itemFramePos = BlockPos.ofFloored(itemFrame.getX(), itemFrame.getY(), itemFrame.getZ());
+
+ for (int i = 2; i >= 0; i--) {
+ int realI = i * sign;
+ BlockPos blockPos = itemFramePos;
+
+ if (itemFrame.getX() % 0.5 == 0) {
+ blockPos = itemFramePos.add(realI, 0, 0);
+ } else if (itemFrame.getZ() % 0.5 == 0) {
+ blockPos = itemFramePos.add(0, 0, realI);
+ facing = 'Z';
+ }
+
+ Block block = world.getBlockState(blockPos).getBlock();
+ if (block == Blocks.AIR || block == Blocks.STONE_BUTTON) {
+ leftmostRow = blockPos;
+ column = i;
+
+ break;
+ }
+ }
+
+ //Determine the row of the item frame
+ if (itemFrame.getY() == 72.5) {
+ row = 0;
+ } else if (itemFrame.getY() == 71.5) {
+ row = 1;
+ } else if (itemFrame.getY() == 70.5) {
+ row = 2;
+ } else {
+ continue;
+ }
+
+
+ //Get the color of the middle pixel of the map which determines whether its X or O
+ int middleColor = mapState.colors[8256] & 255;
+
+ if (middleColor == 114) {
+ board[row][column] = 'X';
+ } else if (middleColor == 33) {
+ board[row][column] = 'O';
+ }
+
+ int bestMove = TicTacToeUtils.getBestMove(board) - 1;
+
+ if (leftmostRow != null) {
+ double drawX = facing == 'X' ? leftmostRow.getX() - sign * (bestMove % 3) : leftmostRow.getX();
+ double drawY = 72 - (double) (bestMove / 3);
+ double drawZ = facing == 'Z' ? leftmostRow.getZ() - sign * (bestMove % 3) : leftmostRow.getZ();
+
+ nextBestMoveToMake = new Box(drawX, drawY, drawZ, drawX + 1, drawY + 1, drawZ + 1);
+ }
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while determining a tic tac toe solution!", e);
+ }
+ }
+
+ private static void solutionRenderer(WorldRenderContext context) {
+ try {
+ if (SkyblockerConfig.get().locations.dungeons.solveTicTacToe && nextBestMoveToMake != null) {
+ RenderHelper.renderOutline(context, nextBestMoveToMake, RED_COLOR_COMPONENTS, 5);
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while rendering the tic tac toe solution!", e);
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java
new file mode 100644
index 00000000..b56b2827
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java
@@ -0,0 +1,275 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
+
+import com.google.gson.JsonObject;
+import it.unimi.dsi.fastutil.ints.IntSortedSet;
+import it.unimi.dsi.fastutil.objects.ObjectIntPair;
+import net.minecraft.block.MapColor;
+import net.minecraft.item.map.MapIcon;
+import net.minecraft.item.map.MapState;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.math.Vec3i;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.RoundingMode;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+
+import java.util.*;
+
+public class DungeonMapUtils {
+ public static final byte BLACK_COLOR = MapColor.BLACK.getRenderColorByte(MapColor.Brightness.LOWEST);
+ public static final byte WHITE_COLOR = MapColor.WHITE.getRenderColorByte(MapColor.Brightness.HIGH);
+
+ public static byte getColor(MapState map, @Nullable Vector2ic pos) {
+ return pos == null ? -1 : getColor(map, pos.x(), pos.y());
+ }
+
+ public static byte getColor(MapState map, int x, int z) {
+ if (x < 0 || z < 0 || x >= 128 || z >= 128) {
+ return -1;
+ }
+ return map.colors[x + (z << 7)];
+ }
+
+ public static boolean isEntranceColor(MapState map, int x, int z) {
+ return getColor(map, x, z) == Room.Type.ENTRANCE.color;
+ }
+
+ public static boolean isEntranceColor(MapState map, @Nullable Vector2ic pos) {
+ return getColor(map, pos) == Room.Type.ENTRANCE.color;
+ }
+
+ @Nullable
+ private static Vector2i getMapPlayerPos(MapState map) {
+ for (MapIcon icon : map.getIcons()) {
+ if (icon.getType() == MapIcon.Type.FRAME) {
+ return new Vector2i((icon.getX() >> 1) + 64, (icon.getZ() >> 1) + 64);
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public static ObjectIntPair<Vector2ic> getMapEntrancePosAndRoomSize(@NotNull MapState map) {
+ Vector2ic mapPos = getMapPlayerPos(map);
+ if (mapPos == null) {
+ return null;
+ }
+ Queue<Vector2ic> posToCheck = new ArrayDeque<>();
+ Set<Vector2ic> checked = new HashSet<>();
+ posToCheck.add(mapPos);
+ checked.add(mapPos);
+ while ((mapPos = posToCheck.poll()) != null) {
+ if (isEntranceColor(map, mapPos)) {
+ ObjectIntPair<Vector2ic> mapEntranceAndRoomSizePos = getMapEntrancePosAndRoomSizeAt(map, mapPos);
+ if (mapEntranceAndRoomSizePos.rightInt() > 0) {
+ return mapEntranceAndRoomSizePos;
+ }
+ }
+ Vector2ic pos = new Vector2i(mapPos).sub(10, 0);
+ if (checked.add(pos)) {
+ posToCheck.add(pos);
+ }
+ pos = new Vector2i(mapPos).sub(0, 10);
+ if (checked.add(pos)) {
+ posToCheck.add(pos);
+ }
+ pos = new Vector2i(mapPos).add(10, 0);
+ if (checked.add(pos)) {
+ posToCheck.add(pos);
+ }
+ pos = new Vector2i(mapPos).add(0, 10);
+ if (checked.add(pos)) {
+ posToCheck.add(pos);
+ }
+ }
+ return null;
+ }
+
+ private static ObjectIntPair<Vector2ic> getMapEntrancePosAndRoomSizeAt(MapState map, Vector2ic mapPosImmutable) {
+ Vector2i mapPos = new Vector2i(mapPosImmutable);
+ // noinspection StatementWithEmptyBody
+ while (isEntranceColor(map, mapPos.sub(1, 0))) {
+ }
+ mapPos.add(1, 0);
+ //noinspection StatementWithEmptyBody
+ while (isEntranceColor(map, mapPos.sub(0, 1))) {
+ }
+ return ObjectIntPair.of(mapPos.add(0, 1), getMapRoomSize(map, mapPos));
+ }
+
+ public static int getMapRoomSize(MapState map, Vector2ic mapEntrancePos) {
+ int i = -1;
+ //noinspection StatementWithEmptyBody
+ while (isEntranceColor(map, mapEntrancePos.x() + ++i, mapEntrancePos.y())) {
+ }
+ return i > 5 ? i : 0;
+ }
+
+ /**
+ * Gets the map position of the top left corner of the room the player is in.
+ *
+ * @param map the map
+ * @param mapEntrancePos the map position of the top left corner of the entrance
+ * @param mapRoomSize the size of a room on the map
+ * @return the map position of the top left corner of the room the player is in
+ * @implNote {@code mapPos} is shifted by 2 so room borders are evenly split.
+ * {@code mapPos} is then shifted by {@code offset} to align the top left most room at (0, 0)
+ * so subtracting the modulo will give the top left corner of the room shifted by {@code offset}.
+ * Finally, {@code mapPos} is shifted back by {@code offset} to its intended position.
+ */
+ @Nullable
+ public static Vector2ic getMapRoomPos(MapState map, Vector2ic mapEntrancePos, int mapRoomSize) {
+ int mapRoomSizeWithGap = mapRoomSize + 4;
+ Vector2i mapPos = getMapPlayerPos(map);
+ if (mapPos == null) {
+ return null;
+ }
+ Vector2ic offset = new Vector2i(mapEntrancePos.x() % mapRoomSizeWithGap, mapEntrancePos.y() % mapRoomSizeWithGap);
+ return mapPos.add(2, 2).sub(offset).sub(mapPos.x() % mapRoomSizeWithGap, mapPos.y() % mapRoomSizeWithGap).add(offset);
+ }
+
+ /**
+ * Gets the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room.
+ *
+ * @param physicalEntrancePos the physical position of the northwest corner of the entrance room
+ * @param mapEntrancePos the map position of the top left corner of the entrance room
+ * @param mapRoomSize the size of a room on the map
+ * @param physicalPos the physical position of the northwest corner of the room
+ * @return the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room
+ */
+ public static Vector2ic getMapPosFromPhysical(Vector2ic physicalEntrancePos, Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalPos) {
+ return new Vector2i(physicalPos).sub(physicalEntrancePos).div(32).mul(mapRoomSize + 4).add(mapEntrancePos);
+ }
+
+ /**
+ * @see #getPhysicalRoomPos(double, double)
+ */
+ @NotNull
+ public static Vector2ic getPhysicalRoomPos(@NotNull Vec3d pos) {
+ return getPhysicalRoomPos(pos.getX(), pos.getZ());
+ }
+
+ /**
+ * @see #getPhysicalRoomPos(double, double)
+ */
+ @NotNull
+ public static Vector2ic getPhysicalRoomPos(@NotNull Vec3i pos) {
+ return getPhysicalRoomPos(pos.getX(), pos.getZ());
+ }
+
+ /**
+ * Gets the physical position of the northwest corner of the room the given coordinate is in. Hypixel Skyblock Dungeons are aligned to a 32 by 32 blocks grid, allowing corners to be calculated through math.
+ *
+ * @param x the x position of the coordinate to calculate
+ * @param z the z position of the coordinate to calculate
+ * @return the physical position of the northwest corner of the room the player is in
+ * @implNote {@code physicalPos} is shifted by 0.5 so room borders are evenly split.
+ * {@code physicalPos} is further shifted by 8 because Hypixel offset dungeons by 8 blocks in Skyblock 0.12.3.
+ * Subtracting the modulo gives the northwest corner of the room shifted by 8. Finally, {@code physicalPos} is shifted back by 8 to its intended position.
+ */
+ @NotNull
+ public static Vector2ic getPhysicalRoomPos(double x, double z) {
+ Vector2i physicalPos = new Vector2i(x + 8.5, z + 8.5, RoundingMode.TRUNCATE);
+ return physicalPos.sub(MathHelper.floorMod(physicalPos.x(), 32), MathHelper.floorMod(physicalPos.y(), 32)).sub(8, 8);
+ }
+
+ public static Vector2ic[] getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic... mapPositions) {
+ for (int i = 0; i < mapPositions.length; i++) {
+ mapPositions[i] = getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, mapPositions[i]);
+ }
+ return mapPositions;
+ }
+
+ /**
+ * Gets the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room.
+ *
+ * @param mapEntrancePos the map position of the top left corner of the entrance room
+ * @param mapRoomSize the size of a room on the map
+ * @param physicalEntrancePos the physical position of the northwest corner of the entrance room
+ * @param mapPos the map position of the top left corner of the room
+ * @return the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room
+ */
+ public static Vector2ic getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic mapPos) {
+ return new Vector2i(mapPos).sub(mapEntrancePos).div(mapRoomSize + 4).mul(32).add(physicalEntrancePos);
+ }
+
+ public static Vector2ic getPhysicalCornerPos(Room.Direction direction, IntSortedSet segmentsX, IntSortedSet segmentsY) {
+ return switch (direction) {
+ case NW -> new Vector2i(segmentsX.firstInt(), segmentsY.firstInt());
+ case NE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.firstInt());
+ case SW -> new Vector2i(segmentsX.firstInt(), segmentsY.lastInt() + 30);
+ case SE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.lastInt() + 30);
+ };
+ }
+
+ public static BlockPos actualToRelative(Room.Direction direction, Vector2ic physicalCornerPos, BlockPos pos) {
+ return switch (direction) {
+ case NW -> new BlockPos(pos.getX() - physicalCornerPos.x(), pos.getY(), pos.getZ() - physicalCornerPos.y());
+ case NE -> new BlockPos(pos.getZ() - physicalCornerPos.y(), pos.getY(), -pos.getX() + physicalCornerPos.x());
+ case SW -> new BlockPos(-pos.getZ() + physicalCornerPos.y(), pos.getY(), pos.getX() - physicalCornerPos.x());
+ case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y());
+ };
+ }
+
+ public static BlockPos relativeToActual(Room.Direction direction, Vector2ic physicalCornerPos, JsonObject posJson) {
+ return relativeToActual(direction, physicalCornerPos, new BlockPos(posJson.get("x").getAsInt(), posJson.get("y").getAsInt(), posJson.get("z").getAsInt()));
+ }
+
+ public static BlockPos relativeToActual(Room.Direction direction, Vector2ic physicalCornerPos, BlockPos pos) {
+ return switch (direction) {
+ case NW -> new BlockPos(pos.getX() + physicalCornerPos.x(), pos.getY(), pos.getZ() + physicalCornerPos.y());
+ case NE -> new BlockPos(-pos.getZ() + physicalCornerPos.x(), pos.getY(), pos.getX() + physicalCornerPos.y());
+ case SW -> new BlockPos(pos.getZ() + physicalCornerPos.x(), pos.getY(), -pos.getX() + physicalCornerPos.y());
+ case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y());
+ };
+ }
+
+ public static Room.Type getRoomType(MapState map, Vector2ic mapPos) {
+ return switch (getColor(map, mapPos)) {
+ case 30 -> Room.Type.ENTRANCE;
+ case 63 -> Room.Type.ROOM;
+ case 66 -> Room.Type.PUZZLE;
+ case 62 -> Room.Type.TRAP;
+ case 74 -> Room.Type.MINIBOSS;
+ case 82 -> Room.Type.FAIRY;
+ case 18 -> Room.Type.BLOOD;
+ case 85 -> Room.Type.UNKNOWN;
+ default -> null;
+ };
+ }
+
+ public static Vector2ic[] getRoomSegments(MapState map, Vector2ic mapPos, int mapRoomSize, byte color) {
+ Set<Vector2ic> segments = new HashSet<>();
+ Queue<Vector2ic> queue = new ArrayDeque<>();
+ segments.add(mapPos);
+ queue.add(mapPos);
+ while (!queue.isEmpty()) {
+ Vector2ic curMapPos = queue.poll();
+ Vector2i newMapPos = new Vector2i();
+ if (getColor(map, newMapPos.set(curMapPos).sub(1, 0)) == color && !segments.contains(newMapPos.sub(mapRoomSize + 3, 0))) {
+ segments.add(newMapPos);
+ queue.add(newMapPos);
+ newMapPos = new Vector2i();
+ }
+ if (getColor(map, newMapPos.set(curMapPos).sub(0, 1)) == color && !segments.contains(newMapPos.sub(0, mapRoomSize + 3))) {
+ segments.add(newMapPos);
+ queue.add(newMapPos);
+ newMapPos = new Vector2i();
+ }
+ if (getColor(map, newMapPos.set(curMapPos).add(mapRoomSize, 0)) == color && !segments.contains(newMapPos.add(4, 0))) {
+ segments.add(newMapPos);
+ queue.add(newMapPos);
+ newMapPos = new Vector2i();
+ }
+ if (getColor(map, newMapPos.set(curMapPos).add(0, mapRoomSize)) == color && !segments.contains(newMapPos.add(0, 4))) {
+ segments.add(newMapPos);
+ queue.add(newMapPos);
+ }
+ }
+ DungeonSecrets.LOGGER.debug("[Skyblocker] Found dungeon room segments: {}", Arrays.toString(segments.toArray()));
+ return segments.toArray(Vector2ic[]::new);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
new file mode 100644
index 00000000..c916a5e4
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
@@ -0,0 +1,427 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.arguments.IntegerArgumentType;
+import com.mojang.brigadier.builder.ArgumentBuilder;
+import com.mojang.brigadier.builder.RequiredArgumentBuilder;
+import it.unimi.dsi.fastutil.objects.Object2ByteMap;
+import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectIntPair;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+import net.fabricmc.fabric.api.event.player.UseBlockCallback;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.entity.ItemEntity;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.item.FilledMapItem;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.item.map.MapState;
+import net.minecraft.resource.Resource;
+import net.minecraft.text.Text;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.World;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Vector2ic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.zip.InflaterInputStream;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class DungeonSecrets {
+ protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class);
+ private static final String DUNGEONS_PATH = "dungeons";
+ /**
+ * Maps the block identifier string to a custom numeric block id used in dungeon rooms data.
+ *
+ * @implNote Not using {@link net.minecraft.registry.Registry#getId(Object) Registry#getId(Block)} and {@link net.minecraft.block.Blocks Blocks} since this is also used by {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft.
+ */
+ @SuppressWarnings("JavadocReference")
+ protected static final Object2ByteMap<String> NUMERIC_ID = new Object2ByteOpenHashMap<>(Map.ofEntries(
+ Map.entry("minecraft:stone", (byte) 1),
+ Map.entry("minecraft:diorite", (byte) 2),
+ Map.entry("minecraft:polished_diorite", (byte) 3),
+ Map.entry("minecraft:andesite", (byte) 4),
+ Map.entry("minecraft:polished_andesite", (byte) 5),
+ Map.entry("minecraft:grass_block", (byte) 6),
+ Map.entry("minecraft:dirt", (byte) 7),
+ Map.entry("minecraft:coarse_dirt", (byte) 8),
+ Map.entry("minecraft:cobblestone", (byte) 9),
+ Map.entry("minecraft:bedrock", (byte) 10),
+ Map.entry("minecraft:oak_leaves", (byte) 11),
+ Map.entry("minecraft:gray_wool", (byte) 12),
+ Map.entry("minecraft:double_stone_slab", (byte) 13),
+ Map.entry("minecraft:mossy_cobblestone", (byte) 14),
+ Map.entry("minecraft:clay", (byte) 15),
+ Map.entry("minecraft:stone_bricks", (byte) 16),
+ Map.entry("minecraft:mossy_stone_bricks", (byte) 17),
+ Map.entry("minecraft:chiseled_stone_bricks", (byte) 18),
+ Map.entry("minecraft:gray_terracotta", (byte) 19),
+ Map.entry("minecraft:cyan_terracotta", (byte) 20),
+ Map.entry("minecraft:black_terracotta", (byte) 21)
+ ));
+ /**
+ * Block data for dungeon rooms. See {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU} for format details and how it's generated.
+ * All access to this map must check {@link #isRoomsLoaded()} to prevent concurrent modification.
+ */
+ @SuppressWarnings("JavadocReference")
+ protected static final HashMap<String, Map<String, Map<String, int[]>>> ROOMS_DATA = new HashMap<>();
+ @NotNull
+ private static final Map<Vector2ic, Room> rooms = new HashMap<>();
+ private static final Map<String, JsonElement> roomsJson = new HashMap<>();
+ private static final Map<String, JsonElement> waypointsJson = new HashMap<>();
+ @Nullable
+ private static CompletableFuture<Void> roomsLoaded;
+ /**
+ * The map position of the top left corner of the entrance room.
+ */
+ @Nullable
+ private static Vector2ic mapEntrancePos;
+ /**
+ * The size of a room on the map.
+ */
+ private static int mapRoomSize;
+ /**
+ * The physical position of the northwest corner of the entrance room.
+ */
+ @Nullable
+ private static Vector2ic physicalEntrancePos;
+ private static Room currentRoom;
+
+ public static boolean isRoomsLoaded() {
+ return roomsLoaded != null && roomsLoaded.isDone();
+ }
+
+ @SuppressWarnings("unused")
+ public static JsonObject getRoomMetadata(String room) {
+ return roomsJson.get(room).getAsJsonObject();
+ }
+
+ public static JsonArray getRoomWaypoints(String room) {
+ return waypointsJson.get(room).getAsJsonArray();
+ }
+
+ /**
+ * Loads the dungeon secrets asynchronously from {@code /assets/skyblocker/dungeons}.
+ * Use {@link #isRoomsLoaded()} to check for completion of loading.
+ */
+ public static void init() {
+ if (SkyblockerConfig.get().locations.dungeons.secretWaypoints.noInitSecretWaypoints) {
+ return;
+ }
+ // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set
+ CompletableFuture.runAsync(DungeonSecrets::load, MinecraftClient.getInstance()).exceptionally(e -> {
+ LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e);
+ return null;
+ });
+ SkyblockerMod.getInstance().scheduler.scheduleCyclic(DungeonSecrets::update, 10);
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonSecrets::render);
+ ClientReceiveMessageEvents.GAME.register(DungeonSecrets::onChatMessage);
+ ClientReceiveMessageEvents.GAME_CANCELED.register(DungeonSecrets::onChatMessage);
+ UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> onUseBlock(world, hitResult));
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("secrets")
+ .then(literal("markAsFound").then(markSecretsCommand(true)))
+ .then(literal("markAsMissing").then(markSecretsCommand(false)))))));
+ ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> reset()));
+ }
+
+ private static void load() {
+ long startTime = System.currentTimeMillis();
+ List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>();
+ for (Map.Entry<Identifier, Resource> resourceEntry : MinecraftClient.getInstance().getResourceManager().findResources(DUNGEONS_PATH, id -> id.getPath().endsWith(".skeleton")).entrySet()) {
+ String[] path = resourceEntry.getKey().getPath().split("/");
+ if (path.length != 4) {
+ LOGGER.error("[Skyblocker] Failed to load dungeon secrets, invalid resource identifier {}", resourceEntry.getKey());
+ break;
+ }
+ String dungeon = path[1];
+ String roomShape = path[2];
+ String room = path[3].substring(0, path[3].length() - ".skeleton".length());
+ ROOMS_DATA.computeIfAbsent(dungeon, dungeonKey -> new HashMap<>());
+ ROOMS_DATA.get(dungeon).computeIfAbsent(roomShape, roomShapeKey -> new HashMap<>());
+ dungeonFutures.add(CompletableFuture.supplyAsync(() -> readRoom(resourceEntry.getValue())).thenAcceptAsync(rooms -> {
+ Map<String, int[]> roomsMap = ROOMS_DATA.get(dungeon).get(roomShape);
+ synchronized (roomsMap) {
+ roomsMap.put(room, rooms);
+ }
+ LOGGER.debug("[Skyblocker] Loaded dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room);
+ }).exceptionally(e -> {
+ LOGGER.error("[Skyblocker] Failed to load dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room, e);
+ return null;
+ }));
+ }
+ dungeonFutures.add(CompletableFuture.runAsync(() -> {
+ try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) {
+ SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class).asMap().forEach((room, jsonElement) -> roomsJson.put(room.toLowerCase(), jsonElement));
+ SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class).asMap().forEach((room, jsonElement) -> waypointsJson.put(room.toLowerCase(), jsonElement));
+ LOGGER.debug("[Skyblocker] Loaded dungeon secrets json");
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Failed to load dungeon secrets json", e);
+ }
+ }));
+ roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("[Skyblocker] Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total in {} ms", ROOMS_DATA.size(), ROOMS_DATA.values().stream().mapToInt(Map::size).sum(), ROOMS_DATA.values().stream().map(Map::values).flatMap(Collection::stream).mapToInt(Map::size).sum(), System.currentTimeMillis() - startTime)).exceptionally(e -> {
+ LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e);
+ return null;
+ });
+ LOGGER.info("[Skyblocker] Started loading dungeon secrets in (blocked main thread for) {} ms", System.currentTimeMillis() - startTime);
+ }
+
+ private static int[] readRoom(Resource resource) throws RuntimeException {
+ try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(resource.getInputStream()))) {
+ return (int[]) in.readObject();
+ } catch (IOException | ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, Integer>> markSecretsCommand(boolean found) {
+ return argument("secret", IntegerArgumentType.integer()).executes(context -> {
+ int secretIndex = IntegerArgumentType.getInteger(context, "secret");
+ if (markSecrets(secretIndex, found)) {
+ context.getSource().sendFeedback(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFound" : "skyblocker.dungeons.secrets.markSecretMissing", secretIndex));
+ } else {
+ context.getSource().sendError(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFoundUnable" : "skyblocker.dungeons.secrets.markSecretMissingUnable", secretIndex));
+ }
+ return Command.SINGLE_SUCCESS;
+ });
+ }
+
+ /**
+ * Updates the dungeon. The general idea is similar to the Dungeon Rooms Mod.
+ * <p></p>
+ * When entering a new dungeon, this method:
+ * <ul>
+ * <li> Gets the physical northwest corner position of the entrance room and saves it in {@link #physicalEntrancePos}. </li>
+ * <li> Do nothing until the dungeon map exists. </li>
+ * <li> Gets the upper left corner of entrance room on the map and saves it in {@link #mapEntrancePos}. </li>
+ * <li> Gets the size of a room on the map in pixels and saves it in {@link #mapRoomSize}. </li>
+ * <li> Creates a new {@link Room} with {@link Room.Type} {@link Room.Type.ENTRANCE ENTRANCE} and sets {@link #currentRoom}. </li>
+ * </ul>
+ * When processing an existing dungeon, this method:
+ * <ul>
+ * <li> Calculates the physical northwest corner and upper left corner on the map of the room the player is currently in. </li>
+ * <li> Gets the room type based on the map color. </li>
+ * <li> If the room has not been created (when the physical northwest corner is not in {@link #rooms}):</li>
+ * <ul>
+ * <li> If the room type is {@link Room.Type.ROOM}, gets the northwest corner of all connected room segments with {@link DungeonMapUtils#getRoomSegments(MapState, Vector2ic, int, byte)}. (For example, a 1x2 room has two room segments.) </li>
+ * <li> Create a new room. </li>
+ * </ul>
+ * <li> Sets {@link #currentRoom} to the current room, either created from the previous step or from {@link #rooms}. </li>
+ * <li> Calls {@link Room#update()} on {@link #currentRoom}. </li>
+ * </ul>
+ */
+ @SuppressWarnings("JavadocReference")
+ private static void update() {
+ if (!SkyblockerConfig.get().locations.dungeons.secretWaypoints.enableSecretWaypoints) {
+ return;
+ }
+ if (!Utils.isInDungeons()) {
+ if (mapEntrancePos != null) {
+ reset();
+ }
+ return;
+ }
+ MinecraftClient client = MinecraftClient.getInstance();
+ ClientPlayerEntity player = client.player;
+ if (player == null || client.world == null) {
+ return;
+ }
+ if (physicalEntrancePos == null) {
+ Vec3d playerPos = player.getPos();
+ physicalEntrancePos = DungeonMapUtils.getPhysicalRoomPos(playerPos);
+ currentRoom = newRoom(Room.Type.ENTRANCE, physicalEntrancePos);
+ }
+ ItemStack stack = player.getInventory().main.get(8);
+ if (!stack.isOf(Items.FILLED_MAP)) {
+ return;
+ }
+ MapState map = FilledMapItem.getMapState(FilledMapItem.getMapId(stack), client.world);
+ if (map == null) {
+ return;
+ }
+ if (mapEntrancePos == null || mapRoomSize == 0) {
+ ObjectIntPair<Vector2ic> mapEntrancePosAndSize = DungeonMapUtils.getMapEntrancePosAndRoomSize(map);
+ if (mapEntrancePosAndSize == null) {
+ return;
+ }
+ mapEntrancePos = mapEntrancePosAndSize.left();
+ mapRoomSize = mapEntrancePosAndSize.rightInt();
+ LOGGER.info("[Skyblocker] Started dungeon with map room size {}, map entrance pos {}, player pos {}, and physical entrance pos {}", mapRoomSize, mapEntrancePos, client.player.getPos(), physicalEntrancePos);
+ }
+
+ Vector2ic physicalPos = DungeonMapUtils.getPhysicalRoomPos(client.player.getPos());
+ Vector2ic mapPos = DungeonMapUtils.getMapPosFromPhysical(physicalEntrancePos, mapEntrancePos, mapRoomSize, physicalPos);
+ Room room = rooms.get(physicalPos);
+ if (room == null) {
+ Room.Type type = DungeonMapUtils.getRoomType(map, mapPos);
+ if (type == null || type == Room.Type.UNKNOWN) {
+ return;
+ }
+ switch (type) {
+ case ENTRANCE, PUZZLE, TRAP, MINIBOSS, FAIRY, BLOOD -> room = newRoom(type, physicalPos);
+ case ROOM -> room = newRoom(type, DungeonMapUtils.getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, DungeonMapUtils.getRoomSegments(map, mapPos, mapRoomSize, type.color)));
+ }
+ }
+ if (room != null && currentRoom != room) {
+ currentRoom = room;
+ }
+ currentRoom.update();
+ }
+
+ /**
+ * Creates a new room with the given type and physical positions,
+ * adds the room to {@link #rooms}, and sets {@link #currentRoom} to the new room.
+ *
+ * @param type the type of room to create
+ * @param physicalPositions the physical positions of the room
+ */
+ @Nullable
+ private static Room newRoom(Room.Type type, Vector2ic... physicalPositions) {
+ try {
+ Room newRoom = new Room(type, physicalPositions);
+ for (Vector2ic physicalPos : physicalPositions) {
+ rooms.put(physicalPos, newRoom);
+ }
+ return newRoom;
+ } catch (IllegalArgumentException e) {
+ LOGGER.error("[Skyblocker] Failed to create room", e);
+ }
+ return null;
+ }
+
+ /**
+ * Renders the secret waypoints in {@link #currentRoom} if {@link #isCurrentRoomMatched()}.
+ */
+ private static void render(WorldRenderContext context) {
+ if (isCurrentRoomMatched()) {
+ currentRoom.render(context);
+ }
+ }
+
+ /**
+ * Calls {@link Room#onChatMessage(String)} on {@link #currentRoom} if the message is an overlay message and {@link #isCurrentRoomMatched()}.
+ * Used to detect when all secrets in a room are found.
+ */
+ private static void onChatMessage(Text text, boolean overlay) {
+ if (overlay && isCurrentRoomMatched()) {
+ currentRoom.onChatMessage(text.getString());
+ }
+ }
+
+ /**
+ * Calls {@link Room#onUseBlock(World, BlockHitResult)} on {@link #currentRoom} if {@link #isCurrentRoomMatched()}.
+ * Used to detect finding {@link SecretWaypoint.Category.CHEST} and {@link SecretWaypoint.Category.WITHER} secrets.
+ *
+ * @return {@link ActionResult#PASS}
+ */
+ @SuppressWarnings("JavadocReference")
+ private static ActionResult onUseBlock(World world, BlockHitResult hitResult) {
+ if (isCurrentRoomMatched()) {
+ currentRoom.onUseBlock(world, hitResult);
+ }
+ return ActionResult.PASS;
+ }
+
+ /**
+ * Calls {@link Room#onItemPickup(ItemEntity, LivingEntity)} on the room the {@code collector} is in if that room {@link #isRoomMatched(Room)}.
+ * Used to detect finding {@link SecretWaypoint.Category.ITEM} and {@link SecretWaypoint.Category.BAT} secrets.
+ * If the collector is the player, {@link #currentRoom} is used as an optimization.
+ */
+ @SuppressWarnings("JavadocReference")
+ public static void onItemPickup(ItemEntity itemEntity, LivingEntity collector, boolean isPlayer) {
+ if (isPlayer) {
+ if (isCurrentRoomMatched()) {
+ currentRoom.onItemPickup(itemEntity, collector);
+ }
+ } else {
+ Room room = getRoomAtPhysical(collector.getPos());
+ if (isRoomMatched(room)) {
+ room.onItemPickup(itemEntity, collector);
+ }
+ }
+ }
+
+ public static boolean markSecrets(int secretIndex, boolean found) {
+ if (isCurrentRoomMatched()) {
+ return currentRoom.markSecrets(secretIndex, found);
+ }
+ return false;
+ }
+
+ /**
+ * Gets the room at the given physical position.
+ *
+ * @param pos the physical position
+ * @return the room at the given physical position, or null if there is no room at the given physical position
+ * @see #rooms
+ * @see DungeonMapUtils#getPhysicalRoomPos(Vec3d)
+ */
+ @Nullable
+ private static Room getRoomAtPhysical(Vec3d pos) {
+ return rooms.get(DungeonMapUtils.getPhysicalRoomPos(pos));
+ }
+
+ /**
+ * Calls {@link #isRoomMatched(Room)} on {@link #currentRoom}.
+ *
+ * @return {@code true} if {@link #currentRoom} is not null and {@link #isRoomMatched(Room)}
+ */
+ private static boolean isCurrentRoomMatched() {
+ return isRoomMatched(currentRoom);
+ }
+
+ /**
+ * Calls {@link #shouldProcess()} and {@link Room#isMatched()} on the given room.
+ *
+ * @param room the room to check
+ * @return {@code true} if {@link #shouldProcess()}, the given room is not null, and {@link Room#isMatched()} on the given room
+ */
+ @Contract("null -> false")
+ private static boolean isRoomMatched(@Nullable Room room) {
+ return shouldProcess() && room != null && room.isMatched();
+ }
+
+ /**
+ * Checks if the player is in a dungeon and {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.Dungeons#secretWaypoints Secret Waypoints} is enabled.
+ *
+ * @return whether dungeon secrets should be processed
+ */
+ private static boolean shouldProcess() {
+ return SkyblockerConfig.get().locations.dungeons.secretWaypoints.enableSecretWaypoints && Utils.isInDungeons();
+ }
+
+ /**
+ * Resets fields when leaving a dungeon.
+ */
+ private static void reset() {
+ mapEntrancePos = null;
+ mapRoomSize = 0;
+ physicalEntrancePos = null;
+ rooms.clear();
+ currentRoom = null;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
new file mode 100644
index 00000000..fc62150c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
@@ -0,0 +1,461 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
+import it.unimi.dsi.fastutil.ints.IntSortedSet;
+import it.unimi.dsi.fastutil.ints.IntSortedSets;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.util.TriState;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.Blocks;
+import net.minecraft.block.MapColor;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.ItemEntity;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.registry.Registries;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.world.World;
+import org.apache.commons.lang3.tuple.MutableTriple;
+import org.apache.commons.lang3.tuple.Triple;
+import org.jetbrains.annotations.NotNull;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Room {
+ private static final Pattern SECRETS = Pattern.compile("§7(\\d{1,2})/(\\d{1,2}) Secrets");
+ @NotNull
+ private final Type type;
+ @NotNull
+ private final Set<Vector2ic> segments;
+ /**
+ * The shape of the room. See {@link #getShape(IntSortedSet, IntSortedSet)}.
+ */
+ @NotNull
+ private final Shape shape;
+ /**
+ * The room data containing all rooms for a specific dungeon and {@link #shape}.
+ */
+ private Map<String, int[]> roomsData;
+ /**
+ * Contains all possible dungeon rooms for this room. The list is gradually shrunk by checking blocks until only one room is left.
+ */
+ private List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms;
+ /**
+ * Contains all blocks that have been checked to prevent checking the same block multiple times.
+ */
+ private Set<BlockPos> checkedBlocks = new HashSet<>();
+ /**
+ * The task that is used to check blocks. This is used to ensure only one such task can run at a time.
+ */
+ private CompletableFuture<Void> findRoom;
+ private int doubleCheckBlocks;
+ /**
+ * Represents the matching state of the room with the following possible values:
+ * <li>{@link TriState#DEFAULT} means that the room has not been checked, is being processed, or does not {@link Type#needsScanning() need to be processed}.
+ * <li>{@link TriState#FALSE} means that the room has been checked and there is no match.
+ * <li>{@link TriState#TRUE} means that the room has been checked and there is a match.
+ */
+ private TriState matched = TriState.DEFAULT;
+ private Table<Integer, BlockPos, SecretWaypoint> secretWaypoints;
+
+ public Room(@NotNull Type type, @NotNull Vector2ic... physicalPositions) {
+ this.type = type;
+ segments = Set.of(physicalPositions);
+ IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray()));
+ IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray()));
+ shape = getShape(segmentsX, segmentsY);
+ roomsData = DungeonSecrets.ROOMS_DATA.getOrDefault("catacombs", Collections.emptyMap()).getOrDefault(shape.shape.toLowerCase(), Collections.emptyMap());
+ possibleRooms = getPossibleRooms(segmentsX, segmentsY);
+ }
+
+ @NotNull
+ public Type getType() {
+ return type;
+ }
+
+ public boolean isMatched() {
+ return matched == TriState.TRUE;
+ }
+
+ @Override
+ public String toString() {
+ return "Room{type=" + type + ", shape=" + shape + ", matched=" + matched + ", segments=" + Arrays.toString(segments.toArray()) + "}";
+ }
+
+ @NotNull
+ private Shape getShape(IntSortedSet segmentsX, IntSortedSet segmentsY) {
+ return switch (segments.size()) {
+ case 1 -> Shape.ONE_BY_ONE;
+ case 2 -> Shape.ONE_BY_TWO;
+ case 3 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.L_SHAPE : Shape.ONE_BY_THREE;
+ case 4 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.TWO_BY_TWO : Shape.ONE_BY_FOUR;
+ default -> throw new IllegalArgumentException("There are no matching room shapes with this set of physical positions: " + Arrays.toString(segments.toArray()));
+ };
+ }
+
+ private List<MutableTriple<Direction, Vector2ic, List<String>>> getPossibleRooms(IntSortedSet segmentsX, IntSortedSet segmentsY) {
+ List<String> possibleDirectionRooms = new ArrayList<>(roomsData.keySet());
+ List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms = new ArrayList<>();
+ for (Direction direction : getPossibleDirections(segmentsX, segmentsY)) {
+ possibleRooms.add(MutableTriple.of(direction, DungeonMapUtils.getPhysicalCornerPos(direction, segmentsX, segmentsY), possibleDirectionRooms));
+ }
+ return possibleRooms;
+ }
+
+ @NotNull
+ private Direction[] getPossibleDirections(IntSortedSet segmentsX, IntSortedSet segmentsY) {
+ return switch (shape) {
+ case ONE_BY_ONE, TWO_BY_TWO -> Direction.values();
+ case ONE_BY_TWO, ONE_BY_THREE, ONE_BY_FOUR -> {
+ if (segmentsX.size() > 1 && segmentsY.size() == 1) {
+ yield new Direction[]{Direction.NW, Direction.SE};
+ } else if (segmentsX.size() == 1 && segmentsY.size() > 1) {
+ yield new Direction[]{Direction.NE, Direction.SW};
+ }
+ throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray()));
+ }
+ case L_SHAPE -> {
+ if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.firstInt()))) {
+ yield new Direction[]{Direction.SW};
+ } else if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.lastInt()))) {
+ yield new Direction[]{Direction.SE};
+ } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.firstInt()))) {
+ yield new Direction[]{Direction.NW};
+ } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.lastInt()))) {
+ yield new Direction[]{Direction.NE};
+ }
+ throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray()));
+ }
+ };
+ }
+
+ /**
+ * Updates the room.
+ * <p></p>
+ * This method returns immediately if any of the following conditions are met:
+ * <ul>
+ * <li> The room does not need to be scanned and matched. (When the room is not of type {@link Type.ROOM}, {@link Type.PUZZLE}, or {@link Type.TRAP}. See {@link Type#needsScanning()}) </li>
+ * <li> The room has been matched or failed to match and is on cooldown. See {@link #matched}. </li>
+ * <li> {@link #findRoom The previous update} has not completed. </li>
+ * </ul>
+ * Then this method tries to match this room through:
+ * <ul>
+ * <li> Iterate over a 11 by 11 by 11 box around the player. </li>
+ * <li> Check it the block is part of this room and not part of a doorway. See {@link #segments} and {@link #notInDoorway(BlockPos)}. </li>
+ * <li> Checks if the position has been checked and adds it to {@link #checkedBlocks}. </li>
+ * <li> Calls {@link #checkBlock(ClientWorld, BlockPos)} </li>
+ * </ul>
+ */
+ @SuppressWarnings("JavadocReference")
+ protected void update() {
+ // Logical AND has higher precedence than logical OR
+ if (!type.needsScanning() || matched != TriState.DEFAULT || !DungeonSecrets.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) {
+ return;
+ }
+ MinecraftClient client = MinecraftClient.getInstance();
+ ClientPlayerEntity player = client.player;
+ ClientWorld world = client.world;
+ if (player == null || world == null) {
+ return;
+ }
+ findRoom = CompletableFuture.runAsync(() -> {
+ for (BlockPos pos : BlockPos.iterate(player.getBlockPos().add(-5, -5, -5), player.getBlockPos().add(5, 5, 5))) {
+ if (segments.contains(DungeonMapUtils.getPhysicalRoomPos(pos)) && notInDoorway(pos) && checkedBlocks.add(pos) && checkBlock(world, pos)) {
+ break;
+ }
+ }
+ });
+ }
+
+ private static boolean notInDoorway(BlockPos pos) {
+ if (pos.getY() < 66 || pos.getY() > 73) {
+ return true;
+ }
+ int x = MathHelper.floorMod(pos.getX() - 8, 32);
+ int z = MathHelper.floorMod(pos.getZ() - 8, 32);
+ return (x < 13 || x > 17 || z > 2 && z < 28) && (z < 13 || z > 17 || x > 2 && x < 28);
+ }
+
+ /**
+ * Filters out dungeon rooms which does not contain the block at the given position.
+ * <p></p>
+ * This method:
+ * <ul>
+ * <li> Checks if the block type is included in the dungeon rooms data. See {@link DungeonSecrets#NUMERIC_ID}. </li>
+ * <li> For each possible direction: </li>
+ * <ul>
+ * <li> Rotate and convert the position to a relative position. See {@link DungeonMapUtils#actualToRelative(Direction, Vector2ic, BlockPos)}. </li>
+ * <li> Encode the block based on the relative position and the custom numeric block id. See {@link #posIdToInt(BlockPos, byte)}. </li>
+ * <li> For each possible room in the current direction: </li>
+ * <ul>
+ * <li> Check if {@link #roomsData} contains the encoded block. </li>
+ * <li> If so, add the room to the new list of possible rooms for this direction. </li>
+ * </ul>
+ * <li> Replace the old possible room list for the current direction with the new one. </li>
+ * </ul>
+ * <li> If there are no matching rooms left: </li>
+ * <ul>
+ * <li> Terminate matching by setting {@link #matched} to {@link TriState#FALSE}. </li>
+ * <li> Schedule another matching attempt in 50 ticks (2.5 seconds). </li>
+ * <li> Reset {@link #possibleRooms} and {@link #checkedBlocks} with {@link #reset()}. </li>
+ * <li> Return {@code true} </li>
+ * </ul>
+ * <li> If there are exactly one room matching: </li>
+ * <ul>
+ * <li> Call {@link #roomMatched(String, Direction, Vector2ic)}. </li>
+ * <li> Discard the no longer needed fields to save memory. </li>
+ * <li> Return {@code true} </li>
+ * </ul>
+ * <li> Return {@code false} </li>
+ * </ul>
+ *
+ * @param world the world to get the block from
+ * @param pos the position of the block to check
+ * @return whether room matching should end. Either a match is found or there are no valid rooms left
+ */
+ private boolean checkBlock(ClientWorld world, BlockPos pos) {
+ byte id = DungeonSecrets.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString());
+ if (id == 0) {
+ return false;
+ }
+ for (MutableTriple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) {
+ int block = posIdToInt(DungeonMapUtils.actualToRelative(directionRooms.getLeft(), directionRooms.getMiddle(), pos), id);
+ List<String> possibleDirectionRooms = new ArrayList<>();
+ for (String room : directionRooms.getRight()) {
+ if (Arrays.binarySearch(roomsData.get(room), block) >= 0) {
+ possibleDirectionRooms.add(room);
+ }
+ }
+ directionRooms.setRight(possibleDirectionRooms);
+ }
+
+ int matchingRoomsSize = possibleRooms.stream().map(Triple::getRight).mapToInt(Collection::size).sum();
+ if (matchingRoomsSize == 0) {
+ // If no rooms match, reset the fields and scan again after 50 ticks.
+ matched = TriState.FALSE;
+ DungeonSecrets.LOGGER.warn("[Skyblocker] No dungeon room matches after checking {} block(s)", checkedBlocks.size());
+ SkyblockerMod.getInstance().scheduler.schedule(() -> matched = TriState.DEFAULT, 50);
+ reset();
+ return true;
+ } else if (matchingRoomsSize == 1 && ++doubleCheckBlocks >= 10) {
+ // If one room matches, load the secrets for that room and discard the no longer needed fields.
+ for (Triple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) {
+ if (directionRooms.getRight().size() == 1) {
+ roomMatched(directionRooms.getRight().get(0), directionRooms.getLeft(), directionRooms.getMiddle());
+ discard();
+ return true;
+ }
+ }
+ return false; // This should never happen, we just checked that there is one possible room, and the return true in the loop should activate
+ } else {
+ DungeonSecrets.LOGGER.debug("[Skyblocker] {} room(s) remaining after checking {} block(s)", matchingRoomsSize, checkedBlocks.size());
+ return false;
+ }
+ }
+
+ /**
+ * Encodes a {@link BlockPos} and the custom numeric block id into an integer.
+ *
+ * @param pos the position of the block
+ * @param id the custom numeric block id
+ * @return the encoded integer
+ */
+ private int posIdToInt(BlockPos pos, byte id) {
+ return pos.getX() << 24 | pos.getY() << 16 | pos.getZ() << 8 | id;
+ }
+
+ /**
+ * Loads the secret waypoints for the room from {@link DungeonSecrets#waypointsJson} once it has been matched
+ * and sets {@link #matched} to {@link TriState#TRUE}.
+ *
+ * @param directionRooms the direction, position, and name of the room
+ */
+ @SuppressWarnings("JavadocReference")
+ private void roomMatched(String name, Direction direction, Vector2ic physicalCornerPos) {
+ Table<Integer, BlockPos, SecretWaypoint> secretWaypointsMutable = HashBasedTable.create();
+ for (JsonElement waypointElement : DungeonSecrets.getRoomWaypoints(name)) {
+ JsonObject waypoint = waypointElement.getAsJsonObject();
+ String secretName = waypoint.get("secretName").getAsString();
+ int secretIndex = Integer.parseInt(secretName.substring(0, Character.isDigit(secretName.charAt(1)) ? 2 : 1));
+ BlockPos pos = DungeonMapUtils.relativeToActual(direction, physicalCornerPos, waypoint);
+ secretWaypointsMutable.put(secretIndex, pos, new SecretWaypoint(secretIndex, waypoint, secretName, pos));
+ }
+ secretWaypoints = ImmutableTable.copyOf(secretWaypointsMutable);
+ matched = TriState.TRUE;
+ DungeonSecrets.LOGGER.info("[Skyblocker] Room {} matched after checking {} block(s)", name, checkedBlocks.size());
+ }
+
+ /**
+ * Resets fields for another round of matching after room matching fails.
+ */
+ private void reset() {
+ IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray()));
+ IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray()));
+ possibleRooms = getPossibleRooms(segmentsX, segmentsY);
+ checkedBlocks = new HashSet<>();
+ doubleCheckBlocks = 0;
+ }
+
+ /**
+ * Discards fields after room matching completes when a room is found.
+ * These fields are no longer needed and are discarded to save memory.
+ */
+ private void discard() {
+ roomsData = null;
+ possibleRooms = null;
+ checkedBlocks = null;
+ doubleCheckBlocks = 0;
+ }
+
+ /**
+ * Calls {@link SecretWaypoint#render(WorldRenderContext)} on {@link #secretWaypoints all secret waypoints}.
+ */
+ protected void render(WorldRenderContext context) {
+ for (SecretWaypoint secretWaypoint : secretWaypoints.values()) {
+ if (secretWaypoint.shouldRender()) {
+ secretWaypoint.render(context);
+ }
+ }
+ }
+
+ /**
+ * Sets all secrets as found if {@link #isAllSecretsFound(String)}.
+ */
+ protected void onChatMessage(String message) {
+ if (isAllSecretsFound(message)) {
+ secretWaypoints.values().forEach(SecretWaypoint::setFound);
+ }
+ }
+
+ /**
+ * Checks if the number of found secrets is equals or greater than the total number of secrets in the room.
+ *
+ * @param message the message to check in
+ * @return whether the number of found secrets is equals or greater than the total number of secrets in the room
+ */
+ protected static boolean isAllSecretsFound(String message) {
+ Matcher matcher = SECRETS.matcher(message);
+ if (matcher.find()) {
+ return Integer.parseInt(matcher.group(1)) >= Integer.parseInt(matcher.group(2));
+ }
+ return false;
+ }
+
+ /**
+ * Marks the secret at the interaction position as found when the player interacts with a chest or a player head,
+ * if there is a secret at the interaction position.
+ *
+ * @param world the world to get the block from
+ * @param hitResult the block being interacted with
+ * @see #onSecretFound(SecretWaypoint, String, Object...)
+ */
+ protected void onUseBlock(World world, BlockHitResult hitResult) {
+ BlockState state = world.getBlockState(hitResult.getBlockPos());
+ if (state.isOf(Blocks.CHEST) || state.isOf(Blocks.PLAYER_HEAD) || state.isOf(Blocks.PLAYER_WALL_HEAD)) {
+ secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::needsInteraction).findAny()
+ .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex));
+ } else if (state.isOf(Blocks.LEVER)) {
+ secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::isLever).forEach(SecretWaypoint::setFound);
+ }
+ }
+
+ /**
+ * Marks the closest secret no greater than 6 blocks away as found when the player picks up a secret item.
+ *
+ * @param itemEntity the item entity being picked up
+ * @param collector the collector of the item
+ * @see #onSecretFound(SecretWaypoint, String, Object...)
+ */
+ protected void onItemPickup(ItemEntity itemEntity, LivingEntity collector) {
+ if (SecretWaypoint.SECRET_ITEMS.stream().noneMatch(itemEntity.getStack().getName().getString()::contains)) {
+ return;
+ }
+ secretWaypoints.values().stream().filter(SecretWaypoint::needsItemPickup).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(collector))).filter(SecretWaypoint.getRangePredicate(collector))
+ .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} picked up a {} from a {} secret, setting secret #{} as found", collector.getName().getString(), itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex));
+ }
+
+ /**
+ * Marks all secret waypoints with the same index as the given {@link SecretWaypoint} as found.
+ *
+ * @param secretWaypoint the secret waypoint to read the index from.
+ * @param msg the message to log
+ * @param args the args for the {@link org.slf4j.Logger#info(String, Object...) Logger#info(String, Object...)} call
+ */
+ private void onSecretFound(SecretWaypoint secretWaypoint, String msg, Object... args) {
+ secretWaypoints.row(secretWaypoint.secretIndex).values().forEach(SecretWaypoint::setFound);
+ DungeonSecrets.LOGGER.info(msg, args);
+ }
+
+ protected boolean markSecrets(int secretIndex, boolean found) {
+ Map<BlockPos, SecretWaypoint> secret = secretWaypoints.row(secretIndex);
+ if (secret.isEmpty()) {
+ return false;
+ } else {
+ secret.values().forEach(found ? SecretWaypoint::setFound : SecretWaypoint::setMissing);
+ return true;
+ }
+ }
+
+ public enum Type {
+ ENTRANCE(MapColor.DARK_GREEN.getRenderColorByte(MapColor.Brightness.HIGH)),
+ ROOM(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.LOWEST)),
+ PUZZLE(MapColor.MAGENTA.getRenderColorByte(MapColor.Brightness.HIGH)),
+ TRAP(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.HIGH)),
+ MINIBOSS(MapColor.YELLOW.getRenderColorByte(MapColor.Brightness.HIGH)),
+ FAIRY(MapColor.PINK.getRenderColorByte(MapColor.Brightness.HIGH)),
+ BLOOD(MapColor.BRIGHT_RED.getRenderColorByte(MapColor.Brightness.HIGH)),
+ UNKNOWN(MapColor.GRAY.getRenderColorByte(MapColor.Brightness.NORMAL));
+ final byte color;
+
+ Type(byte color) {
+ this.color = color;
+ }
+
+ /**
+ * @return whether this room type has secrets and needs to be scanned and matched.
+ */
+ private boolean needsScanning() {
+ return switch (this) {
+ case ROOM, PUZZLE, TRAP -> true;
+ default -> false;
+ };
+ }
+ }
+
+ private enum Shape {
+ ONE_BY_ONE("1x1"),
+ ONE_BY_TWO("1x2"),
+ ONE_BY_THREE("1x3"),
+ ONE_BY_FOUR("1x4"),
+ L_SHAPE("L-shape"),
+ TWO_BY_TWO("2x2");
+ final String shape;
+
+ Shape(String shape) {
+ this.shape = shape;
+ }
+
+ @Override
+ public String toString() {
+ return shape;
+ }
+ }
+
+ public enum Direction {
+ NW, NE, SW, SE
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
new file mode 100644
index 00000000..edf18f85
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
@@ -0,0 +1,132 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
+
+import com.google.gson.JsonObject;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.Entity;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.function.ToDoubleFunction;
+
+public class SecretWaypoint {
+ static final List<String> SECRET_ITEMS = List.of("Decoy", "Defuse Kit", "Dungeon Chest Key", "Healing VIII", "Inflatable Jerry", "Spirit Leap", "Training Weights", "Trap", "Treasure Talisman");
+ final int secretIndex;
+ final Category category;
+ private final Text name;
+ private final BlockPos pos;
+ private final Vec3d centerPos;
+ private boolean missing;
+
+ SecretWaypoint(int secretIndex, JsonObject waypoint, String name, BlockPos pos) {
+ this.secretIndex = secretIndex;
+ this.category = Category.get(waypoint);
+ this.name = Text.of(name);
+ this.pos = pos;
+ this.centerPos = pos.toCenterPos();
+ this.missing = true;
+ }
+
+ static ToDoubleFunction<SecretWaypoint> getSquaredDistanceToFunction(Entity entity) {
+ return secretWaypoint -> entity.squaredDistanceTo(secretWaypoint.centerPos);
+ }
+
+ static Predicate<SecretWaypoint> getRangePredicate(Entity entity) {
+ return secretWaypoint -> entity.squaredDistanceTo(secretWaypoint.centerPos) <= 36D;
+ }
+
+ boolean shouldRender() {
+ return category.isEnabled() && missing;
+ }
+
+ boolean needsInteraction() {
+ return category.needsInteraction();
+ }
+
+ boolean isLever() {
+ return category.isLever();
+ }
+
+ boolean needsItemPickup() {
+ return category.needsItemPickup();
+ }
+
+ void setFound() {
+ this.missing = false;
+ }
+
+ void setMissing() {
+ this.missing = true;
+ }
+
+ /**
+ * Renders the secret waypoint, including a filled cube, a beacon beam, the name, and the distance from the player.
+ */
+ void render(WorldRenderContext context) {
+ RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, pos, category.colorComponents, 0.5F);
+ Vec3d posUp = centerPos.add(0, 1, 0);
+ RenderHelper.renderText(context, name, posUp, true);
+ double distance = context.camera().getPos().distanceTo(centerPos);
+ RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, 1, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true);
+ }
+
+ enum Category {
+ ENTRANCE(secretWaypoints -> secretWaypoints.enableEntranceWaypoints, 0, 255, 0),
+ SUPERBOOM(secretWaypoints -> secretWaypoints.enableSuperboomWaypoints, 255, 0, 0),
+ CHEST(secretWaypoints -> secretWaypoints.enableChestWaypoints, 2, 213, 250),
+ ITEM(secretWaypoints -> secretWaypoints.enableItemWaypoints, 2, 64, 250),
+ BAT(secretWaypoints -> secretWaypoints.enableBatWaypoints, 142, 66, 0),
+ WITHER(secretWaypoints -> secretWaypoints.enableWitherWaypoints, 30, 30, 30),
+ LEVER(secretWaypoints -> secretWaypoints.enableLeverWaypoints, 250, 217, 2),
+ FAIRYSOUL(secretWaypoints -> secretWaypoints.enableFairySoulWaypoints, 255, 85, 255),
+ STONK(secretWaypoints -> secretWaypoints.enableStonkWaypoints, 146, 52, 235),
+ DEFAULT(secretWaypoints -> secretWaypoints.enableDefaultWaypoints, 190, 255, 252);
+ private final Predicate<SkyblockerConfig.SecretWaypoints> enabledPredicate;
+ private final float[] colorComponents;
+
+ Category(Predicate<SkyblockerConfig.SecretWaypoints> enabledPredicate, int... intColorComponents) {
+ this.enabledPredicate = enabledPredicate;
+ colorComponents = new float[intColorComponents.length];
+ for (int i = 0; i < intColorComponents.length; i++) {
+ colorComponents[i] = intColorComponents[i] / 255F;
+ }
+ }
+
+ private static Category get(JsonObject categoryJson) {
+ return switch (categoryJson.get("category").getAsString()) {
+ case "entrance" -> Category.ENTRANCE;
+ case "superboom" -> Category.SUPERBOOM;
+ case "chest" -> Category.CHEST;
+ case "item" -> Category.ITEM;
+ case "bat" -> Category.BAT;
+ case "wither" -> Category.WITHER;
+ case "lever" -> Category.LEVER;
+ case "fairysoul" -> Category.FAIRYSOUL;
+ case "stonk" -> Category.STONK;
+ default -> Category.DEFAULT;
+ };
+ }
+
+ boolean needsInteraction() {
+ return this == CHEST || this == WITHER;
+ }
+
+ boolean isLever() {
+ return this == LEVER;
+ }
+
+ boolean needsItemPickup() {
+ return this == ITEM || this == BAT;
+ }
+
+ boolean isEnabled() {
+ return enabledPredicate.test(SkyblockerConfig.get().locations.dungeons.secretWaypoints);
+ }
+ }
+}
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 5d6df319..3563741c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
@@ -1,7 +1,8 @@
package me.xmrvizzy.skyblocker.skyblock.dwarven;
+import it.unimi.dsi.fastutil.ints.IntIntPair;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CommsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.hud.HudCommsWidget;
import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
@@ -20,11 +21,9 @@ import java.util.stream.Stream;
public class DwarvenHud {
-
public static final MinecraftClient client = MinecraftClient.getInstance();
public static List<Commission> commissionList = new ArrayList<>();
-
public static final List<Pattern> COMMISSIONS = Stream.of(
"(?:Titanium|Mithril|Hard Stone) Miner",
"(?:Ice Walker|Goblin|Goblin Raid|Automaton|Sludge|Team Treasurite Member|Yog|Boss Corleone|Thyst) Slayer",
@@ -37,8 +36,7 @@ public class DwarvenHud {
"2x Mithril Powder Collector",
"(?:Ruby|Amber|Sapphire|Jade|Amethyst|Topaz) Gemstone Collector",
"(?:Amber|Sapphire|Jade|Amethyst|Topaz) Crystal Hunter",
- "Chest Looter"
- ).map(s -> Pattern.compile("^.*(" + s + "): (\\d+\\.?\\d*%|DONE)"))
+ "Chest Looter").map(s -> Pattern.compile("^.*(" + s + "): (\\d+\\.?\\d*%|DONE)"))
.collect(Collectors.toList());
public static void init() {
@@ -54,15 +52,34 @@ public class DwarvenHud {
|| commissionList.isEmpty()) {
return;
}
- render(context, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.x, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.y, commissionList);
+ render(HudCommsWidget.INSTANCE, context, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.x,
+ SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.y, commissionList);
});
}
- public static void render(DrawContext context, int hudX, int hudY, List<Commission> commissions) {
+ public static IntIntPair getDimForConfig(List<Commission> commissions) {
+ return switch (SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.style) {
+ case SIMPLE -> {
+ HudCommsWidget.INSTANCE_CFG.updateData(commissions, false);
+ yield IntIntPair.of(
+ HudCommsWidget.INSTANCE_CFG.getWidth(),
+ HudCommsWidget.INSTANCE_CFG.getHeight());
+ }
+ case FANCY -> {
+ HudCommsWidget.INSTANCE_CFG.updateData(commissions, true);
+ yield IntIntPair.of(
+ HudCommsWidget.INSTANCE_CFG.getWidth(),
+ HudCommsWidget.INSTANCE_CFG.getHeight());
+ }
+ default -> IntIntPair.of(200, 20 * commissions.size());
+ };
+ }
+
+ public static void render(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List<Commission> commissions) {
switch (SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.style) {
- case SIMPLE -> renderSimple(context, hudX, hudY, commissions);
- case FANCY -> renderFancy(context, hudX, hudY, commissions);
+ case SIMPLE -> renderSimple(hcw, context, hudX, hudY, commissions);
+ case FANCY -> renderFancy(hcw, context, hudX, hudY, commissions);
case CLASSIC -> renderClassic(context, hudX, hudY, commissions);
}
}
@@ -85,23 +102,28 @@ public class DwarvenHud {
}
}
- public static void renderSimple(DrawContext context, int hudX, int hudY, List<Commission> commissions) {
- CommsWidget cw = new CommsWidget(commissions, false);
- cw.setX(hudX);
- cw.setY(hudY);
- cw.render(context, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground);
+ public static void renderSimple(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List<Commission> commissions) {
+ hcw.updateData(commissions, false);
+ hcw.update();
+ hcw.setX(hudX);
+ hcw.setY(hudY);
+ hcw.render(context,
+ SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground);
}
- public static void renderFancy(DrawContext context, int hudX, int hudY, List<Commission> commissions) {
- CommsWidget cw = new CommsWidget(commissions, true);
- cw.setX(hudX);
- cw.setY(hudY);
- cw.render(context, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground);
+ public static void renderFancy(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List<Commission> commissions) {
+ hcw.updateData(commissions, true);
+ hcw.update();
+ hcw.setX(hudX);
+ hcw.setY(hudY);
+ hcw.render(context,
+ SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground);
}
public static void update() {
commissionList = new ArrayList<>();
- if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enabled) return;
+ if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enabled)
+ return;
client.getNetworkHandler().getPlayerList().forEach(playerListEntry -> {
if (playerListEntry.getDisplayName() != null) {
@@ -116,7 +138,7 @@ public class DwarvenHud {
});
}
- // steamroller tactics to get visibility from outside classes (CommsWidget)
+ // steamroller tactics to get visibility from outside classes (HudCommsWidget)
public record Commission(String commission, String progression) {
}
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java
index 3ab08bfb..35a296fb 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java
@@ -1,7 +1,10 @@
package me.xmrvizzy.skyblocker.skyblock.dwarven;
+import it.unimi.dsi.fastutil.ints.IntIntPair;
import me.shedaniel.autoconfig.AutoConfig;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud.Commission;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.hud.HudCommsWidget;
import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
@@ -12,6 +15,8 @@ import java.util.List;
public class DwarvenHudConfigScreen extends Screen {
+ private static final List<Commission> CFG_COMMS = List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%"));
+
private int hudX = SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.x;
private int hudY = SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.y;
@@ -23,15 +28,16 @@ public class DwarvenHudConfigScreen extends Screen {
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
renderBackground(context);
- DwarvenHud.render(context, hudX, hudY, List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%")));
+ DwarvenHud.render(HudCommsWidget.INSTANCE_CFG, context, hudX, hudY, List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%")));
context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, height / 2, Color.GRAY.getRGB());
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
+ IntIntPair dims = DwarvenHud.getDimForConfig(CFG_COMMS);
if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + 200, hudY + 40) && button == 0) {
- hudX = (int) Math.max(Math.min(mouseX - 100, this.width - 200), 0);
- hudY = (int) Math.max(Math.min(mouseY - 20, this.height - 40), 0);
+ hudX = (int) Math.max(Math.min(mouseX - (double) dims.leftInt() / 2, this.width - dims.leftInt()), 0);
+ hudY = (int) Math.max(Math.min(mouseY - (double) dims.rightInt() / 2, this.height - dims.rightInt()), 0);
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
@@ -39,8 +45,9 @@ public class DwarvenHudConfigScreen extends Screen {
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (button == 1) {
- hudX = this.width / 2 - 100;
- hudY = this.height / 2 - 20;
+ IntIntPair dims = DwarvenHud.getDimForConfig(CFG_COMMS);
+ hudX = this.width / 2 - dims.leftInt();
+ hudY = this.height / 2 - dims.rightInt();
}
return super.mouseClicked(mouseX, mouseY, button);
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java
index ffc3f67d..6c648da9 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java
@@ -42,7 +42,10 @@ public class CustomArmorTrims {
private static void initializeTrimCache() {
ClientPlayerEntity player = MinecraftClient.getInstance().player;
- if (!trimsInitialized && player != null) {
+ if (trimsInitialized || player == null) {
+ return;
+ }
+ try {
TRIMS_CACHE.clear();
DynamicRegistryManager registryManager = player.networkHandler.getRegistryManager();
for (Identifier material : registryManager.get(RegistryKeys.TRIM_MATERIAL).getIds()) {
@@ -62,6 +65,8 @@ public class CustomArmorTrims {
LOGGER.info("[Skyblocker] Successfully cached all armor trims!");
trimsInitialized = true;
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Encountered an exception while caching armor trims", e);
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
index 1d58435e..351cef48 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
@@ -18,8 +18,8 @@ public class QuickNav {
private static final String dungeonHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}}";
public static void init() {
- ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
- if (Utils.isOnSkyblock() && SkyblockerConfig.get().quickNav.enableQuickNav && screen instanceof HandledScreen<?>) {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().quickNav.enableQuickNav && screen instanceof HandledScreen<?> && client.player != null && !client.player.isCreative()) {
String screenTitle = screen.getTitle().getString().trim();
List<QuickNavButton> buttons = QuickNav.init(screenTitle);
for (QuickNavButton button : buttons) Screens.getButtons(screen).add(button);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java
index 6d90b269..8f18e367 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java
@@ -10,8 +10,8 @@ import net.minecraft.client.util.InputUtil;
public class TabHud {
- public static KeyBinding playerTgl;
- public static KeyBinding genericTgl;
+ public static KeyBinding toggleB;
+ public static KeyBinding toggleA;
// public static KeyBinding mapTgl;
public static KeyBinding defaultTgl;
@@ -19,21 +19,16 @@ public class TabHud {
public static void init() {
- playerTgl = KeyBindingHelper.registerKeyBinding(
- new KeyBinding("key.skyblocker.playerTgl",
+ toggleB = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyblocker.toggleB",
InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_B,
"key.categories.skyblocker"));
- genericTgl = KeyBindingHelper.registerKeyBinding(
- new KeyBinding("key.skyblocker.genericTgl",
+ toggleA = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyblocker.toggleA",
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,
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java
new file mode 100644
index 00000000..084a6ffd
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java
@@ -0,0 +1,181 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder;
+
+import java.io.BufferedReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.AlignStage;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.CollideStage;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.PipelineStage;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.PlaceStage;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.StackStage;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonPlayerWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ErrorWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EventWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.util.Identifier;
+
+public class ScreenBuilder {
+
+ // layout pipeline
+ private final ArrayList<PipelineStage> layoutPipeline = new ArrayList<>();
+
+ // all widget instances this builder knows
+ private final ArrayList<Widget> instances = new ArrayList<>();
+ // maps alias -> widget instance
+ private final HashMap<String, Widget> objectMap = new HashMap<>();
+
+ private final String builderName;
+
+ /**
+ * Create a ScreenBuilder from a json.
+ */
+ public ScreenBuilder(Identifier ident) {
+
+ try (BufferedReader reader = MinecraftClient.getInstance().getResourceManager().openAsReader(ident)) {
+ this.builderName = ident.getPath();
+
+ JsonObject json = JsonParser.parseReader(reader).getAsJsonObject();
+
+ JsonArray widgets = json.getAsJsonArray("widgets");
+ JsonArray layout = json.getAsJsonArray("layout");
+
+ for (JsonElement w : widgets) {
+ JsonObject widget = w.getAsJsonObject();
+ String name = widget.get("name").getAsString();
+ String alias = widget.get("alias").getAsString();
+
+ Widget wid = instanceFrom(name, widget);
+ objectMap.put(alias, wid);
+ instances.add(wid);
+ }
+
+ for (JsonElement l : layout) {
+ PipelineStage ps = createStage(l.getAsJsonObject());
+ layoutPipeline.add(ps);
+ }
+ } catch (Exception ex) {
+ // rethrow as unchecked exception so that I don't have to catch anything in the ScreenMaster
+ throw new IllegalStateException("Failed to load file " + ident + ". Reason: " + ex.getMessage());
+ }
+ }
+
+ /**
+ * Try to find a class in the widget package that has the supplied name and
+ * call it's constructor. Manual work is required if the class has arguments.
+ */
+ public Widget instanceFrom(String name, JsonObject widget) {
+
+ // do widgets that require args the normal way
+ JsonElement arg;
+ switch (name) {
+ case "EventWidget" -> {
+ return new EventWidget(widget.get("inGarden").getAsBoolean());
+ }
+ case "DungeonPlayerWidget" -> {
+ return new DungeonPlayerWidget(widget.get("player").getAsInt());
+ }
+ case "ErrorWidget" -> {
+ arg = widget.get("text");
+ if (arg == null) {
+ return new ErrorWidget();
+ } else {
+ return new ErrorWidget(arg.getAsString());
+ }
+ }
+ case "Widget" ->
+ // clown case sanity check. don't instantiate the superclass >:|
+ throw new NoSuchElementException(builderName + "[ERROR]: No such Widget type \"Widget\"!");
+ }
+
+ // reflect something together for the "normal" ones.
+ // TODO don't get package list for every widget; do it once and cache.
+ // fine for now, as this would only shorten the load time anyways
+
+ // list all packages that might contain widget classes
+ // using Package isn't reliable, as some classes might not be loaded yet,
+ // causing the packages not to show.
+ String packbase = "me.xmrvizzy.skyblocker.skyblock.tabhud.widget";
+ String[] packnames = {
+ packbase,
+ packbase + ".rift"
+ };
+
+ // construct the full class name and try to load.
+ Class<?> clazz = null;
+ for (String pn : packnames) {
+ try {
+ clazz = Class.forName(pn + "." + name);
+ } catch (LinkageError | ClassNotFoundException ex) {
+ continue;
+ }
+ }
+
+ // load failed.
+ if (clazz == null) {
+ throw new NoSuchElementException(builderName + "/[ERROR]: No such Widget type \"" + name + "\"!");
+ }
+
+ // return instance of that class.
+ try {
+ Constructor<?> ctor = clazz.getConstructor();
+ return (Widget) ctor.newInstance();
+ } catch (NoSuchMethodException | InstantiationException | IllegalAccessException
+ | IllegalArgumentException | InvocationTargetException | SecurityException ex) {
+ throw new IllegalStateException(builderName + "/" + name + ": Internal error...");
+ }
+ }
+
+ /**
+ * Create a PipelineStage from a json object.
+ */
+ public PipelineStage createStage(JsonObject descr) throws NoSuchElementException {
+
+ String op = descr.get("op").getAsString();
+
+ return switch (op) {
+ case "place" -> new PlaceStage(this, descr);
+ case "stack" -> new StackStage(this, descr);
+ case "align" -> new AlignStage(this, descr);
+ case "collideAgainst" -> new CollideStage(this, descr);
+ default -> throw new NoSuchElementException("No such op " + op + " as requested by " + this.builderName);
+ };
+ }
+
+ /**
+ * Lookup Widget instance from alias name
+ */
+ public Widget getInstance(String name) {
+ if (!this.objectMap.containsKey(name)) {
+ throw new NoSuchElementException("No widget with alias " + name + " in screen " + builderName);
+ }
+ return this.objectMap.get(name);
+ }
+
+ /**
+ * Run the pipeline to build a Screen
+ */
+ public void run(DrawContext context, int screenW, int screenH) {
+
+ for (Widget w : instances) {
+ w.update();
+ }
+ for (PipelineStage ps : layoutPipeline) {
+ ps.run(screenW, screenH);
+ }
+ for (Widget w : instances) {
+ w.render(context);
+ }
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java
new file mode 100644
index 00000000..194ab3d4
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java
@@ -0,0 +1,144 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder;
+
+import java.io.BufferedReader;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerLocator;
+import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
+import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
+import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.resource.Resource;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.resource.ResourceType;
+import net.minecraft.util.Identifier;
+
+public class ScreenMaster {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger("skyblocker");
+
+ private static final int VERSION = 1;
+
+ private static final HashMap<String, ScreenBuilder> standardMap = new HashMap<>();
+ private static final HashMap<String, ScreenBuilder> screenAMap = new HashMap<>();
+ private static final HashMap<String, ScreenBuilder> screenBMap = new HashMap<>();
+
+ /**
+ * Load a screen mapping from an identifier
+ */
+ public static void load(Identifier ident) {
+
+ String path = ident.getPath();
+ String[] parts = path.split("/");
+ String screenType = parts[parts.length - 2];
+ String location = parts[parts.length - 1];
+ location = location.replace(".json", "");
+
+ ScreenBuilder sb = new ScreenBuilder(ident);
+ switch (screenType) {
+ case "standard" -> standardMap.put(location, sb);
+ case "screen_a" -> screenAMap.put(location, sb);
+ case "screen_b" -> screenBMap.put(location, sb);
+ }
+ }
+
+ /**
+ * Top level render method.
+ * Calls the appropriate ScreenBuilder with the screen's dimensions
+ */
+ public static void render(DrawContext context, int w, int h) {
+ String location = PlayerLocator.getPlayerLocation().internal;
+ HashMap<String, ScreenBuilder> lookup;
+ if (TabHud.toggleA.isPressed()) {
+ lookup = screenAMap;
+ } else if (TabHud.toggleB.isPressed()) {
+ lookup = screenBMap;
+ } else {
+ lookup = standardMap;
+ }
+
+ ScreenBuilder sb = lookup.get(location);
+ // seems suboptimal, maybe load the default first into all possible values
+ // and then override?
+ if (sb == null) {
+ sb = lookup.get("default");
+ }
+
+ sb.run(context, w, h);
+
+ }
+
+ public static void init() {
+
+ // WHY MUST IT ALWAYS BE SUCH NESTED GARBAGE MINECRAFT KEEP THAT IN DFU FFS
+
+ FabricLoader.getInstance()
+ .getModContainer("skyblocker")
+ .ifPresent(container -> ResourceManagerHelper.registerBuiltinResourcePack(
+ new Identifier("skyblocker", "top_aligned"),
+ container,
+ ResourcePackActivationType.NORMAL));
+
+ ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(
+ // ...why are we instantiating an interface again?
+ new SimpleSynchronousResourceReloadListener() {
+ @Override
+ public Identifier getFabricId() {
+ return new Identifier("skyblocker", "tabhud");
+ }
+
+ @Override
+ public void reload(ResourceManager manager) {
+
+ standardMap.clear();
+ screenAMap.clear();
+ screenBMap.clear();
+
+ int excnt = 0;
+
+ for (Map.Entry<Identifier, Resource> entry : manager
+ .findResources("tabhud", path -> path.getPath().endsWith("version.json"))
+ .entrySet()) {
+
+ try (BufferedReader reader = MinecraftClient.getInstance().getResourceManager()
+ .openAsReader(entry.getKey())) {
+ JsonObject json = JsonParser.parseReader(reader).getAsJsonObject();
+ if (json.get("format_version").getAsInt() != VERSION) {
+ throw new IllegalStateException(String.format("Resource pack isn't compatible! Expected version %d, got %d", VERSION, json.get("format_version").getAsInt()));
+ }
+
+ } catch (Exception ex) {
+ throw new IllegalStateException(
+ "Rejected this resource pack. Reason: " + ex.getMessage());
+ }
+ }
+
+ for (Map.Entry<Identifier, Resource> entry : manager
+ .findResources("tabhud", path -> path.getPath().endsWith(".json") && !path.getPath().endsWith("version.json"))
+ .entrySet()) {
+ try {
+
+ load(entry.getKey());
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage());
+ excnt++;
+ }
+ }
+ if (excnt > 0) {
+ throw new IllegalStateException("This screen definition isn't valid, see above");
+ }
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java
new file mode 100644
index 00000000..76789a4c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java
@@ -0,0 +1,83 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+import com.google.gson.JsonObject;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.ScreenConst;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+
+public class AlignStage extends PipelineStage {
+
+ private enum AlignReference {
+ HORICENT("horizontalCenter"),
+ VERTCENT("verticalCenter"),
+ LEFTCENT("leftOfCenter"),
+ RIGHTCENT("rightOfCenter"),
+ TOPCENT("topOfCenter"),
+ BOTCENT("botOfCenter"),
+ TOP("top"),
+ BOT("bot"),
+ LEFT("left"),
+ RIGHT("right");
+
+ private final String str;
+
+ AlignReference(String d) {
+ this.str = d;
+ }
+
+ public static AlignReference parse(String s) throws NoSuchElementException {
+ for (AlignReference d : AlignReference.values()) {
+ if (d.str.equals(s)) {
+ return d;
+ }
+ }
+ throw new NoSuchElementException("\"" + s + "\" is not a valid reference for an align op!");
+ }
+ }
+
+ private final AlignReference reference;
+
+ public AlignStage(ScreenBuilder builder, JsonObject descr) {
+ this.reference = AlignReference.parse(descr.get("reference").getAsString());
+ this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to")
+ .asList()
+ .stream()
+ .map(x -> builder.getInstance(x.getAsString()))
+ .toList());
+ }
+
+ public void run(int screenW, int screenH) {
+ int wHalf, hHalf;
+ for (Widget wid : primary) {
+ switch (this.reference) {
+ case HORICENT -> wid.setX((screenW - wid.getWidth()) / 2);
+ case VERTCENT -> wid.setY((screenH - wid.getHeight()) / 2);
+ case LEFTCENT -> {
+ wHalf = screenW / 2;
+ wid.setX(wHalf - ScreenConst.WIDGET_PAD_HALF - wid.getWidth());
+ }
+ case RIGHTCENT -> {
+ wHalf = screenW / 2;
+ wid.setX(wHalf + ScreenConst.WIDGET_PAD_HALF);
+ }
+ case TOPCENT -> {
+ hHalf = screenH / 2;
+ wid.setY(hHalf - ScreenConst.WIDGET_PAD_HALF - wid.getHeight());
+ }
+ case BOTCENT -> {
+ hHalf = screenH / 2;
+ wid.setY(hHalf + ScreenConst.WIDGET_PAD_HALF);
+ }
+ case TOP -> wid.setY(ScreenConst.getScreenPad());
+ case BOT -> wid.setY(screenH - wid.getHeight() - ScreenConst.getScreenPad());
+ case LEFT -> wid.setX(ScreenConst.getScreenPad());
+ case RIGHT -> wid.setX(screenW - wid.getWidth() - ScreenConst.getScreenPad());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java
new file mode 100644
index 00000000..b6a5f789
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java
@@ -0,0 +1,153 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+import com.google.gson.JsonObject;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.ScreenConst;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+
+public class CollideStage extends PipelineStage {
+
+ private enum CollideDirection {
+ LEFT("left"),
+ RIGHT("right"),
+ TOP("top"),
+ BOT("bot");
+
+ private final String str;
+
+ CollideDirection(String d) {
+ this.str = d;
+ }
+
+ public static CollideDirection parse(String s) throws NoSuchElementException {
+ for (CollideDirection d : CollideDirection.values()) {
+ if (d.str.equals(s)) {
+ return d;
+ }
+ }
+ throw new NoSuchElementException("\"" + s + "\" is not a valid direction for a collide op!");
+ }
+ }
+
+ private final CollideDirection direction;
+
+ public CollideStage(ScreenBuilder builder, JsonObject descr) {
+ this.direction = CollideDirection.parse(descr.get("direction").getAsString());
+ this.primary = new ArrayList<>(descr.getAsJsonArray("widgets")
+ .asList()
+ .stream()
+ .map(x -> builder.getInstance(x.getAsString()))
+ .toList());
+ this.secondary = new ArrayList<>(descr.getAsJsonArray("colliders")
+ .asList()
+ .stream()
+ .map(x -> builder.getInstance(x.getAsString()))
+ .toList());
+ }
+
+ public void run(int screenW, int screenH) {
+ switch (this.direction) {
+ case LEFT -> primary.forEach(w -> collideAgainstL(screenW, w));
+ case RIGHT -> primary.forEach(w -> collideAgainstR(screenW, w));
+ case TOP -> primary.forEach(w -> collideAgainstT(screenH, w));
+ case BOT -> primary.forEach(w -> collideAgainstB(screenH, w));
+ }
+ }
+
+ public void collideAgainstL(int screenW, Widget w) {
+ int yMin = w.getY();
+ int yMax = w.getY() + w.getHeight();
+
+ int xCor = screenW;
+
+ for (Widget other : secondary) {
+ if (other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD < yMin) {
+ // too high, next one
+ continue;
+ }
+
+ if (other.getY() - ScreenConst.WIDGET_PAD > yMax) {
+ // too low, next
+ continue;
+ }
+
+ int xPos = other.getX() - ScreenConst.WIDGET_PAD - w.getWidth();
+ xCor = Math.min(xCor, xPos);
+ }
+ w.setX(xCor);
+ }
+
+ public void collideAgainstR(int screenW, Widget w) {
+ int yMin = w.getY();
+ int yMax = w.getY() + w.getHeight();
+
+ int xCor = 0;
+
+ for (Widget other : secondary) {
+ if (other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD < yMin) {
+ // too high, next one
+ continue;
+ }
+
+ if (other.getY() - ScreenConst.WIDGET_PAD > yMax) {
+ // too low, next
+ continue;
+ }
+
+ int xPos = other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD;
+ xCor = Math.max(xCor, xPos);
+ }
+ w.setX(xCor);
+ }
+
+ public void collideAgainstT(int screenH, Widget w) {
+ int xMin = w.getX();
+ int xMax = w.getX() + w.getWidth();
+
+ int yCor = screenH;
+
+ for (Widget other : secondary) {
+ if (other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD < xMin) {
+ // too far left, next one
+ continue;
+ }
+
+ if (other.getX() - ScreenConst.WIDGET_PAD > xMax) {
+ // too far right, next
+ continue;
+ }
+
+ int yPos = other.getY() - ScreenConst.WIDGET_PAD - w.getHeight();
+ yCor = Math.min(yCor, yPos);
+ }
+ w.setY(yCor);
+ }
+
+ public void collideAgainstB(int screenH, Widget w) {
+ int xMin = w.getX();
+ int xMax = w.getX() + w.getWidth();
+
+ int yCor = 0;
+
+ for (Widget other : secondary) {
+ if (other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD < xMin) {
+ // too far left, next one
+ continue;
+ }
+
+ if (other.getX() - ScreenConst.WIDGET_PAD > xMax) {
+ // too far right, next
+ continue;
+ }
+
+ int yPos = other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD;
+ yCor = Math.max(yCor, yPos);
+ }
+ w.setY(yCor);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java
new file mode 100644
index 00000000..e560058c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java
@@ -0,0 +1,14 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline;
+
+import java.util.ArrayList;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+
+public abstract class PipelineStage {
+
+ protected ArrayList<Widget> primary = null;
+ protected ArrayList<Widget> secondary = null;
+
+ public abstract void run(int screenW, int screenH);
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java
new file mode 100644
index 00000000..a950f8f2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java
@@ -0,0 +1,94 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+import com.google.gson.JsonObject;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.ScreenConst;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+
+public class PlaceStage extends PipelineStage {
+
+ private enum PlaceLocation {
+ CENTER("center"),
+ TOPCENT("centerTop"),
+ BOTCENT("centerBot"),
+ LEFTCENT("centerLeft"),
+ RIGHTCENT("centerRight"),
+ TRCORNER("cornerTopRight"),
+ TLCORNER("cornerTopLeft"),
+ BRCORNER("cornerBotRight"),
+ BLCORNER("cornerBotLeft");
+
+ private final String str;
+
+ PlaceLocation(String d) {
+ this.str = d;
+ }
+
+ public static PlaceLocation parse(String s) throws NoSuchElementException {
+ for (PlaceLocation d : PlaceLocation.values()) {
+ if (d.str.equals(s)) {
+ return d;
+ }
+ }
+ throw new NoSuchElementException("\"" + s + "\" is not a valid location for a place op!");
+ }
+ }
+
+ private final PlaceLocation where;
+
+ public PlaceStage(ScreenBuilder builder, JsonObject descr) {
+ this.where = PlaceLocation.parse(descr.get("where").getAsString());
+ this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to")
+ .asList()
+ .stream()
+ .map(x -> builder.getInstance(x.getAsString()))
+ .limit(1)
+ .toList());
+ }
+
+ public void run(int screenW, int screenH) {
+ Widget wid = primary.get(0);
+ switch (where) {
+ case CENTER -> {
+ wid.setX((screenW - wid.getWidth()) / 2);
+ wid.setY((screenH - wid.getHeight()) / 2);
+ }
+ case TOPCENT -> {
+ wid.setX((screenW - wid.getWidth()) / 2);
+ wid.setY(ScreenConst.getScreenPad());
+ }
+ case BOTCENT -> {
+ wid.setX((screenW - wid.getWidth()) / 2);
+ wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad());
+ }
+ case LEFTCENT -> {
+ wid.setX(ScreenConst.getScreenPad());
+ wid.setY((screenH - wid.getHeight()) / 2);
+ }
+ case RIGHTCENT -> {
+ wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad());
+ wid.setY((screenH - wid.getHeight()) / 2);
+ }
+ case TLCORNER -> {
+ wid.setX(ScreenConst.getScreenPad());
+ wid.setY(ScreenConst.getScreenPad());
+ }
+ case TRCORNER -> {
+ wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad());
+ wid.setY(ScreenConst.getScreenPad());
+ }
+ case BLCORNER -> {
+ wid.setX(ScreenConst.getScreenPad());
+ wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad());
+ }
+ case BRCORNER -> {
+ wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad());
+ wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad());
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java
new file mode 100644
index 00000000..5eb575c3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java
@@ -0,0 +1,115 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+import com.google.gson.JsonObject;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.ScreenConst;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+
+public class StackStage extends PipelineStage {
+
+ private enum StackDirection {
+ HORIZONTAL("horizontal"),
+ VERTICAL("vertical");
+
+ private final String str;
+
+ StackDirection(String d) {
+ this.str = d;
+ }
+
+ public static StackDirection parse(String s) throws NoSuchElementException {
+ for (StackDirection d : StackDirection.values()) {
+ if (d.str.equals(s)) {
+ return d;
+ }
+ }
+ throw new NoSuchElementException("\"" + s + "\" is not a valid direction for a stack op!");
+ }
+ }
+
+ private enum StackAlign {
+ TOP("top"),
+ BOT("bot"),
+ LEFT("left"),
+ RIGHT("right"),
+ CENTER("center");
+
+ private final String str;
+
+ StackAlign(String d) {
+ this.str = d;
+ }
+
+ public static StackAlign parse(String s) throws NoSuchElementException {
+ for (StackAlign d : StackAlign.values()) {
+ if (d.str.equals(s)) {
+ return d;
+ }
+ }
+ throw new NoSuchElementException("\"" + s + "\" is not a valid alignment for a stack op!");
+ }
+ }
+
+ private final StackDirection direction;
+ private final StackAlign align;
+
+ public StackStage(ScreenBuilder builder, JsonObject descr) {
+ this.direction = StackDirection.parse(descr.get("direction").getAsString());
+ this.align = StackAlign.parse(descr.get("align").getAsString());
+ this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to")
+ .asList()
+ .stream()
+ .map(x -> builder.getInstance(x.getAsString()))
+ .toList());
+ }
+
+ public void run(int screenW, int screenH) {
+ switch (this.direction) {
+ case HORIZONTAL -> stackWidgetsHoriz(screenW);
+ case VERTICAL -> stackWidgetsVert(screenH);
+ }
+ }
+
+ public void stackWidgetsVert(int screenH) {
+ int compHeight = -ScreenConst.WIDGET_PAD;
+ for (Widget wid : primary) {
+ compHeight += wid.getHeight() + 5;
+ }
+
+ int y = switch (this.align) {
+
+ case TOP -> ScreenConst.getScreenPad();
+ case BOT -> (screenH - compHeight) - ScreenConst.getScreenPad();
+ default -> (screenH - compHeight) / 2;
+ };
+
+ for (Widget wid : primary) {
+ wid.setY(y);
+ y += wid.getHeight() + ScreenConst.WIDGET_PAD;
+ }
+ }
+
+ public void stackWidgetsHoriz(int screenW) {
+ // TODO not centered (?)
+ int compWidth = -ScreenConst.WIDGET_PAD;
+ for (Widget wid : primary) {
+ compWidth += wid.getWidth() + ScreenConst.WIDGET_PAD;
+ }
+
+ int x = switch (this.align) {
+
+ case LEFT -> ScreenConst.getScreenPad();
+ case RIGHT -> (screenW - compWidth) - ScreenConst.getScreenPad();
+ default -> (screenW - compWidth) / 2;
+ };
+
+ for (Widget wid : primary) {
+ wid.setX(x);
+ x += wid.getWidth() + ScreenConst.WIDGET_PAD;
+ }
+ }
+} \ No newline at end of file
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
deleted file mode 100644
index 5c302eb3..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java
+++ /dev/null
@@ -1,16 +0,0 @@
-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
deleted file mode 100644
index 6d06c637..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java
+++ /dev/null
@@ -1,230 +0,0 @@
-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.genericInfo.GenericRiftInfoScreen;
-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.main.RiftScreen;
-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.gui.DrawContext;
-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 THE_RIFT -> new GenericRiftInfoScreen(w, h, footer);
- 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 THE_RIFT -> new RiftScreen(w, h, footer);
- 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(DrawContext context) {
- for (Widget w : widgets) {
- w.render(context);
- }
- }
-
- /**
- * 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
deleted file mode 100644
index 0bb12c8e..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GardenInfoScreen.java
+++ /dev/null
@@ -1,51 +0,0 @@
-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
deleted file mode 100644
index 046a9313..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java
+++ /dev/null
@@ -1,48 +0,0 @@
-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/genericInfo/GenericRiftInfoScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericRiftInfoScreen.java
deleted file mode 100644
index 9821b5a3..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericRiftInfoScreen.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo;
-
-import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.RiftProfileWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.RiftStatsWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.ShenWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CookieWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.AdvertisementWidget;
-import net.minecraft.text.Text;
-
-public class GenericRiftInfoScreen extends Screen {
-
- public GenericRiftInfoScreen(int w, int h, Text footer) {
- super(w, h);
-
- String f = footer.getString();
-
- RiftProfileWidget profile = new RiftProfileWidget();
- RiftStatsWidget stats = new RiftStatsWidget();
- ShenWidget shen = new ShenWidget();
-
- CookieWidget cookie = new CookieWidget(f);
- AdvertisementWidget advertisement = new AdvertisementWidget();
-
- this.stackWidgetsH(stats, advertisement);
- this.stackWidgetsH(profile, shen, cookie);
-
- this.offCenterL(stats);
- this.offCenterL(advertisement);
-
- this.offCenterR(profile);
- this.offCenterR(shen);
- this.offCenterR(cookie);
-
- this.addWidgets(profile, stats, shen, cookie, advertisement);
- }
-
-}
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
deleted file mode 100644
index 6e6f563b..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/CrimsonIsleScreen.java
+++ /dev/null
@@ -1,32 +0,0 @@
-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
deleted file mode 100644
index 5db461af..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonHubScreen.java
+++ /dev/null
@@ -1,25 +0,0 @@
-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
deleted file mode 100644
index 852ee876..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java
+++ /dev/null
@@ -1,40 +0,0 @@
-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
deleted file mode 100644
index 02c81f23..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java
+++ /dev/null
@@ -1,26 +0,0 @@
-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
deleted file mode 100644
index 8fd69cbb..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
-
-
-import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CameraPositionWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ComposterWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GardenServerWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GardenVisitorsWidget;
-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();
- GardenVisitorsWidget vw = new GardenVisitorsWidget();
- CameraPositionWidget cpw = new CameraPositionWidget();
-
- this.stackWidgetsH(gsw, vw);
- this.stackWidgetsH(cw, cpw);
- this.offCenterL(gsw);
- this.offCenterL(vw);
- this.offCenterR(cw);
- this.offCenterR(cpw);
- this.addWidgets(gsw, cw, vw, cpw);
- }
-
-}
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
deleted file mode 100644
index a89563db..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java
+++ /dev/null
@@ -1,21 +0,0 @@
-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
deleted file mode 100644
index 57d7a199..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java
+++ /dev/null
@@ -1,22 +0,0 @@
-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
deleted file mode 100644
index e61ba4b0..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java
+++ /dev/null
@@ -1,26 +0,0 @@
-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
deleted file mode 100644
index e2857f7e..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java
+++ /dev/null
@@ -1,26 +0,0 @@
-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
deleted file mode 100644
index 616c3e82..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java
+++ /dev/null
@@ -1,30 +0,0 @@
-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
deleted file mode 100644
index aa65d946..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java
+++ /dev/null
@@ -1,19 +0,0 @@
-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/main/RiftScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/RiftScreen.java
deleted file mode 100644
index d63bcf62..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/RiftScreen.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
-
-import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.RiftProgressWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.GoodToKnowWidget;
-import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.RiftServerInfoWidget;
-
-
-import net.minecraft.text.Text;
-
-public class RiftScreen extends Screen {
-
- public RiftScreen(int w, int h, Text footer) {
- super(w, h);
-
- RiftProgressWidget rftProg = new RiftProgressWidget();
- GoodToKnowWidget gtk = new GoodToKnowWidget();
- RiftServerInfoWidget si = new RiftServerInfoWidget();
-
- this.stackWidgetsH(si, gtk);
- this.stackWidgetsH(rftProg);
- this.offCenterL(si);
- this.offCenterL(gtk);
- this.offCenterR(rftProg);
- this.addWidgets(si, gtk, rftProg);
- }
-
-}
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
deleted file mode 100644
index 2567da13..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java
+++ /dev/null
@@ -1,29 +0,0 @@
-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
deleted file mode 100644
index 5a9733cc..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java
+++ /dev/null
@@ -1,26 +0,0 @@
-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
deleted file mode 100644
index 2a159ecc..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java
+++ /dev/null
@@ -1,25 +0,0 @@
-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
deleted file mode 100644
index 5db01512..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/PlayerListScreen.java
+++ /dev/null
@@ -1,20 +0,0 @@
-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/PlayerListMgr.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java
index d0ce6b72..7b35bcce 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java
@@ -26,6 +26,7 @@ public class PlayerListMgr {
public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Regex");
private static List<PlayerListEntry> playerList;
+ private static String footer;
public static void updateList() {
@@ -41,6 +42,14 @@ public class PlayerListMgr {
}
}
+ public static void updateFooter(Text f) {
+ footer = f.getString();
+ }
+
+ public static String getFooter() {
+ return footer;
+ }
+
/**
* Get the display name at some index of the player list and apply a pattern to
* it
@@ -85,7 +94,7 @@ public class PlayerListMgr {
return null;
}
String str = txt.getString().trim();
- if (str.length() == 0) {
+ if (str.isEmpty()) {
return null;
}
return str;
@@ -135,7 +144,7 @@ public class PlayerListMgr {
}
// Avoid returning an empty component - Rift advertisements needed this
- if (newText.getString().length() == 0) {
+ if (newText.getString().isEmpty()) {
return null;
}
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
index f7a989bc..11c9d860 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java
@@ -7,27 +7,35 @@ import me.xmrvizzy.skyblocker.utils.Utils;
*/
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,
- THE_RIFT,
- DARK_AUCTION,
- UNKNOWN
+ public enum Location {
+ DUNGEON("dungeon"),
+ GUEST_ISLAND("guest_island"),
+ HOME_ISLAND("home_island"),
+ CRIMSON_ISLE("crimson_isle"),
+ DUNGEON_HUB("dungeon_hub"),
+ FARMING_ISLAND("farming_island"),
+ PARK("park"),
+ DWARVEN_MINES("dwarven_mines"),
+ CRYSTAL_HOLLOWS("crystal_hollows"),
+ END("end"),
+ GOLD_MINE("gold_mine"),
+ DEEP_CAVERNS("deep_caverns"),
+ HUB("hub"),
+ SPIDER_DEN("spider_den"),
+ JERRY("jerry_workshop"),
+ GARDEN("garden"),
+ INSTANCED("kuudra"),
+ THE_RIFT("rift"),
+ DARK_AUCTION("dark_auction"),
+ UNKNOWN("unknown");
+
+ public final String internal;
+
+ Location(String i) {
+ // as used internally by the mod, e.g. in the json
+ this.internal = i;
+ }
+
}
public static Location getPlayerLocation() {
@@ -36,60 +44,44 @@ public class PlayerLocator {
return Location.UNKNOWN;
}
- String areaDesciptor = PlayerListMgr.strAt(41);
+ String areaDescriptor = PlayerListMgr.strAt(41);
- if (areaDesciptor == null || areaDesciptor.length() < 6) {
+ if (areaDescriptor == null || areaDescriptor.length() < 6) {
return Location.UNKNOWN;
}
- if (areaDesciptor.startsWith("Dungeon")) {
+ if (areaDescriptor.startsWith("Dungeon")) {
return Location.DUNGEON;
}
- switch (areaDesciptor.substring(6)) {
- case "Private Island":
+ return switch (areaDescriptor.substring(6)) {
+ case "Private Island" -> {
String islandType = PlayerListMgr.strAt(44);
if (islandType == null) {
- return Location.UNKNOWN;
+ yield Location.UNKNOWN;
} else if (islandType.endsWith("Guest")) {
- return Location.GUEST_ISLAND;
+ yield Location.GUEST_ISLAND;
} else {
- return Location.HOME_ISLAND;
+ yield 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;
- case "The Rift":
- return Location.THE_RIFT;
- case "Dark Auction":
- return Location.DARK_AUCTION;
- default:
- return Location.UNKNOWN;
- }
+ }
+ case "Crimson Isle" -> Location.CRIMSON_ISLE;
+ case "Dungeon Hub" -> Location.DUNGEON_HUB;
+ case "The Farming Islands" -> Location.FARMING_ISLAND;
+ case "The Park" -> Location.PARK;
+ case "Dwarven Mines" -> Location.DWARVEN_MINES;
+ case "Crystal Hollows" -> Location.CRYSTAL_HOLLOWS;
+ case "The End" -> Location.END;
+ case "Gold Mine" -> Location.GOLD_MINE;
+ case "Deep Caverns" -> Location.DEEP_CAVERNS;
+ case "Hub" -> Location.HUB;
+ case "Spider's Den" -> Location.SPIDER_DEN;
+ case "Jerry's Workshop" -> Location.JERRY;
+ case "Garden" -> Location.GARDEN;
+ case "Instanced" -> Location.INSTANCED;
+ case "The Rift" -> Location.THE_RIFT;
+ case "Dark Auction" -> Location.DARK_AUCTION;
+ default -> Location.UNKNOWN;
+ };
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/ScreenConst.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/ScreenConst.java
new file mode 100644
index 00000000..42089e9e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/ScreenConst.java
@@ -0,0 +1,13 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.util;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+
+public class ScreenConst {
+ public static final int WIDGET_PAD = 5;
+ public static final int WIDGET_PAD_HALF = 3;
+ private static final int SCREEN_PAD_BASE = 20;
+
+ public static int getScreenPad() {
+ return (int) ((1f/((float)SkyblockerConfig.get().general.tabHud.tabHudScale/100f) * SCREEN_PAD_BASE));
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java
index ca108837..7e44bc44 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java
@@ -8,25 +8,30 @@ import net.minecraft.util.Formatting;
import net.minecraft.util.math.MathHelper;
public class CameraPositionWidget extends Widget {
- private static final MutableText TITLE = Text.literal("Camera Pos").formatted(Formatting.DARK_PURPLE, Formatting.BOLD);
- private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
-
- public CameraPositionWidget() {
- super(TITLE, Formatting.DARK_PURPLE.getColorValue());
-
- double yaw = CLIENT.getCameraEntity().getYaw();
- double pitch = CLIENT.getCameraEntity().getPitch();
-
- this.addComponent(new PlainTextComponent(Text.literal("Yaw: " + roundToDecimalPlaces(MathHelper.wrapDegrees(yaw), 3))));
- this.addComponent(new PlainTextComponent(Text.literal("Pitch: " + roundToDecimalPlaces(MathHelper.wrapDegrees(pitch), 3))));
-
- this.pack();
- }
-
- //https://stackoverflow.com/a/33889423
- private static double roundToDecimalPlaces(double value, int decimalPlaces) {
- double shift = Math.pow(10, decimalPlaces);
-
- return Math.round(value * shift) / shift;
- }
+ private static final MutableText TITLE = Text.literal("Camera Pos").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+ private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
+
+ public CameraPositionWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
+ double yaw = CLIENT.getCameraEntity().getYaw();
+ double pitch = CLIENT.getCameraEntity().getPitch();
+
+ this.addComponent(
+ new PlainTextComponent(Text.literal("Yaw: " + roundToDecimalPlaces(MathHelper.wrapDegrees(yaw), 3))));
+ this.addComponent(new PlainTextComponent(
+ Text.literal("Pitch: " + roundToDecimalPlaces(MathHelper.wrapDegrees(pitch), 3))));
+
+ }
+
+ // https://stackoverflow.com/a/33889423
+ private static double roundToDecimalPlaces(double value, int decimalPlaces) {
+ double shift = Math.pow(10, decimalPlaces);
+
+ return Math.round(value * shift) / shift;
+ }
}
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
index de90cf30..8bbdb25f 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java
@@ -1,15 +1,11 @@
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;
@@ -31,7 +27,10 @@ public class CommsWidget extends Widget {
public CommsWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
for (int i = 50; i <= 53; i++) {
Matcher m = PlayerListMgr.regexAt(i, COMM_PATTERN);
// end of comms found?
@@ -55,33 +54,6 @@ public class CommsWidget extends Widget {
}
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) {
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
index 5922fcbc..1307b407 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java
@@ -16,13 +16,15 @@ public class ComposterWidget extends Widget {
public ComposterWidget() {
super(TITLE, Formatting.GREEN.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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
index 48cb90bd..bf1d086f 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java
@@ -4,6 +4,7 @@ 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;
@@ -19,19 +20,21 @@ public class CookieWidget extends Widget {
private static final Pattern COOKIE_PATTERN = Pattern.compile(".*\\nCookie Buff\\n(?<buff>.*)\\n");
- public CookieWidget(String footertext) {
+ public CookieWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+ @Override
+ public void updateContent() {
+ String footertext = PlayerListMgr.getFooter();
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;
}
@@ -42,7 +45,6 @@ public class CookieWidget extends Widget {
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
index d56d3522..b7f58763 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
@@ -1,5 +1,6 @@
package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+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;
@@ -16,12 +17,17 @@ public class DungeonBuffWidget extends Widget {
private static final MutableText TITLE = Text.literal("Dungeon Buffs").formatted(Formatting.DARK_PURPLE,
Formatting.BOLD);
- public DungeonBuffWidget(String footertext) {
+ public DungeonBuffWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
+
+ String footertext = PlayerListMgr.getFooter();
if (footertext == null || !footertext.contains("Dungeon Buffs")) {
this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY)));
- this.pack();
return;
}
@@ -30,7 +36,6 @@ public class DungeonBuffWidget extends Widget {
if (!lines[1].startsWith("Blessing")) {
this.addComponent(new PlainTextComponent(Text.literal("No buffs found!").formatted(Formatting.GRAY)));
- this.pack();
return;
}
@@ -48,7 +53,6 @@ public class DungeonBuffWidget extends Widget {
this.addComponent(new PlainTextComponent(Text.literal(line).styled(style -> style.withColor(color))));
}
- this.pack();
}
@SuppressWarnings("DataFlowIssue")
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
index 05223211..80134b66 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java
@@ -24,7 +24,10 @@ public class DungeonDeathWidget extends Widget {
public DungeonDeathWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+ @Override
+ public void updateContent() {
Matcher m = PlayerListMgr.regexAt(25, DEATH_PATTERN);
if (m == null) {
this.addComponent(new IcoTextComponent());
@@ -39,7 +42,6 @@ public class DungeonDeathWidget extends Widget {
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
index 9bb250f7..2ca617ee 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java
@@ -17,7 +17,10 @@ public class DungeonDownedWidget extends Widget {
public DungeonDownedWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+ @Override
+ public void updateContent() {
String down = PlayerListMgr.strAt(21);
if (down == null) {
this.addComponent(new IcoTextComponent());
@@ -36,7 +39,6 @@ public class DungeonDownedWidget extends Widget {
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
index 443cca55..2ba0c0cc 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java
@@ -45,10 +45,16 @@ public class DungeonPlayerWidget extends Widget {
MSGS.add("More is better!");
}
+ private final int player;
+
// title needs to be changeable here
public DungeonPlayerWidget(int player) {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ this.player = player;
+ }
+ @Override
+ public void updateContent() {
int start = 1 + (player - 1) * 4;
if (PlayerListMgr.strAt(start) == null) {
@@ -56,7 +62,6 @@ public class DungeonPlayerWidget extends Widget {
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);
@@ -94,6 +99,5 @@ public class DungeonPlayerWidget extends Widget {
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
index 2529e876..ef765fc3 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java
@@ -26,7 +26,10 @@ public class DungeonPuzzleWidget extends Widget {
public DungeonPuzzleWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+ @Override
+ public void updateContent() {
int pos = 48;
while (pos < 60) {
@@ -49,7 +52,6 @@ public class DungeonPuzzleWidget extends Widget {
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
index 93eb69de..a985b4b1 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
@@ -14,11 +14,13 @@ public class DungeonSecretWidget extends Widget {
public DungeonSecretWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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
index 81b8f907..05b88127 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java
@@ -24,7 +24,10 @@ public class DungeonServerWidget extends Widget {
public DungeonServerWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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);
@@ -40,8 +43,6 @@ public class DungeonServerWidget extends Widget {
}
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
index cd39a25a..718ea2d3 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java
@@ -1,6 +1,7 @@
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.IcoFatTextComponent;
import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
import net.minecraft.text.MutableText;
@@ -16,12 +17,17 @@ public class EffectWidget extends Widget {
private static final MutableText TITLE = Text.literal("Effect Info").formatted(Formatting.DARK_PURPLE,
Formatting.BOLD);
- public EffectWidget(String footertext) {
+ public EffectWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
+
+ String footertext = PlayerListMgr.getFooter();
if (footertext == null || !footertext.contains("Active Effects")) {
this.addComponent(new IcoTextComponent());
- this.pack();
return;
}
@@ -29,7 +35,6 @@ public class EffectWidget extends Widget {
String[] lines = footertext.split("Active Effects")[1].split("\n");
if (lines.length < 2) {
this.addComponent(new IcoTextComponent());
- this.pack();
return;
}
@@ -47,7 +52,6 @@ public class EffectWidget extends Widget {
int idx = number.indexOf(' ');
if (idx == -1 || lines.length < 4) {
this.addComponent(new IcoFatTextComponent());
- this.pack();
return;
}
number = number.substring(0, idx);
@@ -58,7 +62,6 @@ public class EffectWidget extends Widget {
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
index ed07982c..0f858fb6 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java
@@ -48,14 +48,16 @@ public class ElectionWidget extends Widget {
public ElectionWidget() {
super(TITLE, Formatting.YELLOW.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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;
}
@@ -97,7 +99,6 @@ public class ElectionWidget extends Widget {
}
}
}
- 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/ErrorWidget.java
index 52d6cfbd..7f53fde7 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ErrorWidget.java
@@ -8,17 +8,25 @@ 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,
+public class ErrorWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Error").formatted(Formatting.RED,
Formatting.BOLD);
- public EmptyWidget() {
+ Text error = Text.of("No info available!");
+
+ public ErrorWidget() {
super(TITLE, Formatting.RED.getColorValue());
+ }
+
+ public ErrorWidget(String error) {
+ super(TITLE, Formatting.RED.getColorValue());
+ this.error = Text.of(error);
+ }
- Text info = Text.of("No info for this area!");
- PlainTextComponent inf = new PlainTextComponent(info);
+ @Override
+ public void updateContent() {
+ PlainTextComponent inf = new PlainTextComponent(this.error);
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
index fc0780e1..c503d89f 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java
@@ -19,6 +19,10 @@ public class EssenceWidget extends Widget {
public EssenceWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
wither = Widget.simpleEntryText(46, "Wither:", Formatting.DARK_PURPLE);
spider = Widget.simpleEntryText(47, "Spider:", Formatting.DARK_PURPLE);
undead = Widget.simpleEntryText(48, "Undead:", Formatting.DARK_PURPLE);
@@ -39,6 +43,5 @@ public class EssenceWidget extends Widget {
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
index 1b46e621..7d07ad75 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java
@@ -11,9 +11,15 @@ import net.minecraft.util.Formatting;
public class EventWidget extends Widget {
private static final MutableText TITLE = Text.literal("Event Info").formatted(Formatting.YELLOW, Formatting.BOLD);
+ private final boolean isInGarden;
+
public EventWidget(boolean isInGarden) {
super(TITLE, Formatting.YELLOW.getColorValue());
+ this.isInGarden = isInGarden;
+ }
+ @Override
+ public void updateContent() {
// 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;
@@ -24,7 +30,6 @@ public class EventWidget extends Widget {
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
index ddf51f32..ba6a0ec1 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java
@@ -29,18 +29,19 @@ public class FireSaleWidget extends Widget {
public FireSaleWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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;
}
@@ -57,7 +58,6 @@ public class FireSaleWidget extends Widget {
ProgressComponent pc = new ProgressComponent(Ico.GOLD, itemTxt, prgressTxt, pcnt, pcntToCol(pcnt));
this.addComponent(pc);
}
- this.pack();
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
index da1ba6c5..ed87d496 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
@@ -20,12 +20,15 @@ public class ForgeWidget extends Widget {
public ForgeWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
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;
}
@@ -45,22 +48,22 @@ public class ForgeWidget extends Widget {
Text l1, l2;
switch (fstr.substring(3)) {
- case "LOCKED":
+ 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);
+ Text.literal("This message should not appear").formatted(Formatting.RED, Formatting.BOLD);
};
c = new IcoFatTextComponent(Ico.BARRIER, l1, l2);
- break;
- case "EMPTY":
+ }
+ case "EMPTY" -> {
l1 = Text.literal("Empty").formatted(Formatting.GRAY);
c = new IcoTextComponent(Ico.FURNACE, l1);
- break;
- default:
+ }
+ default -> {
String[] parts = fstr.split(": ");
if (parts.length != 2) {
c = new IcoFatTextComponent();
@@ -69,11 +72,10 @@ public class ForgeWidget extends Widget {
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
index b0fc160f..2fd81873 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java
@@ -24,7 +24,10 @@ public class GardenServerWidget extends Widget {
public GardenServerWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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);
@@ -33,7 +36,6 @@ public class GardenServerWidget extends Widget {
Matcher m = PlayerListMgr.regexAt(45, VISITOR_PATTERN);
if (m == null ) {
this.addComponent(new IcoTextComponent());
- this.pack();
return;
}
@@ -47,7 +49,6 @@ public class GardenServerWidget extends Widget {
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
index 26e29ce2..2b928cba 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java
@@ -30,7 +30,10 @@ public class GardenSkillsWidget extends Widget {
public GardenSkillsWidget() {
super(TITLE, Formatting.YELLOW.getColorValue());
+ }
+ @Override
+ public void updateContent() {
ProgressComponent pc;
Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN);
if (m == null) {
@@ -72,7 +75,6 @@ public class GardenSkillsWidget extends Widget {
}
this.addComponent(pc2);
- this.pack();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java
index 6a4d2af3..945fb17c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java
@@ -7,23 +7,24 @@ import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
public class GardenVisitorsWidget extends Widget {
- private static final MutableText TITLE = Text.literal("Visitors").formatted(Formatting.DARK_GREEN, Formatting.BOLD);
+ private static final MutableText TITLE = Text.literal("Visitors").formatted(Formatting.DARK_GREEN, Formatting.BOLD);
- public GardenVisitorsWidget() {
- super(TITLE, Formatting.DARK_GREEN.getColorValue());
-
- if (PlayerListMgr.textAt(54) == null) {
- this.addComponent(new PlainTextComponent(Text.literal("No visitors!").formatted(Formatting.GRAY)));
- this.pack();
-
- return;
- }
-
- for (int i = 54; i < 59; i++) {
- String text = PlayerListMgr.strAt(i);
- if (text != null) this.addComponent(new PlainTextComponent(Text.literal(text)));
- }
-
- this.pack();
- }
+ public GardenVisitorsWidget() {
+ super(TITLE, Formatting.DARK_GREEN.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
+ if (PlayerListMgr.textAt(54) == null) {
+ this.addComponent(new PlainTextComponent(Text.literal("No visitors!").formatted(Formatting.GRAY)));
+ return;
+ }
+
+ for (int i = 54; i < 59; i++) {
+ String text = PlayerListMgr.strAt(i);
+ if (text != null)
+ this.addComponent(new PlainTextComponent(Text.literal(text)));
+ }
+
+ }
}
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
index cb208e92..6f1f4811 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java
@@ -16,13 +16,15 @@ public class GuestServerWidget extends Widget {
public GuestServerWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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
index e0f5f1a3..faf231a8 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java
@@ -23,6 +23,10 @@ public class IslandGuestsWidget extends Widget {
public IslandGuestsWidget() {
super(TITLE, Formatting.AQUA.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
for (int i = 21; i < 40; i++) {
String str = PlayerListMgr.strAt(i);
if (str == null) {
@@ -38,7 +42,6 @@ public class IslandGuestsWidget extends Widget {
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
index e81a6d85..2e8e2c40 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
@@ -26,14 +26,19 @@ public class IslandOwnersWidget extends Widget {
public IslandOwnersWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
+
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;
+ String name, lastseen;
+ Formatting format;
if (m.group("nameA") != null) {
name = m.group("nameA");
lastseen = m.group("lastseen");
@@ -55,7 +60,7 @@ public class IslandOwnersWidget extends Widget {
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
index 4b03da6e..cc7a2f0c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java
@@ -20,20 +20,24 @@ public class IslandSelfWidget extends Widget {
// matches an owner
// group 1: player name, optionally offline time
// ^\[\d*\] (?:\[[A-Za-z]+\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$
- private static final Pattern OWNER_PATTERN = Pattern.compile("^\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$");
+ private static final Pattern OWNER_PATTERN = Pattern
+ .compile("^\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$");
public IslandSelfWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
for (int i = 1; i < 20; i++) {
- Matcher m = PlayerListMgr.regexAt( i, OWNER_PATTERN);
+ Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN);
if (m == null) {
break;
}
-
+
Text entry = (m.group(1) != null) ? Text.of(m.group(1)) : Text.of(m.group(2));
this.addComponent(new PlainTextComponent(entry));
}
- 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
index 2b02c514..1ed15f5e 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java
@@ -16,14 +16,16 @@ public class IslandServerWidget extends Widget {
public IslandServerWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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
index 8d49efaa..888c3697 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java
@@ -37,7 +37,10 @@ public class JacobsContestWidget extends Widget {
public JacobsContestWidget() {
super(TITLE, Formatting.YELLOW.getColorValue());
+ }
+ @Override
+ public void updateContent() {
this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.GOLD, 76);
TableComponent tc = new TableComponent(1, 3, Formatting.YELLOW .getColorValue());
@@ -54,7 +57,6 @@ public class JacobsContestWidget extends Widget {
}
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
index fe52fcdf..039871b2 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java
@@ -93,7 +93,10 @@ public class MinionWidget extends Widget {
public MinionWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
for (int i = 48; i < 59; i++) {
Matcher m = PlayerListMgr.regexAt(i, MINION_PATTERN);
if (m != null) {
@@ -128,7 +131,6 @@ public class MinionWidget extends Widget {
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
index 4148ed77..2c422bd1 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java
@@ -16,13 +16,15 @@ public class ParkServerWidget extends Widget {
public ParkServerWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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
index 439fcb56..31370dc6 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java
@@ -21,11 +21,15 @@ public class PlayerListWidget extends Widget {
private static final MutableText TITLE = Text.literal("Players").formatted(Formatting.GREEN,
Formatting.BOLD);
- private final ArrayList<PlayerListEntry> list = new ArrayList<>();
-
public PlayerListWidget() {
super(TITLE, Formatting.GREEN.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
+ ArrayList<PlayerListEntry> list = new ArrayList<>();
+
// hard cap to 4x20 entries.
// 5x20 is too wide (and not possible in theory. in reality however...)
int listlen = Math.min(PlayerListMgr.getSize(), 160);
@@ -33,7 +37,6 @@ public class PlayerListWidget extends Widget {
// 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;
}
@@ -63,6 +66,5 @@ public class PlayerListWidget extends Widget {
}
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
index 459e3de2..7e56d4d9 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java
@@ -17,12 +17,13 @@ public class PowderWidget extends Widget {
public PowderWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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
index a6d9e82d..8bc94622 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java
@@ -14,12 +14,15 @@ public class ProfileWidget extends Widget {
public ProfileWidget() {
super(TITLE, Formatting.YELLOW.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
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
index 43b741ba..02137b1a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java
@@ -18,12 +18,15 @@ public class QuestWidget extends Widget {
public QuestWidget() {
super(TITLE, Formatting.AQUA.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
for (int i = 51; i < 56; i++) {
Text q = PlayerListMgr.textAt(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
index 3685e0ca..32060bd0 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java
@@ -29,7 +29,10 @@ public class ReputationWidget extends Widget {
public ReputationWidget() {
super(TITLE, Formatting.AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
String fracstr = PlayerListMgr.strAt(45);
int spaceidx;
@@ -61,8 +64,6 @@ public class ReputationWidget extends Widget {
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
index 2d8d1c63..62c01b63 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java
@@ -18,11 +18,13 @@ public class ServerWidget extends Widget {
public ServerWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
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
index 88ba8022..cecee76c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java
@@ -30,6 +30,10 @@ public class SkillsWidget extends Widget {
public SkillsWidget() {
super(TITLE, Formatting.YELLOW.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN);
Component progress;
if (m == null) {
@@ -69,7 +73,6 @@ public class SkillsWidget extends Widget {
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
index d47849c3..68f8dcf2 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java
@@ -15,8 +15,11 @@ public class TrapperWidget extends Widget {
public TrapperWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
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
index ef7c21d0..4553c7f9 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java
@@ -1,6 +1,7 @@
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 me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
@@ -17,17 +18,21 @@ public class UpgradeWidget extends Widget {
private static final MutableText TITLE = Text.literal("Upgrade Info").formatted(Formatting.GOLD,
Formatting.BOLD);
- public UpgradeWidget(String footertext) {
+ public UpgradeWidget() {
super(TITLE, Formatting.GOLD.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
+ String footertext = PlayerListMgr.getFooter();
+
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;
}
@@ -41,7 +46,6 @@ public class UpgradeWidget extends Widget {
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
index ec6a35fb..90f947ba 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java
@@ -22,26 +22,30 @@ public class VolcanoWidget extends Widget {
static {
BOOM_TYPE.put("INACTIVE",
- new Pair<ItemStack, Formatting>(new ItemStack(Items.BARRIER), Formatting.DARK_GRAY));
+ new Pair<>(new ItemStack(Items.BARRIER), Formatting.DARK_GRAY));
BOOM_TYPE.put("CHILL",
- new Pair<ItemStack, Formatting>(new ItemStack(Items.ICE), Formatting.AQUA));
+ new Pair<>(new ItemStack(Items.ICE), Formatting.AQUA));
BOOM_TYPE.put("LOW",
- new Pair<ItemStack, Formatting>(new ItemStack(Items.FLINT_AND_STEEL), Formatting.GRAY));
+ new Pair<>(new ItemStack(Items.FLINT_AND_STEEL), Formatting.GRAY));
BOOM_TYPE.put("DISRUPTIVE",
- new Pair<ItemStack, Formatting>(new ItemStack(Items.CAMPFIRE), Formatting.WHITE));
+ new Pair<>(new ItemStack(Items.CAMPFIRE), Formatting.WHITE));
BOOM_TYPE.put("MEDIUM",
- new Pair<ItemStack, Formatting>(new ItemStack(Items.LAVA_BUCKET), Formatting.YELLOW));
+ new Pair<>(new ItemStack(Items.LAVA_BUCKET), Formatting.YELLOW));
BOOM_TYPE.put("HIGH",
- new Pair<ItemStack, Formatting>(new ItemStack(Items.FIRE_CHARGE), Formatting.GOLD));
+ new Pair<>(new ItemStack(Items.FIRE_CHARGE), Formatting.GOLD));
BOOM_TYPE.put("EXPLOSIVE",
- new Pair<ItemStack, Formatting>(new ItemStack(Items.TNT), Formatting.RED));
+ new Pair<>(new ItemStack(Items.TNT), Formatting.RED));
BOOM_TYPE.put("CATACLYSMIC",
- new Pair<ItemStack, Formatting>(new ItemStack(Items.SKELETON_SKULL), Formatting.DARK_RED));
+ new Pair<>(new ItemStack(Items.SKELETON_SKULL), Formatting.DARK_RED));
}
public VolcanoWidget() {
super(TITLE, Formatting.AQUA.getColorValue());
+ }
+
+ @Override
+ public void updateContent() {
String s = PlayerListMgr.strAt(58);
if (s == null) {
this.addComponent(new IcoTextComponent());
@@ -50,8 +54,6 @@ public class VolcanoWidget extends Widget {
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
index 33f77933..bca67b70 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java
@@ -25,13 +25,13 @@ import net.minecraft.util.Formatting;
*/
public abstract class Widget {
- private ArrayList<Component> components = new ArrayList<>();
+ private final ArrayList<Component> components = new ArrayList<>();
private int w = 0, h = 0;
private int x = 0, y = 0;
- private int color;
- private Text title;
+ private final int color;
+ private final Text title;
- private static TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
+ private static final TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
static final int BORDER_SZE_N = txtRend.fontHeight + 4;
static final int BORDER_SZE_S = 4;
@@ -45,12 +45,21 @@ public abstract class Widget {
}
public final void addComponent(Component c) {
- components.add(c);
+ this.components.add(c);
}
+ public final void update() {
+ this.components.clear();
+ this.updateContent();
+ this.pack();
+ }
+
+ public abstract void updateContent();
+
/**
* Shorthand function for simple components.
- * If the entry at idx has the format "<textA>: <textB>", an IcoTextComponent is added as such:
+ * 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) {
@@ -60,9 +69,12 @@ public abstract class Widget {
/**
* Calculate the size of this widget.
- * <b>Must be called before returning from the widget constructor and after all components are added!</b>
+ * <b>Must be called before returning from the widget constructor and after all
+ * components are added!</b>
*/
- public final void pack() {
+ private void pack() {
+ h = 0;
+ w = 0;
for (Component c : components) {
h += c.getHeight() + Component.PAD_L;
w = Math.max(w, c.getWidth() + Component.PAD_S);
@@ -111,7 +123,7 @@ public abstract class Widget {
* Draw this widget, possibly with a background
*/
public final void render(DrawContext context, boolean hasBG) {
- MatrixStack ms = context.getMatrices();
+ MatrixStack ms = context.getMatrices();
// 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
@@ -124,9 +136,9 @@ public abstract class Widget {
// move above other UI elements
ms.translate(0, 0, 200);
if (hasBG) {
- context.fill(x + 1, y, x + w - 1, y + h, COL_BG_BOX);
- context.fill(x, y + 1, x + 1, y + h - 1, COL_BG_BOX);
- context.fill(x + w - 1, y + 1, x + w, y + h - 1, COL_BG_BOX);
+ context.fill(x + 1, y, x + w - 1, y + h, COL_BG_BOX);
+ context.fill(x, y + 1, x + 1, y + h - 1, COL_BG_BOX);
+ context.fill(x + w - 1, y + 1, x + w, y + h - 1, COL_BG_BOX);
}
// move above background (if exists)
ms.translate(0, 0, 100);
@@ -163,9 +175,10 @@ public abstract class Widget {
}
/**
- * If the entry at idx has the format "[textA]: [textB]", the following is returned:
+ * 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);
@@ -185,7 +198,7 @@ public abstract class Widget {
/**
* @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));
}
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
index 850cb3d2..118d3cfe 100644
--- 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
@@ -3,7 +3,6 @@ package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
-import net.minecraft.client.render.item.ItemRenderer;
/**
* Abstract base class for a component that may be added to a Widget.
@@ -14,7 +13,7 @@ public abstract class Component {
public static final int PAD_S = 2;
public static final int PAD_L = 4;
- static TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
+ static final TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
// these should always be the content dimensions without any padding.
int width, height;
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
index a7cc8d12..90e210e9 100644
--- 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
@@ -19,25 +19,25 @@ public class ProgressComponent extends Component {
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;
+ private final ItemStack ico;
+ private final Text desc, bar;
+ private final float pcnt;
+ private final int color;
+ private final 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();
+ } else {
+ this.ico = (ico == null) ? Ico.BARRIER : ico;
+ this.desc = d;
+ this.bar = b;
+ this.pcnt = pcnt;
+ this.color = 0xff000000 | color;
}
this.barW = BAR_WIDTH;
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
index 30287dc0..850bbb0d 100644
--- 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
@@ -8,9 +8,9 @@ import net.minecraft.client.gui.DrawContext;
*/
public class TableComponent extends Component {
- private Component[][] comps;
- private int color;
- private int cols, rows;
+ private final Component[][] comps;
+ private final int color;
+ private final int cols, rows;
private int cellW, cellH;
public TableComponent(int w, int h, int col) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java
new file mode 100644
index 00000000..88d40891
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java
@@ -0,0 +1,73 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.hud;
+
+import java.util.List;
+
+import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud.Commission;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import net.minecraft.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)
+// USE ONLY WITH THE DWARVEN HUD!
+
+public class HudCommsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ private List<Commission> commissions;
+ private boolean isFancy;
+
+ // disgusting hack to get around text renderer issues.
+ // the ctor eventually tries to get the font's height, which doesn't work
+ // when called before the client window is created (roughly).
+ // the rebdering god 2 from the fabricord explained that detail, thanks!
+ public static final HudCommsWidget INSTANCE = new HudCommsWidget();
+ public static final HudCommsWidget INSTANCE_CFG = new HudCommsWidget();
+
+ // another repulsive hack to make this widget-like hud element work with the new widget class
+ // DON'T USE WITH THE WIDGET SYSTEM, ONLY USE FOR DWARVENHUD!
+ public HudCommsWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+
+ public void updateData(List<Commission> commissions, boolean isFancy) {
+ this.commissions = commissions;
+ this.isFancy = isFancy;
+ }
+
+ @Override
+ public void updateContent() {
+ 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);
+ }
+ }
+
+ 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/rift/AdvertisementWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java
index 287b25b1..88e3a5cd 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java
@@ -14,14 +14,16 @@ public class AdvertisementWidget extends Widget {
public AdvertisementWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+ @Override
+ public void updateContent() {
for (int i = 73; i < 80; i++) {
Text text = PlayerListMgr.textAt(i);
if (text != null)
this.addComponent(new PlainTextComponent(text));
}
- this.pack();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java
index 667bc154..e3b462a9 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java
@@ -14,7 +14,10 @@ public class GoodToKnowWidget extends Widget {
public GoodToKnowWidget() {
super(TITLE, Formatting.BLUE.getColorValue());
+ }
+ @Override
+ public void updateContent() {
// After you progress further the tab adds more info so we need to be careful of
// that
// In beginning it only shows montezuma, then timecharms and enigma souls are
@@ -50,6 +53,5 @@ public class GoodToKnowWidget extends Widget {
new IcoTextComponent(Ico.PINK_DYE, Text.literal("Lifetime Earned: ").append(lifetimeMotesEarned)));
}
- this.pack();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java
index 5460de49..0b6ff5bf 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java
@@ -12,8 +12,10 @@ public class RiftProfileWidget extends Widget {
public RiftProfileWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
-
+ }
+
+ @Override
+ public void updateContent() {
this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61);
- this.pack();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java
index 9ce12e76..375a41b9 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java
@@ -22,7 +22,10 @@ public class RiftProgressWidget extends Widget {
public RiftProgressWidget() {
super(TITLE, Formatting.BLUE.getColorValue());
+ }
+ @Override
+ public void updateContent() {
// After you progress further the tab adds more info so we need to be careful of
// that
// In beginning it only shows montezuma, then timecharms and enigma souls are
@@ -33,7 +36,7 @@ public class RiftProgressWidget extends Widget {
boolean hasTimecharms = false;
boolean hasEnigmaSouls = false;
- int montezumaPos = 0;
+ int montezumaPos;
// Check each position to see what is or isn't there so we don't try adding
// invalid components
@@ -88,7 +91,6 @@ public class RiftProgressWidget extends Widget {
this.addComponent(pc);
}
- this.pack();
}
private static int pcntToCol(float pcnt) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java
index 2ac2a35d..cab38a86 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java
@@ -16,11 +16,12 @@ public class RiftServerInfoWidget extends Widget {
public RiftServerInfoWidget() {
super(TITLE, Formatting.LIGHT_PURPLE.getColorValue());
-
+ }
+
+ @Override
+ public void updateContent() {
this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.LIGHT_PURPLE, 41);
this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
-
- this.pack();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java
index ef5876f2..8fab3dd4 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java
@@ -15,7 +15,10 @@ public class RiftStatsWidget extends Widget {
public RiftStatsWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
-
+ }
+
+ @Override
+ public void updateContent() {
Text riftDamage = Widget.simpleEntryText(64, "RDG", Formatting.DARK_PURPLE);
IcoTextComponent rdg = new IcoTextComponent(Ico.DIASWORD, riftDamage);
@@ -35,7 +38,6 @@ public class RiftStatsWidget extends Widget {
tc.addToCell(1, 1, mrg);
this.addComponent(tc);
- this.pack();
}
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java
index 5dcc08c0..a1345c49 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java
@@ -13,8 +13,10 @@ public class ShenWidget extends Widget {
public ShenWidget() {
super(TITLE, Formatting.DARK_AQUA.getColorValue());
-
+ }
+
+ @Override
+ public void updateContent() {
this.addComponent(new PlainTextComponent(Text.literal(PlayerListMgr.strAt(70))));
- this.pack();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
index e85020aa..839e0dae 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
@@ -28,6 +28,7 @@ import java.util.List;
* Utility variables and methods for retrieving Skyblock related information.
*/
public class Utils {
+ private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", "");
private static final String PROFILE_PREFIX = "Profile: ";
private static boolean isOnHypixel = false;
private static boolean isOnSkyblock = false;
@@ -129,10 +130,9 @@ public class Utils {
return;
}
String string = sidebar.toString();
- String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : "";
if (sidebar.isEmpty()) return;
- if (serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io")) {
+ if (isConnectedToHypixel(client)) {
if (!isOnHypixel) {
isOnHypixel = true;
}
@@ -154,6 +154,14 @@ public class Utils {
leaveSkyblock();
}
}
+
+ private static boolean isConnectedToHypixel(MinecraftClient client) {
+ String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : "";
+ String serverBrand = (client.player != null && client.player.getServerBrand() != null) ? client.player.getServerBrand() : "";
+ boolean isOnHypixel = (serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"));
+
+ return isOnHypixel;
+ }
private static void leaveSkyblock() {
if (isOnSkyblock) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java
index 18d9b28a..aaf92d68 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java
@@ -11,12 +11,12 @@ import me.xmrvizzy.skyblocker.utils.render.title.TitleContainer;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
-import net.minecraft.client.font.TextRenderer.TextLayerType;
import net.minecraft.client.render.*;
import net.minecraft.client.render.VertexFormat.DrawMode;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.OrderedText;
+import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
@@ -164,17 +164,29 @@ public class RenderHelper {
RenderSystem.disableDepthTest();
}
+ public static void renderText(WorldRenderContext context, Text text, Vec3d pos, boolean seeThrough) {
+ renderText(context, text, pos, 1, seeThrough);
+ }
+
+ public static void renderText(WorldRenderContext context, Text text, Vec3d pos, float scale, boolean seeThrough) {
+ renderText(context, text, pos, scale, 0, seeThrough);
+ }
+
+ public static void renderText(WorldRenderContext context, Text text, Vec3d pos, float scale, float yOffset, boolean seeThrough) {
+ renderText(context, text.asOrderedText(), pos, scale, yOffset, seeThrough);
+ }
+
/**
* Renders text in the world space.
*
* @param seeThrough Whether the text should be able to be seen through walls or not.
*/
- public static void renderText(WorldRenderContext context, Vec3d pos, OrderedText text, float scale, boolean seeThrough) {
+ public static void renderText(WorldRenderContext context, OrderedText text, Vec3d pos, float scale, float yOffset, boolean seeThrough) {
MatrixStack matrices = context.matrixStack();
Vec3d camera = context.camera().getPos();
- TextRenderer textRenderer = client.textRenderer;
+ TextRenderer textRenderer = client.textRenderer;
- scale *= 0.025f;
+ scale *= 0.025f;
matrices.push();
matrices.translate(pos.getX() - camera.getX(), pos.getY() - camera.getY(), pos.getZ() - camera.getZ());
@@ -183,7 +195,7 @@ public class RenderHelper {
matrices.scale(-scale, -scale, scale);
Matrix4f positionMatrix = matrices.peek().getPositionMatrix();
- float xOffset = -textRenderer.getWidth(text) / 2f;
+ float xOffset = -textRenderer.getWidth(text) / 2f;
Tessellator tessellator = RenderSystem.renderThreadTesselator();
BufferBuilder buffer = tessellator.getBuffer();
@@ -191,7 +203,7 @@ public class RenderHelper {
RenderSystem.depthFunc(seeThrough ? GL11.GL_ALWAYS : GL11.GL_LEQUAL);
- textRenderer.draw(text, xOffset, 0, 0xFFFFFFFF, false, positionMatrix, consumers, TextLayerType.SEE_THROUGH, 0, LightmapTextureManager.MAX_LIGHT_COORDINATE);
+ textRenderer.draw(text, xOffset, yOffset, 0xFFFFFFFF, false, positionMatrix, consumers, TextRenderer.TextLayerType.SEE_THROUGH, 0, LightmapTextureManager.MAX_LIGHT_COORDINATE);
consumers.draw();
RenderSystem.depthFunc(GL11.GL_LEQUAL);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java
new file mode 100644
index 00000000..5e5c8f89
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java
@@ -0,0 +1,104 @@
+package me.xmrvizzy.skyblocker.utils.tictactoe;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class TicTacToeUtils {
+
+ public static int getBestMove(char[][] board) {
+ HashMap<Integer, Integer> moves = new HashMap<>();
+ for (int row = 0; row < board.length; row++) {
+ for (int col = 0; col < board[row].length; col++) {
+ if (board[row][col] != '\0') continue;
+ board[row][col] = 'O';
+ int score = alphabeta(board, Integer.MIN_VALUE, Integer.MAX_VALUE, false, 0);
+ board[row][col] = '\0';
+ moves.put(row * 3 + col + 1, score);
+ }
+ }
+ return Collections.max(moves.entrySet(), Map.Entry.comparingByValue()).getKey();
+ }
+
+ public static boolean hasMovesLeft(char[][] board) {
+ for (char[] rows : board) {
+ for (char col : rows) {
+ if (col == '\0') return true;
+ }
+ }
+ return false;
+ }
+
+ public static int getBoardRanking(char[][] board) {
+ for (int row = 0; row < 3; row++) {
+ if (board[row][0] == board[row][1] && board[row][0] == board[row][2]) {
+ if (board[row][0] == 'X') {
+ return -10;
+ } else if (board[row][0] == 'O') {
+ return 10;
+ }
+ }
+ }
+
+ for (int col = 0; col < 3; col++) {
+ if (board[0][col] == board[1][col] && board[0][col] == board[2][col]) {
+ if (board[0][col] == 'X') {
+ return -10;
+ } else if (board[0][col] == 'O') {
+ return 10;
+ }
+ }
+ }
+
+ if (board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
+ if (board[0][0] == 'X') {
+ return -10;
+ } else if (board[0][0] == 'O') {
+ return 10;
+ }
+ } else if (board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
+ if (board[0][2] == 'X') {
+ return -10;
+ } else if (board[0][2] == 'O') {
+ return 10;
+ }
+ }
+
+ return 0;
+ }
+ public static int alphabeta(char[][] board, int alpha, int beta, boolean max, int depth) {
+ int score = getBoardRanking(board);
+ if (score == 10 || score == -10) return score;
+ if (!hasMovesLeft(board)) return 0;
+
+ if (max) {
+ int bestScore = Integer.MIN_VALUE;
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 3; col++) {
+ if (board[row][col] == '\0') {
+ board[row][col] = 'O';
+ bestScore = Math.max(bestScore, alphabeta(board, alpha, beta, false, depth + 1));
+ board[row][col] = '\0';
+ alpha = Math.max(alpha, bestScore);
+ if (beta <= alpha) break; // Pruning
+ }
+ }
+ }
+ return bestScore - depth;
+ } else {
+ int bestScore = Integer.MAX_VALUE;
+ for (int row = 0; row < 3; row++) {
+ for (int col = 0; col < 3; col++) {
+ if (board[row][col] == '\0') {
+ board[row][col] = 'X';
+ bestScore = Math.min(bestScore, alphabeta(board, alpha, beta, true, depth + 1));
+ board[row][col] = '\0';
+ beta = Math.min(beta, bestScore);
+ if (beta <= alpha) break; // Pruning
+ }
+ }
+ }
+ return bestScore + depth;
+ }
+ }
+} \ No newline at end of file