From 77674c57a3980b9c6891f2f99c53dd29e858cf7c Mon Sep 17 00:00:00 2001 From: daroche <94007359+GatienDoesStuff@users.noreply.github.com> Date: Fri, 2 Jan 2026 12:36:37 +0100 Subject: [PATCH 1/2] Move goldor waypoints to terminal package --- .../skyblock/dungeon/{ => terminal}/GoldorWaypointsManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/de/hysky/skyblocker/skyblock/dungeon/{ => terminal}/GoldorWaypointsManager.java (99%) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/GoldorWaypointsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/GoldorWaypointsManager.java similarity index 99% rename from src/main/java/de/hysky/skyblocker/skyblock/dungeon/GoldorWaypointsManager.java rename to src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/GoldorWaypointsManager.java index 0855fae1c81..e5c6867c016 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/GoldorWaypointsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/GoldorWaypointsManager.java @@ -1,4 +1,4 @@ -package de.hysky.skyblocker.skyblock.dungeon; +package de.hysky.skyblocker.skyblock.dungeon.terminal; import com.google.gson.JsonArray; import com.google.gson.JsonParser; From 0c0b4a65961319bb029b7027a30a5f44243e013f Mon Sep 17 00:00:00 2001 From: daroche <94007359+GatienDoesStuff@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:06:41 +0100 Subject: [PATCH 2/2] Implement nametag-based correction for terminal waypoints Now uses the nametags shown at close range to correct eventual mistakes. Before this can be considered complete, one last edge case has to be taken into account : If a player starts a term, gets moved away from it, and completes it, we might tag the wrong terminal as "solved" (as we'll be closer to that one rather than the one we did solve). With this commit, if the nametag is in range, the terminal that was *actually* completed will get corrected. The terminal that was wrongfully tagged doesn't get corrected though. This will have to be fixed (not sure how, trigger detection on all loaded armor stands on completion message ?) --- .../mixins/ClientPacketListenerMixin.java | 2 + .../terminal/GoldorWaypointsManager.java | 82 ++++++++++++++++--- .../dungeon/{ => terminal}/TerminalHud.java | 2 +- 3 files changed, 74 insertions(+), 12 deletions(-) rename src/main/java/de/hysky/skyblocker/skyblock/dungeon/{ => terminal}/TerminalHud.java (99%) diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPacketListenerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPacketListenerMixin.java index 1797ff7ede3..34ac7dbf03f 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPacketListenerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPacketListenerMixin.java @@ -12,6 +12,7 @@ import de.hysky.skyblocker.events.PlaySoundEvents; import de.hysky.skyblocker.skyblock.CompactDamage; import de.hysky.skyblocker.skyblock.HealthBars; +import de.hysky.skyblocker.skyblock.dungeon.terminal.GoldorWaypointsManager; import de.hysky.skyblocker.skyblock.teleport.PredictiveSmoothAOTE; import de.hysky.skyblocker.skyblock.dungeon.DungeonMapTexture; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; @@ -89,6 +90,7 @@ protected ClientPacketListenerMixin(Minecraft client, Connection connection, Com FishingHelper.checkIfFishWasCaught(armorStandEntity); TreeBreakProgressHud.onEntityUpdate(armorStandEntity); LassoHud.onEntityUpdate(armorStandEntity); + GoldorWaypointsManager.checkForTerminalNameTags(armorStandEntity); try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers CompactDamage.compactDamage(armorStandEntity); } catch (Exception e) { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/GoldorWaypointsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/GoldorWaypointsManager.java index e5c6867c016..838947fb85b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/GoldorWaypointsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/GoldorWaypointsManager.java @@ -25,6 +25,7 @@ import net.minecraft.resources.Identifier; import net.minecraft.util.StringRepresentable; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +48,7 @@ public class GoldorWaypointsManager { private static final ObjectArrayList TERMINALS = new ObjectArrayList<>(); private static final ObjectArrayList DEVICES = new ObjectArrayList<>(); private static final ObjectArrayList LEVERS = new ObjectArrayList<>(); + private static final ObjectArrayList ALL_WAYPOINTS = new ObjectArrayList<>(); private static final ObjectArrayList ACTIVE_PHASE_WAYPOINTS = new ObjectArrayList<>(); @@ -55,6 +57,8 @@ public class GoldorWaypointsManager { private static final Pattern DEVICE_ACTIVATED = Pattern.compile("^(?\\w+) completed a device! \\(\\d/\\d\\)$"); private static final Pattern LEVER_ACTIVATED = Pattern.compile("^(?\\w+) activated a lever! \\(\\d/\\d\\)$"); private static final Pattern PHASE_COMPLETE = Pattern.compile("^(?\\w+) (?:activated a (?:terminal|lever)|completed a device)! (?:\\(7/7\\)|\\(8/8\\))$"); + private static final Pattern NAMETAG_INCOMPLETE = Pattern.compile("Inactive|Not Activated|Inactive Terminal"); + private static final Pattern NAMETAG_COMPLETE = Pattern.compile("Active|Activated|Terminal Active"); private static final String GATE_DESTROYED = "The gate has been destroyed!"; private static final String CORE_ENTRANCE = "The Core entrance is opening!"; private static final Codec> CODEC = GoldorWaypoint.CODEC.listOf(); @@ -79,7 +83,12 @@ public static void init() { private static void load(Minecraft client) { CompletableFuture terminals = loadWaypoints(client, SkyblockerMod.id("dungeons/goldorwaypoints.json")); - terminals.whenComplete((_result, _throwable) -> loaded = true); + terminals.whenComplete((_result, _throwable) -> { + loaded = true; + ALL_WAYPOINTS.addAll(TERMINALS); + ALL_WAYPOINTS.addAll(DEVICES); + ALL_WAYPOINTS.addAll(LEVERS); + }); } private static CompletableFuture loadWaypoints(Minecraft client, Identifier file) { @@ -113,20 +122,29 @@ private static boolean shouldProcess() { } /** - * Given a list of waypoints to operate on and a player name, hides the visible waypoint that is closest to the player + * Util for getting a player's position + */ + private static Optional getPlayerPos(String playerName) { + Minecraft client = Minecraft.getInstance(); + if (client.level == null) return Optional.empty(); + + return client.level.players().stream().filter(player -> player.getGameProfile().name().equals(playerName)).findAny().map(Entity::position); + } + + /** + * Given a list of waypoints to operate on and a position, enables/disables the waypoint that is closest to said pos. * * @param waypoints The list of waypoints to operate on - * @param playerName The name of the player to check against + * @param position The position to query from + * @param completed If the terminal should be marked as completed or missing (for corrections) */ - private static void removeNearestWaypoint(List waypoints, String playerName) { + private static void markClosestWaypoint(List waypoints, Vec3 position, boolean completed) { Minecraft client = Minecraft.getInstance(); if (client.level == null) return; - // Get the position of the player with the given name - Optional posOptional = client.level.players().stream().filter(player -> player.getGameProfile().name().equals(playerName)).findAny().map(Entity::position); + Optional closest = waypoints.stream().min(Comparator.comparingDouble(waypoint -> waypoint.centerPos.distanceToSqr(position))); - // Find the nearest waypoint to the player and hide it - posOptional.flatMap(pos -> waypoints.stream().filter(GoldorWaypoint::shouldRender).min(Comparator.comparingDouble(waypoint -> waypoint.centerPos.distanceToSqr(pos)))).ifPresent(Waypoint::setFound); + closest.ifPresent(completed ? Waypoint::setFound : Waypoint::setMissing); TerminalHud.INSTANCE.update(); } @@ -178,11 +196,14 @@ private static boolean onChatMessage(Component text, boolean overlay) { String playerName; if ((playerName = getPlayerName(TERMINAL_ACTIVATED.matcher(message))) != null) { - removeNearestWaypoint(TERMINALS, playerName); + Optional playerPos = getPlayerPos(playerName); + playerPos.ifPresent(pos -> markClosestWaypoint(TERMINALS, pos, true)); } else if ((playerName = getPlayerName(DEVICE_ACTIVATED.matcher(message))) != null) { - removeNearestWaypoint(DEVICES, playerName); + Optional playerPos = getPlayerPos(playerName); + playerPos.ifPresent(pos -> markClosestWaypoint(DEVICES, pos, true)); } else if ((playerName = getPlayerName(LEVER_ACTIVATED.matcher(message))) != null) { - removeNearestWaypoint(LEVERS, playerName); + Optional playerPos = getPlayerPos(playerName); + playerPos.ifPresent(pos -> markClosestWaypoint(LEVERS, pos, true)); } else if (message.equals(CORE_ENTRANCE)) { active = false; ACTIVE_PHASE_WAYPOINTS.clear(); @@ -238,6 +259,45 @@ public static List getPhaseWaypoints() { return ACTIVE_PHASE_WAYPOINTS; } + + /** + * Corrects the waypoint states. + * Each terminal/device has an associated display (Armor Stand), which shows when the player is close-ish + * It is best to use player position-based detection, but correct eventual mistakes with this. + * (player bounced in lava, landing closer to another term upon completion, or player is out of render distance doing something like i4) + *

+ * As of this being added, here are the names matched : + * # Devices (uses two displays, but only one of them is relevant + * Inactive + * Active + * # Levers + * Not Activated + * Activated + * # Terminals + * Inactive Terminal + * Terminal Active + */ + public static void checkForTerminalNameTags(ArmorStand armorStandEntity) { + if (!(DungeonManager.isInBoss() && DungeonManager.getBoss().isFloor(7))) { + return; + } + + Component nameComponent = armorStandEntity.getCustomName(); + if (nameComponent != null) { + String name = nameComponent.getString(); + + if (NAMETAG_INCOMPLETE.matcher(name).matches()) { + Vec3 pos = armorStandEntity.getEyePosition(); + + markClosestWaypoint(ALL_WAYPOINTS, pos, false); + } else if (NAMETAG_COMPLETE.matcher(name).matches()) { + Vec3 pos = armorStandEntity.getEyePosition(); + + markClosestWaypoint(ALL_WAYPOINTS, pos, true); + } + } + } + public static class GoldorWaypoint extends NamedWaypoint { public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( WaypointTargetKind.CODEC.fieldOf("kind").forGetter(w -> w.kind), diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TerminalHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/TerminalHud.java similarity index 99% rename from src/main/java/de/hysky/skyblocker/skyblock/dungeon/TerminalHud.java rename to src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/TerminalHud.java index 698d8abef88..b885a3dc6ff 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TerminalHud.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/TerminalHud.java @@ -1,4 +1,4 @@ -package de.hysky.skyblocker.skyblock.dungeon; +package de.hysky.skyblocker.skyblock.dungeon.terminal; import de.hysky.skyblocker.annotations.RegisterWidget; import de.hysky.skyblocker.config.SkyblockerConfigManager;