aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java10
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java6
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java1
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java170
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java181
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java71
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java19
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java24
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java5
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java)2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java)14
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java1
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java18
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java87
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Http.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Location.java116
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Utils.java80
21 files changed, 750 insertions, 105 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index fc72ea3f..edd4241e 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -10,25 +10,23 @@ import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra;
import de.hysky.skyblocker.skyblock.chat.ChatRulesHandler;
import de.hysky.skyblocker.skyblock.dungeon.*;
import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen;
+import de.hysky.skyblocker.skyblock.dungeon.puzzle.*;
import de.hysky.skyblocker.skyblock.dungeon.puzzle.boulder.Boulder;
-import de.hysky.skyblocker.skyblock.dungeon.puzzle.CreeperBeams;
-import de.hysky.skyblocker.skyblock.dungeon.puzzle.DungeonBlaze;
-import de.hysky.skyblocker.skyblock.dungeon.puzzle.TicTacToe;
import de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard.Waterboard;
import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
import de.hysky.skyblocker.skyblock.dungeon.secrets.SecretsTracker;
import de.hysky.skyblocker.skyblock.dwarven.CrystalsHud;
import de.hysky.skyblocker.skyblock.dwarven.CrystalsLocationsManager;
import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud;
-import de.hysky.skyblocker.skyblock.end.TheEnd;
import de.hysky.skyblocker.skyblock.end.BeaconHighlighter;
+import de.hysky.skyblocker.skyblock.end.TheEnd;
import de.hysky.skyblocker.skyblock.item.*;
import de.hysky.skyblocker.skyblock.item.tooltip.BackpackPreview;
import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
import de.hysky.skyblocker.skyblock.quicknav.QuickNav;
import de.hysky.skyblocker.skyblock.rift.TheRift;
-import de.hysky.skyblocker.skyblock.searchOverlay.SearchOverManager;
+import de.hysky.skyblocker.skyblock.searchoverlay.SearchOverManager;
import de.hysky.skyblocker.skyblock.shortcut.Shortcuts;
import de.hysky.skyblocker.skyblock.special.SpecialEffects;
import de.hysky.skyblocker.skyblock.tabhud.TabHud;
@@ -121,6 +119,8 @@ public class SkyblockerMod implements ClientModInitializer {
DungeonManager.init();
DungeonBlaze.init();
Waterboard.init();
+ Silverfish.init();
+ IceFill.init();
DungeonScore.init();
PartyFinderScreen.initClass();
ChestValue.init();
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index ea1f7d43..4a1fe614 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -723,6 +723,12 @@ public class SkyblockerConfig {
public boolean solveBoulder = true;
@SerialEntry
+ public boolean solveIceFill = true;
+
+ @SerialEntry
+ public boolean solveSilverfish = true;
+
+ @SerialEntry
public boolean fireFreezeStaffTimer = true;
@SerialEntry
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
index 5eb9a066..3ebd5d76 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
@@ -417,6 +417,20 @@ public class DungeonsCategory {
.controller(ConfigUtils::createBooleanController)
.build())
.option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveIceFill"))
+ .binding(defaults.locations.dungeons.solveIceFill,
+ () -> config.locations.dungeons.solveIceFill,
+ newValue -> config.locations.dungeons.solveIceFill = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveSilverfish"))
+ .binding(defaults.locations.dungeons.solveSilverfish,
+ () -> config.locations.dungeons.solveSilverfish,
+ newValue -> config.locations.dungeons.solveSilverfish = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
.name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer"))
.description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer.@Tooltip")))
.binding(defaults.locations.dungeons.fireFreezeStaffTimer,
diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
index 2a4c38a7..ceda9ed4 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
@@ -6,8 +6,8 @@ import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen;
import de.hysky.skyblocker.skyblock.item.HotbarSlotLock;
import de.hysky.skyblocker.skyblock.item.ItemProtection;
import de.hysky.skyblocker.skyblock.rift.HealingMelonIndicator;
-import de.hysky.skyblocker.skyblock.searchOverlay.OverlayScreen;
-import de.hysky.skyblocker.skyblock.searchOverlay.SearchOverManager;
+import de.hysky.skyblocker.skyblock.searchoverlay.OverlayScreen;
+import de.hysky.skyblocker.skyblock.searchoverlay.SearchOverManager;
import de.hysky.skyblocker.utils.Utils;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.client.MinecraftClient;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java
index 033a919d..5bc98894 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java
@@ -10,7 +10,6 @@ import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
public class Kuudra {
- public static final String LOCATION = "kuudra";
static KuudraPhase phase = KuudraPhase.OTHER;
public static void init() {
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java
new file mode 100644
index 00000000..57386674
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java
@@ -0,0 +1,170 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
+
+import com.google.common.primitives.Booleans;
+import com.mojang.brigadier.Command;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.debug.Debug;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.Room;
+import de.hysky.skyblocker.utils.Constants;
+import de.hysky.skyblocker.utils.render.RenderHelper;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.World;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class IceFill extends DungeonPuzzle {
+ public static final IceFill INSTANCE = new IceFill();
+ private static final float[] RED_COLOR_COMPONENTS = DyeColor.RED.getColorComponents();
+ private static final BlockPos[] BOARD_ORIGINS = {
+ new BlockPos(16, 70, 9),
+ new BlockPos(17, 71, 16),
+ new BlockPos(18, 72, 25)
+ };
+ private CompletableFuture<Void> solve;
+ private final boolean[][][] iceFillBoards = {new boolean[3][3], new boolean[5][5], new boolean[7][7]};
+ private final List<List<Vector2ic>> iceFillPaths = List.of(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+ private IceFill() {
+ super("ice-fill", "ice-path");
+ }
+
+ public static void init() {
+ if (Debug.debugEnabled()) {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName)
+ .then(literal("printBoard1").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[0])));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printBoard2").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[1])));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printBoard3").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[2])));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printPath1").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(0).toString()));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printPath2").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(1).toString()));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printPath3").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(2).toString()));
+ return Command.SINGLE_SUCCESS;
+ }))
+ )))));
+ }
+ }
+
+ private static String boardToString(boolean[][] iceFillBoard) {
+ StringBuilder sb = new StringBuilder();
+ for (boolean[] row : iceFillBoard) {
+ sb.append("\n");
+ for (boolean cell : row) {
+ sb.append(cell ? '#' : '.');
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public void tick(MinecraftClient client) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.solveIceFill || client.world == null || !DungeonManager.isCurrentRoomMatched() || solve != null && !solve.isDone()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+
+ solve = CompletableFuture.runAsync(() -> {
+ BlockPos.Mutable pos = new BlockPos.Mutable();
+ for (int i = 0; i < 3; i++) {
+ if (updateBoard(client.world, room, iceFillBoards[i], pos.set(BOARD_ORIGINS[i]))) {
+ solve(iceFillBoards[i], iceFillPaths.get(i));
+ }
+ }
+ });
+ }
+
+ private boolean updateBoard(World world, Room room, boolean[][] iceFillBoard, BlockPos.Mutable pos) {
+ boolean boardChanged = false;
+ for (int row = 0; row < iceFillBoard.length; pos.move(iceFillBoard[row].length, 0, -1), row++) {
+ for (int col = 0; col < iceFillBoard[row].length; pos.move(Direction.WEST), col++) {
+ BlockPos actualPos = room.relativeToActual(pos);
+ boolean isBlock = !world.getBlockState(actualPos).isAir();
+ if (iceFillBoard[row][col] != isBlock) {
+ iceFillBoard[row][col] = isBlock;
+ boardChanged = true;
+ }
+ }
+ }
+ return boardChanged;
+ }
+
+ void solve(boolean[][] iceFillBoard, List<Vector2ic> iceFillPath) {
+ Vector2ic start = new Vector2i(iceFillBoard.length - 1, iceFillBoard[0].length / 2);
+ int count = iceFillBoard.length * iceFillBoard[0].length - Arrays.stream(iceFillBoard).mapToInt(Booleans::countTrue).sum();
+
+ List<Vector2ic> newPath = solveDfs(iceFillBoard, count - 1, new ArrayList<>(List.of(start)), new HashSet<>(List.of(start)));
+ if (newPath != null) {
+ iceFillPath.clear();
+ iceFillPath.addAll(newPath);
+ }
+ }
+
+ private List<Vector2ic> solveDfs(boolean[][] iceFillBoard, int count, List<Vector2ic> path, Set<Vector2ic> visited) {
+ Vector2ic pos = path.get(path.size() - 1);
+ if (count == 0) {
+ if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2) {
+ return path;
+ } else {
+ return null;
+ }
+ }
+
+ Vector2ic[] newPosArray = {pos.add(1, 0, new Vector2i()), pos.add(-1, 0, new Vector2i()), pos.add(0, 1, new Vector2i()), pos.add(0, -1, new Vector2i())};
+ for (Vector2ic newPos : newPosArray) {
+ if (newPos.x() >= 0 && newPos.x() < iceFillBoard.length && newPos.y() >= 0 && newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) {
+ path.add(newPos);
+ visited.add(newPos);
+ List<Vector2ic> newPath = solveDfs(iceFillBoard, count - 1, path, visited);
+ if (newPath != null) {
+ return newPath;
+ }
+ path.remove(path.size() - 1);
+ visited.remove(newPos);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void render(WorldRenderContext context) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.solveIceFill || !DungeonManager.isCurrentRoomMatched()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+ for (int i = 0; i < 3; i++) {
+ renderPath(context, room, iceFillPaths.get(i), BOARD_ORIGINS[i]);
+ }
+ }
+
+ private void renderPath(WorldRenderContext context, Room room, List<Vector2ic> iceFillPath, BlockPos originPos) {
+ BlockPos.Mutable pos = new BlockPos.Mutable();
+ for (int i = 0; i < iceFillPath.size() - 1; i++) {
+ Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(originPos).move(-iceFillPath.get(i).y(), 0, -iceFillPath.get(i).x())));
+ Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.set(originPos).move(-iceFillPath.get(i + 1).y(), 0, -iceFillPath.get(i + 1).x())));
+ RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, end}, RED_COLOR_COMPONENTS, 1f, 5f, true);
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java
new file mode 100644
index 00000000..b5cbc8ee
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java
@@ -0,0 +1,181 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
+
+import com.mojang.brigadier.Command;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.debug.Debug;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.Room;
+import de.hysky.skyblocker.utils.Constants;
+import de.hysky.skyblocker.utils.render.RenderHelper;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.mob.SilverfishEntity;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Box;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.math.Vec3d;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class Silverfish extends DungeonPuzzle {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Silverfish.class);
+ public static final Silverfish INSTANCE = new Silverfish();
+ private static final float[] RED_COLOR_COMPONENTS = DyeColor.RED.getColorComponents();
+ final boolean[][] silverfishBoard = new boolean[17][17];
+ Vector2ic silverfishPos;
+ final List<Vector2ic> silverfishPath = new ArrayList<>();
+
+ private Silverfish() {
+ super("silverfish", "ice-silverfish-room");
+ }
+
+ public static void init() {
+ if (Debug.debugEnabled()) {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName)
+ .then(literal("printBoard").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.silverfishBoard)));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printPath").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.silverfishPath.toString()));
+ return Command.SINGLE_SUCCESS;
+ }))
+ )))));
+ }
+ }
+
+ private static String boardToString(boolean[][] silverfishBoard) {
+ StringBuilder sb = new StringBuilder();
+ for (boolean[] row : silverfishBoard) {
+ sb.append("\n");
+ for (boolean cell : row) {
+ sb.append(cell ? '#' : '.');
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public void tick(MinecraftClient client) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.solveSilverfish || client.world == null || !DungeonManager.isCurrentRoomMatched()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+
+ boolean boardChanged = false;
+ BlockPos.Mutable pos = new BlockPos.Mutable(23, 67, 24);
+ for (int row = 0; row < silverfishBoard.length; pos.move(silverfishBoard[row].length, 0, -1), row++) {
+ for (int col = 0; col < silverfishBoard[row].length; pos.move(Direction.WEST), col++) {
+ boolean isBlock = !client.world.getBlockState(room.relativeToActual(pos)).isAir();
+ if (silverfishBoard[row][col] != isBlock) {
+ silverfishBoard[row][col] = isBlock;
+ boardChanged = true;
+ }
+ }
+ }
+
+ List<SilverfishEntity> entities = client.world.getEntitiesByClass(SilverfishEntity.class, Box.of(Vec3d.ofCenter(room.relativeToActual(new BlockPos(15, 66, 16))), 16, 16, 16), silverfishEntity -> true);
+ if (entities.isEmpty()) {
+ return;
+ }
+ BlockPos newSilverfishBlockPos = room.actualToRelative(entities.get(0).getBlockPos());
+ Vector2ic newSilverfishPos = new Vector2i(24 - newSilverfishBlockPos.getZ(), 23 - newSilverfishBlockPos.getX());
+ if (newSilverfishPos.x() < 0 || newSilverfishPos.x() >= 17 || newSilverfishPos.y() < 0 || newSilverfishPos.y() >= 17) {
+ return;
+ }
+ boolean silverfishChanged = !newSilverfishPos.equals(silverfishPos);
+ if (silverfishChanged) {
+ silverfishPos = newSilverfishPos;
+ }
+ if (silverfishChanged || boardChanged) {
+ solve();
+ }
+ }
+
+ void solve() {
+ if (silverfishPos == null) {
+ return;
+ }
+ Set<Vector2ic> visited = new HashSet<>();
+ Queue<List<Vector2ic>> queue = new ArrayDeque<>();
+ queue.add(List.of(silverfishPos));
+ visited.add(silverfishPos);
+ while (!queue.isEmpty()) {
+ List<Vector2ic> path = queue.poll();
+ Vector2ic pos = path.get(path.size() - 1);
+ if (pos.x() == 0 && pos.y() >= 7 && pos.y() <= 9) {
+ silverfishPath.clear();
+ silverfishPath.addAll(path);
+ return;
+ }
+
+ Vector2i posMutable = new Vector2i(pos);
+ while (posMutable.x() < 17 && !silverfishBoard[posMutable.x()][posMutable.y()]) {
+ posMutable.add(1, 0);
+ }
+ posMutable.add(-1, 0);
+ addQueue(visited, queue, path, posMutable);
+
+ posMutable = new Vector2i(pos);
+ while (posMutable.x() >= 0 && !silverfishBoard[posMutable.x()][posMutable.y()]) {
+ posMutable.add(-1, 0);
+ }
+ posMutable.add(1, 0);
+ addQueue(visited, queue, path, posMutable);
+
+ posMutable = new Vector2i(pos);
+ while (posMutable.y() < 17 && !silverfishBoard[posMutable.x()][posMutable.y()]) {
+ posMutable.add(0, 1);
+ }
+ posMutable.add(0, -1);
+ addQueue(visited, queue, path, posMutable);
+
+ posMutable = new Vector2i(pos);
+ while (posMutable.y() >= 0 && !silverfishBoard[posMutable.x()][posMutable.y()]) {
+ posMutable.add(0, -1);
+ }
+ posMutable.add(0, 1);
+ addQueue(visited, queue, path, posMutable);
+ }
+ }
+
+ private void addQueue(Set<Vector2ic> visited, Queue<List<Vector2ic>> queue, List<Vector2ic> path, Vector2ic newPos) {
+ if (!visited.contains(newPos)) {
+ List<Vector2ic> newPath = new ArrayList<>(path);
+ newPath.add(newPos);
+ queue.add(newPath);
+ visited.add(newPos);
+ }
+ }
+
+ @Override
+ public void render(WorldRenderContext context) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.solveSilverfish || !DungeonManager.isCurrentRoomMatched() || silverfishPath.isEmpty()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+ BlockPos.Mutable pos = new BlockPos.Mutable();
+ for (int i = 0; i < silverfishPath.size() - 1; i++) {
+ Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i).y(), 67, 24 - silverfishPath.get(i).x())));
+ Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i + 1).y(), 67, 24 - silverfishPath.get(i + 1).x())));
+ RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, end}, RED_COLOR_COMPONENTS, 1f, 5f, true);
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ for (boolean[] silverfishBoardRow : silverfishBoard) {
+ Arrays.fill(silverfishBoardRow, false);
+ }
+ silverfishPos = null;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java
index 3244996a..ba4b9a5f 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java
@@ -88,17 +88,20 @@ public class Waterboard extends DungeonPuzzle {
private Waterboard() {
super("waterboard", "water-puzzle");
- UseBlockCallback.EVENT.register(this::onUseBlock);
+ }
+
+ public static void init() {
+ UseBlockCallback.EVENT.register(INSTANCE::onUseBlock);
if (Debug.debugEnabled()) {
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(puzzleName)
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName)
.then(literal("printBoard").executes(context -> {
- context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(cells)));
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.cells)));
return Command.SINGLE_SUCCESS;
})).then(literal("printDoors").executes(context -> {
context.getSource().sendFeedback(Constants.PREFIX.get().append(Integer.toBinaryString(INSTANCE.doors)));
return Command.SINGLE_SUCCESS;
})).then(literal("printSimulationResults").then(argument("combination", IntegerArgumentType.integer(0, 63)).executes(context -> {
- context.getSource().sendFeedback(Constants.PREFIX.get().append(results[IntegerArgumentType.getInteger(context, "combination")].toString()));
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.results[IntegerArgumentType.getInteger(context, "combination")].toString()));
return Command.SINGLE_SUCCESS;
}))).then(literal("printCurrentCombination").executes(context -> {
context.getSource().sendFeedback(Constants.PREFIX.get().append(Integer.toBinaryString(INSTANCE.currentCombination)));
@@ -111,9 +114,6 @@ public class Waterboard extends DungeonPuzzle {
}
}
- public static void init() {
- }
-
private static String boardToString(Cell[][] cells) {
StringBuilder sb = new StringBuilder();
for (Cell[] row : cells) {
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java
index 68f09344..2f748792 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java
@@ -10,9 +10,6 @@ import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.Http;
import de.hysky.skyblocker.utils.Http.ApiResponse;
import de.hysky.skyblocker.utils.Utils;
-import it.unimi.dsi.fastutil.ints.IntIntPair;
-import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
-import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.minecraft.client.MinecraftClient;
@@ -24,6 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -43,8 +41,6 @@ public class SecretsTracker {
ClientReceiveMessageEvents.GAME.register(SecretsTracker::onMessage);
}
- //If -1 is somehow encountered, it would be very rare, so I just disregard its possibility for now
- //people would probably recognize if it was inaccurate so yeah
private static void calculate(RunPhase phase) {
switch (phase) {
case START -> CompletableFuture.runAsync(() -> {
@@ -57,11 +53,11 @@ public class SecretsTracker {
//The player name will be blank if there isn't a player at that index
if (!playerName.isEmpty()) {
- //If the player was a part of the last run (and didn't have -1 secret count) and that run ended less than 5 mins ago then copy the secrets over
- if (lastRun != null && System.currentTimeMillis() <= lastRunEnded + 300_000 && lastRun.secretCounts().getOrDefault(playerName, -1) != -1) {
- newlyStartedRun.secretCounts().put(playerName, lastRun.secretCounts().getInt(playerName));
+ //If the player was a part of the last run, had non-empty secret data and that run ended less than 5 mins ago then copy the secret data over
+ if (lastRun != null && System.currentTimeMillis() <= lastRunEnded + 300_000 && lastRun.playersSecretData().getOrDefault(playerName, SecretData.EMPTY) != SecretData.EMPTY) {
+ newlyStartedRun.playersSecretData().put(playerName, lastRun.playersSecretData().get(playerName));
} else {
- newlyStartedRun.secretCounts().put(playerName, getPlayerSecrets(playerName).leftInt());
+ newlyStartedRun.playersSecretData().put(playerName, getPlayerSecrets(playerName));
}
}
}
@@ -72,22 +68,23 @@ public class SecretsTracker {
case END -> CompletableFuture.runAsync(() -> {
//In case the game crashes from something
if (currentRun != null) {
- Object2ObjectOpenHashMap<String, IntIntPair> secretsFound = new Object2ObjectOpenHashMap<>();
+ Object2ObjectOpenHashMap<String, SecretData> secretsFound = new Object2ObjectOpenHashMap<>();
//Update secret counts
- for (Entry<String> entry : currentRun.secretCounts().object2IntEntrySet()) {
+ for (Entry<String, SecretData> entry : currentRun.playersSecretData().entrySet()) {
String playerName = entry.getKey();
- int startingSecrets = entry.getIntValue();
- IntIntPair secretsNow = getPlayerSecrets(playerName);
- int secretsPlayerFound = secretsNow.leftInt() - startingSecrets;
+ SecretData startingSecrets = entry.getValue();
+ SecretData secretsNow = getPlayerSecrets(playerName);
+ int secretsPlayerFound = secretsNow.secrets() - startingSecrets.secrets();
- secretsFound.put(playerName, IntIntPair.of(secretsPlayerFound, secretsNow.rightInt()));
- entry.setValue(secretsNow.leftInt());
+ //Add an entry to the secretsFound map with the data - if the secret data from now or the start was cached a warning will be shown
+ secretsFound.put(playerName, secretsNow.updated(secretsPlayerFound, startingSecrets.cached() || secretsNow.cached()));
+ entry.setValue(secretsNow);
}
//Print the results all in one go, so its clean and less of a chance of it being broken up
- for (Map.Entry<String, IntIntPair> entry : secretsFound.entrySet()) {
- sendResultMessage(entry.getKey(), entry.getValue().leftInt(), entry.getValue().rightInt(), true);
+ for (Map.Entry<String, SecretData> entry : secretsFound.entrySet()) {
+ sendResultMessage(entry.getKey(), entry.getValue(), true);
}
//Swap the current and last run as well as mark the run end time
@@ -95,30 +92,31 @@ public class SecretsTracker {
lastRun = currentRun;
currentRun = null;
} else {
- sendResultMessage(null, -1, -1, false);
+ sendResultMessage(null, null, false);
}
});
}
}
- private static void sendResultMessage(String player, int secrets, int cacheAge, boolean success) {
+ private static void sendResultMessage(String player, SecretData secretData, boolean success) {
+ @SuppressWarnings("resource")
PlayerEntity playerEntity = MinecraftClient.getInstance().player;
if (playerEntity != null) {
if (success) {
- playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.feedback", Text.literal(player).withColor(0xf57542), "§7" + secrets, getCacheText(cacheAge))));
+ playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.feedback", Text.literal(player).withColor(0xf57542), "§7" + secretData.secrets(), getCacheText(secretData.cached(), secretData.cacheAge()))));
} else {
playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.failFeedback")));
}
}
}
- private static Text getCacheText(int cacheAge) {
- return Text.literal("\u2139").styled(style -> style.withColor(cacheAge == -1 ? 0x218bff : 0xeac864).withHoverEvent(
- new HoverEvent(HoverEvent.Action.SHOW_TEXT, cacheAge == -1 ? Text.translatable("skyblocker.api.cache.MISS") : Text.translatable("skyblocker.api.cache.HIT", cacheAge))));
+ private static Text getCacheText(boolean cached, int cacheAge) {
+ return Text.literal("\u2139").styled(style -> style.withColor(cached ? 0xeac864 : 0x218bff).withHoverEvent(
+ new HoverEvent(HoverEvent.Action.SHOW_TEXT, cached ? Text.translatable("skyblocker.api.cache.HIT", cacheAge) : Text.translatable("skyblocker.api.cache.MISS"))));
}
private static void onMessage(Text text, boolean overlay) {
- if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.playerSecretsTracker) {
+ if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.playerSecretsTracker && !overlay) {
String message = Formatting.strip(text.getString());
try {
@@ -136,35 +134,44 @@ public class SecretsTracker {
return matcher != null ? matcher.group("name") : "";
}
- private static IntIntPair getPlayerSecrets(String name) {
+ private static SecretData getPlayerSecrets(String name) {
String uuid = ApiUtils.name2Uuid(name);
if (!uuid.isEmpty()) {
try (ApiResponse response = Http.sendHypixelRequest("player", "?uuid=" + uuid)) {
- return IntIntPair.of(getSecretCountFromAchievements(JsonParser.parseString(response.content()).getAsJsonObject()), response.age());
+ return new SecretData(getSecretCountFromAchievements(JsonParser.parseString(response.content()).getAsJsonObject()), response.cached(), response.age());
} catch (Exception e) {
LOGGER.error("[Skyblocker] Encountered an error while trying to fetch {} secret count!", name + "'s", e);
}
}
- return IntIntPair.of(-1, -1);
+ return SecretData.EMPTY;
}
/**
* Gets a player's secret count from their hypixel achievements
*/
private static int getSecretCountFromAchievements(JsonObject playerJson) {
- JsonObject player = playerJson.get("player").getAsJsonObject();
- JsonObject achievements = (player.has("achievements")) ? player.get("achievements").getAsJsonObject() : null;
+ JsonObject player = playerJson.getAsJsonObject("player");
+ JsonObject achievements = player.has("achievements") ? player.getAsJsonObject("achievements") : null;
return (achievements != null && achievements.has("skyblock_treasure_hunter")) ? achievements.get("skyblock_treasure_hunter").getAsInt() : 0;
}
/**
* This will either reflect the value at the start or the end depending on when this is called
*/
- private record TrackedRun(Object2IntOpenHashMap<String> secretCounts) {
+ private record TrackedRun(Object2ObjectOpenHashMap<String, SecretData> playersSecretData) {
private TrackedRun() {
- this(new Object2IntOpenHashMap<>());
+ this(new Object2ObjectOpenHashMap<>());
+ }
+ }
+
+ private record SecretData(int secrets, boolean cached, int cacheAge) {
+ private static final SecretData EMPTY = new SecretData(0, false, 0);
+
+ //If only we had Derived Record Creation :( - https://bugs.openjdk.org/browse/JDK-8321133
+ private SecretData updated(int secrets, boolean cached) {
+ return new SecretData(secrets, cached, this.cacheAge);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java
index 5913a3c6..608873c0 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java
@@ -203,8 +203,8 @@ public class DwarvenHud {
}
public static void update() {
- if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions || (!Utils.isInCrystalHollows()
- && !Utils.isInDwarvenMines()))
+ if (client.player == null || client.getNetworkHandler() == null || (!SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions && !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder)
+ || (!Utils.isInCrystalHollows() && !Utils.isInDwarvenMines()))
return;
commissionList = new ArrayList<>();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java b/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java
index f69fca6d..6c89a07c 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java
@@ -3,8 +3,11 @@ package de.hysky.skyblocker.skyblock.end;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.render.RenderHelper;
+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.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
@@ -20,6 +23,20 @@ public class BeaconHighlighter {
*/
public static void init() {
WorldRenderEvents.AFTER_TRANSLUCENT.register(BeaconHighlighter::render);
+ ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> reset());
+ ClientReceiveMessageEvents.GAME.register(BeaconHighlighter::onMessage);
+ }
+
+ private static void reset() {
+ beaconPositions.clear();
+ }
+
+ private static void onMessage(Text text, boolean overlay) {
+ if (Utils.isInTheEnd() && !overlay) {
+ String message = text.getString();
+
+ if (message.contains("SLAYER QUEST COMPLETE!") || message.contains("NICE! SLAYER BOSS SLAIN!")) reset();
+ }
}
/**
@@ -28,7 +45,7 @@ public class BeaconHighlighter {
*
* @param context An instance of WorldRenderContext for the RenderHelper to use
*/
- public static void render(WorldRenderContext context) {
+ private static void render(WorldRenderContext context) {
if (Utils.isInTheEnd() && SkyblockerConfigManager.get().slayer.endermanSlayer.highlightBeacons) {
for (BlockPos pos : beaconPositions) {
RenderHelper.renderFilled(context, pos, RED_COLOR_COMPONENTS, 0.5f, false);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java
index 19f2e6fd..d5be7eee 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java
@@ -140,15 +140,37 @@ public class ItemTooltip {
}
}
+ final Map<Integer, String> itemTierFloors = new HashMap<>() {{
+ put(1, "F1");
+ put(2, "F2");
+ put(3, "F3");
+ put(4, "F4/M1");
+ put(5, "F5/M2");
+ put(6, "F6/M3");
+ put(7, "F7/M4");
+ put(8, "M5");
+ put(9, "M6");
+ put(10, "M7");
+ }};
+
if (SkyblockerConfigManager.get().general.dungeonQuality) {
NbtCompound ea = ItemUtils.getExtraAttributes(stack);
if (ea != null && ea.contains("baseStatBoostPercentage")) {
int baseStatBoostPercentage = ea.getInt("baseStatBoostPercentage");
- if (baseStatBoostPercentage == 50) {
+ boolean maxQuality = baseStatBoostPercentage == 50;
+ if (maxQuality) {
lines.add(Text.literal(String.format("%-17s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.RED).formatted(Formatting.BOLD));
} else {
lines.add(Text.literal(String.format("%-21s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.BLUE));
}
+ if (ea.contains("item_tier")) { // sometimes it just isn't here?
+ int itemTier = ea.getInt("item_tier");
+ if (maxQuality) {
+ lines.add(Text.literal(String.format("%-17s", "Floor Tier:") + itemTier + " (" + itemTierFloors.get(itemTier) + ")").formatted(Formatting.RED).formatted(Formatting.BOLD));
+ } else {
+ lines.add(Text.literal(String.format("%-21s", "Floor Tier:") + itemTier + " (" + itemTierFloors.get(itemTier) + ")").formatted(Formatting.BLUE));
+ }
+ }
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java
index 02b694b6..7413e06f 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java
@@ -8,11 +8,6 @@ import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
public class TheRift {
- /**
- * @see de.hysky.skyblocker.utils.Utils#isInTheRift() Utils#isInTheRift().
- */
- public static final String LOCATION = "rift";
-
public static void init() {
WorldRenderEvents.AFTER_TRANSLUCENT.register(MirrorverseWaypoints::render);
WorldRenderEvents.AFTER_TRANSLUCENT.register(EffigyWaypoints::render);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java
index e1545c6c..b8907e27 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock.searchOverlay;
+package de.hysky.skyblocker.skyblock.searchoverlay;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import net.minecraft.client.gui.DrawContext;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java
index b2a453a9..1d740601 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock.searchOverlay;
+package de.hysky.skyblocker.skyblock.searchoverlay;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -96,13 +96,15 @@ public class SearchOverManager {
HashMap<String, String> namesToId = new HashMap<>();
//get bazaar items
- try (Http.ApiResponse response = Http.sendHypixelRequest("skyblock/bazaar", "")) {
- JsonObject products = JsonParser.parseString(response.content()).getAsJsonObject().get("products").getAsJsonObject();
+ try {
+ if (TooltipInfoType.BAZAAR.getData() == null) TooltipInfoType.BAZAAR.run();
+
+ JsonObject products = TooltipInfoType.BAZAAR.getData();
for (Map.Entry<String, JsonElement> entry : products.entrySet()) {
if (entry.getValue().isJsonObject()) {
JsonObject product = entry.getValue().getAsJsonObject();
- String id = product.get("product_id").getAsString();
- int sellVolume = product.get("quick_status").getAsJsonObject().get("sellVolume").getAsInt();
+ String id = product.get("id").getAsString();
+ int sellVolume = product.get("sellVolume").getAsInt();
if (sellVolume == 0)
continue; //do not add items that do not sell e.g. they are not actual in the bazaar
Matcher matcher = BAZAAR_ENCHANTMENT_PATTERN.matcher(id);
@@ -334,7 +336,7 @@ public class SearchOverManager {
}
}
- static Pair<String, String> splitString(String s) {
+ public static Pair<String, String> splitString(String s) {
if (s.length() <= 15) {
return Pair.of(s, "");
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java
index 82394a78..96ab35d5 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java
@@ -60,4 +60,5 @@ public class Ico {
public static final ItemStack EXPERIENCE_BOTTLE = new ItemStack(Items.EXPERIENCE_BOTTLE);
public static final ItemStack PINK_DYE = new ItemStack(Items.PINK_DYE);
public static final ItemStack ENCHANTED_BOOK = new ItemStack(Items.ENCHANTED_BOOK);
+ public static final ItemStack SPIDER_EYE = new ItemStack(Items.SPIDER_EYE);
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
index 6f40f5a8..309ba9ca 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
@@ -1,16 +1,19 @@
package de.hysky.skyblocker.skyblock.tabhud.widget;
import de.hysky.skyblocker.skyblock.tabhud.util.Ico;
+import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
+import java.util.regex.Pattern;
+
// this widget shows info about the secrets of the dungeon
public class DungeonSecretWidget extends Widget {
- private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE,
- Formatting.BOLD);
+ private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE, Formatting.BOLD);
+ private static final Pattern DISCOVERIES = Pattern.compile("Discoveries: (\\d+)");
public DungeonSecretWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
@@ -18,9 +21,12 @@ public class DungeonSecretWidget extends Widget {
@Override
public void updateContent() {
- this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31);
- this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32);
-
+ if (PlayerListMgr.regexAt(31, DISCOVERIES) != null) {
+ this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 32);
+ this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 33);
+ } else {
+ this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31);
+ this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32);
+ }
}
-
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java
new file mode 100644
index 00000000..6751ea5d
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java
@@ -0,0 +1,87 @@
+package de.hysky.skyblocker.skyblock.tabhud.widget;
+
+import de.hysky.skyblocker.skyblock.tabhud.util.Ico;
+import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.Arrays;
+
+/**
+ * This widget shows info about the Spider's Den server
+ */
+public class SpidersDenServerWidget extends Widget {
+
+ private static final MutableText TITLE =
+ Text.literal("Server Info").formatted(Formatting.DARK_AQUA, Formatting.BOLD);
+
+ /**
+ * Broodmother Mini-Boss tab states
+ */
+ private enum BroodmotherState {
+ SOON("Soon", Formatting.GOLD),
+ AWAKENING("Awakening", Formatting.GOLD),
+ IMMINENT("Imminent", Formatting.DARK_RED),
+ ALIVE("Alive!", Formatting.DARK_RED),
+ SLAIN("Slain", Formatting.YELLOW),
+ DORMANT("Dormant", Formatting.YELLOW),
+ UNKNOWN("Unknown", Formatting.GRAY);
+
+ private final String text;
+ private final Formatting formatting;
+
+ BroodmotherState(String text, Formatting formatting) {
+ this.text = text;
+ this.formatting = formatting;
+ }
+
+ public String text() {
+ return this.text;
+ }
+
+ public Formatting formatting() {
+ return this.formatting;
+ }
+
+ /**
+ * Returns a state object by text
+ *
+ * @param text text state from tab
+ * @return Broodmother State object
+ */
+ public static BroodmotherState from(String text) {
+ return Arrays.stream(BroodmotherState.values())
+ .filter(broodmotherState -> text.equals(broodmotherState.text())).findFirst().orElse(UNKNOWN);
+ }
+ }
+
+ public SpidersDenServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+
+ /**
+ * Parses the Broodmother string from tab and returns a state object.
+ *
+ * @return Broodmother State object
+ */
+ private static BroodmotherState parseTab() {
+ String state = PlayerListMgr.strAt(45);
+ if (state == null || !state.contains(": ")) return BroodmotherState.UNKNOWN;
+
+ return BroodmotherState.from(state.split(": ")[1]);
+ }
+
+ /**
+ * Updates the information in the widget.
+ */
+ @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);
+
+ BroodmotherState broodmotherState = parseTab();
+ this.addSimpleIcoText(Ico.SPIDER_EYE, "Broodmother: ", broodmotherState.formatting(), broodmotherState.text());
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/Http.java b/src/main/java/de/hysky/skyblocker/utils/Http.java
index 17079d15..58deced2 100644
--- a/src/main/java/de/hysky/skyblocker/utils/Http.java
+++ b/src/main/java/de/hysky/skyblocker/utils/Http.java
@@ -53,7 +53,7 @@ public class Http {
String body = new String(decodedInputStream.readAllBytes());
HttpHeaders headers = response.headers();
- return new ApiResponse(body, response.statusCode(), getCacheStatus(headers), getAge(headers));
+ return new ApiResponse(body, response.statusCode(), getCacheStatuses(headers), getAge(headers));
}
public static HttpHeaders sendHeadRequest(String url) throws IOException, InterruptedException {
@@ -115,12 +115,12 @@ public class Http {
}
/**
- * Returns the cache status of the resource
+ * Returns the cache statuses of the resource. All possible cache status values conform to Cloudflare's.
*
- * @see <a href="https://developers.cloudflare.com/cache/concepts/default-cache-behavior/#cloudflare-cache-responses">Cloudflare Cache Docs</a>
+ * @see <a href="https://developers.cloudflare.com/cache/concepts/cache-responses/">Cloudflare Cache Docs</a>
*/
- private static String getCacheStatus(HttpHeaders headers) {
- return headers.firstValue("CF-Cache-Status").orElse("UNKNOWN");
+ private static String[] getCacheStatuses(HttpHeaders headers) {
+ return new String[] { headers.firstValue("CF-Cache-Status").orElse("UNKNOWN"), headers.firstValue("Local-Cache-Status").orElse("UNKNOWN") };
}
private static int getAge(HttpHeaders headers) {
@@ -128,7 +128,7 @@ public class Http {
}
//TODO If ever needed, we could just replace cache status with the response headers and go from there
- public record ApiResponse(String content, int statusCode, String cacheStatus, int age) implements AutoCloseable {
+ public record ApiResponse(String content, int statusCode, String[] cacheStatuses, int age) implements AutoCloseable {
public boolean ok() {
return statusCode == 200;
@@ -139,7 +139,7 @@ public class Http {
}
public boolean cached() {
- return cacheStatus.equals("HIT");
+ return cacheStatuses[0].equals("HIT") || cacheStatuses[1].equals("HIT");
}
@Override
diff --git a/src/main/java/de/hysky/skyblocker/utils/Location.java b/src/main/java/de/hysky/skyblocker/utils/Location.java
new file mode 100644
index 00000000..bd2773fd
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/Location.java
@@ -0,0 +1,116 @@
+package de.hysky.skyblocker.utils;
+
+import java.util.Arrays;
+
+/**
+ * All Skyblock locations
+ */
+public enum Location {
+ /**
+ * mode: dynamic
+ */
+ PRIVATE_ISLAND("dynamic"),
+ /**
+ * mode: garden
+ */
+ GARDEN("garden"),
+ /**
+ * mode: hub
+ */
+ HUB("hub"),
+ /**
+ * mode: farming_1
+ */
+ THE_FARMING_ISLAND("farming_1"),
+ /**
+ * mode: foraging_1
+ */
+ THE_PARK("foraging_1"),
+ /**
+ * mode: combat_1
+ */
+ SPIDERS_DEN("combat_1"),
+ /**
+ * mode: combat_2
+ */
+ BLAZING_FORTRESS("combat_2"),
+ /**
+ * mode: combat_3
+ */
+ THE_END("combat_3"),
+ /**
+ * mode: crimson_isle
+ */
+ CRIMSON_ISLE("crimson_isle"),
+ /**
+ * mode: mining_1
+ */
+ GOLD_MINE("mining_1"),
+ /**
+ * mode: mining_2
+ */
+ DEEP_CAVERNS("mining_2"),
+ /**
+ * mode: mining_3
+ */
+ DWARVEN_MINES("mining_3"),
+ /**
+ * mode: dungeon_hub
+ */
+ DUNGEON_HUB("dungeon_hub"),
+ /**
+ * mode: winter
+ */
+ WINTER_ISLAND("winter"),
+ /**
+ * mode: rift
+ */
+ THE_RIFT("rift"),
+ /**
+ * mode: dark_auction
+ */
+ DARK_AUCTION("dark_auction"),
+ /**
+ * mode: crystal_hollows
+ */
+ CRYSTAL_HOLLOWS("crystal_hollows"),
+ /**
+ * mode: dungeon
+ */
+ DUNGEON("dungeon"),
+ /**
+ * mode: kuudra
+ */
+ KUUDRAS_HOLLOW("kuudra"),
+ /**
+ * Unknown Skyblock location
+ */
+ UNKNOWN("unknown");
+
+ /**
+ * location id from <a href="https://api.hypixel.net/v2/resources/games">Hypixel API</a>
+ */
+ private final String id;
+
+ /**
+ * @param id location id from <a href="https://api.hypixel.net/v2/resources/games">Hypixel API</a>
+ */
+ Location(String id) {
+ this.id = id;
+ }
+
+ /**
+ * @return location id
+ */
+ public String id() {
+ return this.id;
+ }
+
+ /**
+ * @param id location id from <a href="https://api.hypixel.net/v2/resources/games">Hypixel API</a>
+ * @return location object
+ */
+ public static Location from(String id) {
+ return Arrays.stream(Location.values()).filter(loc -> id.equals(loc.id())).findFirst().orElse(UNKNOWN);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java
index cd739a0c..08d0b167 100644
--- a/src/main/java/de/hysky/skyblocker/utils/Utils.java
+++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java
@@ -3,10 +3,8 @@ package de.hysky.skyblocker.utils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import de.hysky.skyblocker.events.SkyblockEvents;
-import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra;
import de.hysky.skyblocker.skyblock.item.MuseumItemCache;
import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
-import de.hysky.skyblocker.skyblock.rift.TheRift;
import de.hysky.skyblocker.utils.scheduler.MessageScheduler;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@@ -37,15 +35,17 @@ import java.util.concurrent.CompletableFuture;
public class Utils {
private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", "");
- private static final String DUNGEONS_LOCATION = "dungeon";
- private static final String CRYSTAL_HOLLOWS_LOCATION = "crystal_hollows";
- private static final String DWARVEN_MINES_LOCATION = "mining_3";
private static final String PROFILE_PREFIX = "Profile: ";
private static boolean isOnHypixel = false;
private static boolean isOnSkyblock = false;
private static boolean isInjected = false;
/**
+ * Current Skyblock location (from /locraw)
+ */
+ @NotNull
+ private static Location location = Location.UNKNOWN;
+ /**
* The profile name parsed from the player list.
*/
@NotNull
@@ -88,31 +88,30 @@ public class Utils {
}
public static boolean isInDungeons() {
- return getLocationRaw().equals(DUNGEONS_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment();
+ return location == Location.DUNGEON || FabricLoader.getInstance().isDevelopmentEnvironment();
}
public static boolean isInCrystalHollows() {
- return getLocationRaw().equals(CRYSTAL_HOLLOWS_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment();
+ return location == Location.CRYSTAL_HOLLOWS || FabricLoader.getInstance().isDevelopmentEnvironment();
}
public static boolean isInDwarvenMines() {
- return getLocationRaw().equals(DWARVEN_MINES_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment();
+ return location == Location.DWARVEN_MINES || FabricLoader.getInstance().isDevelopmentEnvironment();
}
public static boolean isInTheRift() {
- return getLocationRaw().equals(TheRift.LOCATION);
+ return location == Location.THE_RIFT;
}
/**
* @return if the player is in the end island
*/
public static boolean isInTheEnd() {
- // /locraw returns "combat_3" when in The End
- return getLocationRaw().equals("combat_3");
+ return location == Location.THE_END;
}
public static boolean isInKuudra() {
- return getLocationRaw().equals(Kuudra.LOCATION);
+ return location == Location.KUUDRAS_HOLLOW;
}
public static boolean isInjected() {
@@ -133,6 +132,14 @@ public class Utils {
}
/**
+ * @return the location parsed from /locraw.
+ */
+ @NotNull
+ public static Location getLocation() {
+ return location;
+ }
+
+ /**
* @return the server parsed from /locraw.
*/
@NotNull
@@ -376,31 +383,45 @@ public class Utils {
}
/**
+ * Parses /locraw chat message and updates {@link #server}, {@link #gameType}, {@link #locationRaw}, {@link #map}
+ * and {@link #location}
+ *
+ * @param message json message from chat
+ */
+ private static void parseLocRaw(String message) {
+ JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject();
+
+ if (locRaw.has("server")) {
+ server = locRaw.get("server").getAsString();
+ }
+ if (locRaw.has("gameType")) {
+ gameType = locRaw.get("gameType").getAsString();
+ }
+ if (locRaw.has("mode")) {
+ locationRaw = locRaw.get("mode").getAsString();
+ location = Location.from(locationRaw);
+ } else {
+ location = Location.UNKNOWN;
+ }
+ if (locRaw.has("map")) {
+ map = locRaw.get("map").getAsString();
+ }
+ }
+
+ /**
* Parses the /locraw reply from the server and updates the player's profile id
*
* @return not display the message in chat if the command is sent by the mod
*/
public static boolean onChatMessage(Text text, boolean overlay) {
String message = text.getString();
- if (message.startsWith("{\"server\":") && message.endsWith("}")) {
- JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject();
- if (locRaw.has("server")) {
- server = locRaw.get("server").getAsString();
- if (locRaw.has("gameType")) {
- gameType = locRaw.get("gameType").getAsString();
- }
- if (locRaw.has("mode")) {
- locationRaw = locRaw.get("mode").getAsString();
- }
- if (locRaw.has("map")) {
- map = locRaw.get("map").getAsString();
- }
- boolean shouldFilter = !sentLocRaw;
- sentLocRaw = false;
+ if (message.startsWith("{\"server\":") && message.endsWith("}")) {
+ parseLocRaw(message);
+ boolean shouldFilter = !sentLocRaw;
+ sentLocRaw = false;
- return shouldFilter;
- }
+ return shouldFilter;
}
if (isOnSkyblock && message.startsWith("Profile ID: ")) {
@@ -419,6 +440,7 @@ public class Utils {
gameType = "";
locationRaw = "";
map = "";
+ location = Location.UNKNOWN;
}
private static void tickMayorCache(boolean refresh) {