diff options
Diffstat (limited to 'src/main/java/me')
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 |