From fefd72447fd368590ae3406bf80e593beccdd366 Mon Sep 17 00:00:00 2001 From: Alexey Rakhmaninov <46868290+TheMychenik@users.noreply.github.com> Date: Mon, 18 Aug 2025 23:25:21 +0200 Subject: [PATCH 1/7] More precise crystal waypoints when clicking or killing NPCs (Odawa, Corleone, etc.). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not working with Khazad-dûm (Bal), Fairy Grotto, Dragon's Lair, or Goblin Queen's Den. Maybe figure out how to handle these later. --- .../dwarven/CrystalsLocationsManager.java | 78 +++++++++++++++---- .../skyblock/dwarven/MetalDetector.java | 2 + .../skyblock/dwarven/MiningLocationLabel.java | 52 +++++++++---- 3 files changed, 102 insertions(+), 30 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java index 4bb2174be04..b919dd3f3f7 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -8,6 +8,8 @@ import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.skyblock.dwarven.MiningLocationLabel.CrystalHollowsLocationsCategory; +import de.hysky.skyblocker.skyblock.entity.MobGlow; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.Location; import de.hysky.skyblocker.utils.Utils; @@ -26,14 +28,25 @@ 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.AttackEntityCallback; +import net.fabricmc.fabric.api.event.player.UseEntityCallback; import net.minecraft.client.MinecraftClient; import net.minecraft.command.CommandRegistryAccess; +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.text.ClickEvent; import net.minecraft.text.HoverEvent; import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; import net.minecraft.util.Formatting; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + import org.slf4j.Logger; import java.util.*; @@ -46,6 +59,7 @@ import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; import static net.minecraft.command.CommandSource.suggestMatching; +import org.jetbrains.annotations.Nullable; /** * Manager for Crystal Hollows waypoints that handles {@link #update() location detection}, @@ -81,8 +95,59 @@ public static void init() { // Nucleus Waypoints WorldRenderEvents.AFTER_TRANSLUCENT.register(NucleusWaypoints::render); + + // Verify waypoints by left- or right-clicking on an NPC such as Odawa, Boss Corleone, or Kalhuiki Door Guardian, etc. + AttackEntityCallback.EVENT.register(CrystalsLocationsManager::OnEntryInteract); + UseEntityCallback.EVENT.register(CrystalsLocationsManager::OnEntryInteract); + } + + // Verify waypoints when interacting with an NPC + public static ActionResult OnEntryInteract(PlayerEntity playerEntity, World world, Hand hand, Entity entity, @Nullable EntityHitResult entityHitResult) { + if (!Utils.isInCrystalHollows()) { + return ActionResult.PASS; + } + // Searching for an invisible armor stand behind an entity (it has the entity’s name) + String npcName = MobGlow.getArmorStandName(entity); + + CrystalHollowsLocationsCategory waypointLocation = CrystalHollowsLocationsCategory.findNpcNameBySubstring(npcName); + if (waypointLocation != null && !npcName.isBlank()) { + BlockPos pos = CLIENT.player.getBlockPos(); + placeVerifiedWaypoint(waypointLocation, pos); + } + + return ActionResult.PASS; + } + + // For the Mines of Divan, it's better to add a waypoint using MetalDetector.findCenterOfMines() at the minesCenter coordinates + public static void VerifyMinesOfDivanWaypoint(BlockPos pos) { + CrystalHollowsLocationsCategory waypointLocation = CrystalHollowsLocationsCategory.MINES_OF_DIVAN; + placeVerifiedWaypoint(waypointLocation, pos); + } + + // Check if new coords are distant enough from old coords (return false if too close) + public static boolean isNotCoordsOverlaping(BlockPos newCoords, MiningLocationLabel oldwaypoint, int searchRadius) { + if (oldwaypoint == null || searchRadius == 0 ) return true; + + int dx = Math.abs(newCoords.getX() - oldwaypoint.pos.getX()); + int dz = Math.abs(newCoords.getZ() - oldwaypoint.pos.getZ()); + int dy = Math.abs(newCoords.getY() - oldwaypoint.pos.getY()); + + return dx > searchRadius || dz > searchRadius || dy > 3; + } + + public static void placeVerifiedWaypoint(CrystalHollowsLocationsCategory waypoint, BlockPos pos) { + String waypointName = waypoint.getName(); + + if (!verifiedWaypoints.contains(waypointName)) { + if (isNotCoordsOverlaping(pos, activeWaypoints.get(waypointName), waypoint.getSearchRadius())) { + addCustomWaypoint(waypointName, pos); + trySendWaypoint2Socket(waypoint); + } + verifiedWaypoints.add(waypointName); + } } + private static boolean extractLocationFromMessage(Text message, Boolean overlay) { if (!SkyblockerConfigManager.get().mining.crystalsWaypoints.findInChat || !Utils.isInCrystalHollows() || overlay) { return true; @@ -129,19 +194,6 @@ private static boolean extractLocationFromMessage(Text message, Boolean overlay) LOGGER.error("[Skyblocker Crystals Locations Manager] Encountered an exception while extracing a location from a chat message!", e); } - //move waypoint to be more accurate based on locational chat messages if not already verifed - if (CLIENT.player != null && SkyblockerConfigManager.get().mining.crystalsWaypoints.enabled) { - for (MiningLocationLabel.CrystalHollowsLocationsCategory waypointLocation : WAYPOINT_LOCATIONS.values()) { - String waypointLinkedMessage = waypointLocation.getLinkedMessage(); - String waypointName = waypointLocation.getName(); - if (waypointLinkedMessage != null && text.contains(waypointLinkedMessage) && !verifiedWaypoints.contains(waypointName)) { - addCustomWaypoint(waypointLocation.getName(), CLIENT.player.getBlockPos()); - verifiedWaypoints.add(waypointName); - trySendWaypoint2Socket(waypointLocation); - } - } - } - return true; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MetalDetector.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MetalDetector.java index a870e55b3ed..6d54d2047bb 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MetalDetector.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MetalDetector.java @@ -225,6 +225,8 @@ private static void findCenterOfMines() { Vec3i offset = keeperOffsets.get(nameMatcher.group(1)); minesCenter = armorStand.getBlockPos().add(offset); CLIENT.player.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dwarvenMines.metalDetectorHelper.foundCenter").formatted(Formatting.GREEN)), false); + + CrystalsLocationsManager.VerifyMinesOfDivanWaypoint(new BlockPos(minesCenter)); return; } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java index 632ea3fcfe5..0f9d95a7f2a 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -9,6 +9,9 @@ import net.minecraft.util.math.BlockPos; import java.awt.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public class MiningLocationLabel extends DistancedNamedWaypoint { private final Category category; @@ -163,29 +166,31 @@ public int getColor() { * enum for the different waypoints used int the crystals hud each with a {@link CrystalHollowsLocationsCategory#name} and associated {@link CrystalHollowsLocationsCategory#color} */ public enum CrystalHollowsLocationsCategory implements Category, StringIdentifiable { - UNKNOWN("Unknown", Color.WHITE, null), //used when a location is known but what's at the location is not known - JUNGLE_TEMPLE("Jungle Temple", new Color(DyeColor.PURPLE.getSignColor()), "[NPC] Kalhuiki Door Guardian:"), - MINES_OF_DIVAN("Mines of Divan", Color.GREEN, " Jade Crystal"), - GOBLIN_QUEENS_DEN("Goblin Queen's Den", new Color(DyeColor.ORANGE.getSignColor()), " Amber Crystal"), - LOST_PRECURSOR_CITY("Lost Precursor City", Color.CYAN, " Sapphire Crystal"), - KHAZAD_DUM("Khazad-dûm", Color.YELLOW, " Topaz Crystal"), - FAIRY_GROTTO("Fairy Grotto", Color.PINK, null), - DRAGONS_LAIR("Dragon's Lair", Color.BLACK, null), - CORLEONE("Corleone", Color.WHITE, null), - KING_YOLKAR("King Yolkar", Color.RED, "[NPC] King Yolkar:"), - ODAWA("Odawa", Color.MAGENTA, "[NPC] Odawa:"), - KEY_GUARDIAN("Key Guardian", Color.LIGHT_GRAY, null); + UNKNOWN("Unknown", Color.WHITE, null, 0), //used when a location is known but what's at the location is not known + JUNGLE_TEMPLE("Jungle Temple", new Color(DyeColor.PURPLE.getSignColor()), "Kalhuiki Door Guardian", 10), + LOST_PRECURSOR_CITY("Lost Precursor City", Color.CYAN, "Professor Robot", 8), + CORLEONE("Corleone", Color.WHITE, "Boss Corleone", 20), + KING_YOLKAR("King Yolkar", Color.RED, "King Yolkar", 8), + ODAWA("Odawa", Color.MAGENTA, "Odawa", 8), + KEY_GUARDIAN("Key Guardian", Color.LIGHT_GRAY,"Key Guardian", 10), + MINES_OF_DIVAN("Mines of Divan", Color.GREEN, null, 0), // no npcName because we are getting coords from MetalDetector + KHAZAD_DUM("Khazad-dûm", Color.YELLOW, null, 0), + FAIRY_GROTTO("Fairy Grotto", Color.PINK, null, 0), + DRAGONS_LAIR("Dragon's Lair", Color.BLACK, null, 0), + GOBLIN_QUEENS_DEN("Goblin Queen's Den", new Color(DyeColor.ORANGE.getSignColor()), null, 0); public static final Codec CODEC = StringIdentifiable.createBasicCodec(CrystalHollowsLocationsCategory::values); public final Color color; private final String name; - private final String linkedMessage; + private final String npcName; + private final int searchRadius; - CrystalHollowsLocationsCategory(String name, Color color, String linkedMessage) { + CrystalHollowsLocationsCategory(String name, Color color, String npcName, int searchRadius) { this.name = name; this.color = color; - this.linkedMessage = linkedMessage; + this.npcName = npcName == null ? "" : npcName.toLowerCase(); + this.searchRadius = searchRadius; } @Override @@ -198,14 +203,27 @@ public int getColor() { return this.color.getRGB(); } - public String getLinkedMessage() { - return this.linkedMessage; + public int getSearchRadius() { + return this.searchRadius; } @Override public String asString() { return name(); } + + // npcName search + public static CrystalHollowsLocationsCategory findNpcNameBySubstring(String query) { + if (query == null || query.isBlank()) return null; + String q = query.toLowerCase(); + + for (CrystalHollowsLocationsCategory c : values()) { + if (!c.npcName.isEmpty() && q.contains(c.npcName)) { + return c; + } + } + return null; + } } } From ae45dd727615c8937c893707d587c1571d94f7a9 Mon Sep 17 00:00:00 2001 From: Alexey Rakhmaninov <46868290+TheMychenik@users.noreply.github.com> Date: Mon, 18 Aug 2025 23:31:34 +0200 Subject: [PATCH 2/7] Cleanup --- .../skyblocker/skyblock/dwarven/CrystalsLocationsManager.java | 2 -- .../hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java index b919dd3f3f7..413e594ddc2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -33,9 +33,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.command.CommandRegistryAccess; 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.text.ClickEvent; import net.minecraft.text.HoverEvent; import net.minecraft.text.MutableText; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java index 0f9d95a7f2a..d429e4a2d9f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -9,9 +9,6 @@ import net.minecraft.util.math.BlockPos; import java.awt.*; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; public class MiningLocationLabel extends DistancedNamedWaypoint { private final Category category; From f83d56210d7d429d17c226a93e631e055080f072 Mon Sep 17 00:00:00 2001 From: Alexey Rakhmaninov <46868290+TheMychenik@users.noreply.github.com> Date: Mon, 18 Aug 2025 23:55:26 +0200 Subject: [PATCH 3/7] stylecheck --- .../skyblocker/skyblock/dwarven/CrystalsLocationsManager.java | 4 ++-- .../skyblocker/skyblock/dwarven/MiningLocationLabel.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java index 413e594ddc2..6ce5bdb06cd 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -124,7 +124,7 @@ public static void VerifyMinesOfDivanWaypoint(BlockPos pos) { // Check if new coords are distant enough from old coords (return false if too close) public static boolean isNotCoordsOverlaping(BlockPos newCoords, MiningLocationLabel oldwaypoint, int searchRadius) { - if (oldwaypoint == null || searchRadius == 0 ) return true; + if (oldwaypoint == null || searchRadius == 0) return true; int dx = Math.abs(newCoords.getX() - oldwaypoint.pos.getX()); int dz = Math.abs(newCoords.getZ() - oldwaypoint.pos.getZ()); @@ -132,7 +132,7 @@ public static boolean isNotCoordsOverlaping(BlockPos newCoords, MiningLocationLa return dx > searchRadius || dz > searchRadius || dy > 3; } - + public static void placeVerifiedWaypoint(CrystalHollowsLocationsCategory waypoint, BlockPos pos) { String waypointName = waypoint.getName(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java index d429e4a2d9f..5b7137df8ca 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -169,8 +169,8 @@ public enum CrystalHollowsLocationsCategory implements Category, StringIdentifia CORLEONE("Corleone", Color.WHITE, "Boss Corleone", 20), KING_YOLKAR("King Yolkar", Color.RED, "King Yolkar", 8), ODAWA("Odawa", Color.MAGENTA, "Odawa", 8), - KEY_GUARDIAN("Key Guardian", Color.LIGHT_GRAY,"Key Guardian", 10), - MINES_OF_DIVAN("Mines of Divan", Color.GREEN, null, 0), // no npcName because we are getting coords from MetalDetector + KEY_GUARDIAN("Key Guardian", Color.LIGHT_GRAY, "Key Guardian", 10), + MINES_OF_DIVAN("Mines of Divan", Color.GREEN, null, 0), // no npcName because we are getting coords from MetalDetector KHAZAD_DUM("Khazad-dûm", Color.YELLOW, null, 0), FAIRY_GROTTO("Fairy Grotto", Color.PINK, null, 0), DRAGONS_LAIR("Dragon's Lair", Color.BLACK, null, 0), From 97d83fc42d873ceca59c4f4ef0170bdd4a364204 Mon Sep 17 00:00:00 2001 From: Alexey Rakhmaninov <46868290+TheMychenik@users.noreply.github.com> Date: Thu, 21 Aug 2025 23:55:56 +0200 Subject: [PATCH 4/7] =?UTF-8?q?Fix=20for=20Khazad-d=C3=BBm,=20Goblin=20Que?= =?UTF-8?q?en's=20Den=20and=20Mines=20of=20Divan=20(search=20in=20chat=20m?= =?UTF-8?q?essages)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dwarven/CrystalsLocationsManager.java | 77 +++++++++++-------- .../skyblock/dwarven/MetalDetector.java | 2 - .../skyblock/dwarven/MiningLocationLabel.java | 27 ++++--- 3 files changed, 63 insertions(+), 43 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java index 6ce5bdb06cd..665caf5e728 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -73,6 +73,14 @@ public class CrystalsLocationsManager { * A look-up table to convert between location names and waypoint in the {@link MiningLocationLabel.CrystalHollowsLocationsCategory} values. */ private static final Map WAYPOINT_LOCATIONS = Arrays.stream(MiningLocationLabel.CrystalHollowsLocationsCategory.values()).collect(Collectors.toMap(MiningLocationLabel.CrystalHollowsLocationsCategory::getName, Function.identity())); + // Locations to search for in chat messages + private static final EnumSet CHAT_VERIFIABLE_WAYPOINTS = + EnumSet.of( + MiningLocationLabel.CrystalHollowsLocationsCategory.KHAZAD_DUM, + MiningLocationLabel.CrystalHollowsLocationsCategory.GOBLIN_QUEENS_DEN, + MiningLocationLabel.CrystalHollowsLocationsCategory.MINES_OF_DIVAN + ); + //Package-private for testing static final Pattern TEXT_CWORDS_PATTERN = Pattern.compile("\\Dx?(\\d{3})(?=[, ]),? ?y?(\\d{2,3})(?=[, ]),? ?z?(\\d{3})\\D?(?!\\d)"); private static final int REMOVE_UNKNOWN_DISTANCE = 50; @@ -100,7 +108,7 @@ public static void init() { } // Verify waypoints when interacting with an NPC - public static ActionResult OnEntryInteract(PlayerEntity playerEntity, World world, Hand hand, Entity entity, @Nullable EntityHitResult entityHitResult) { + private static ActionResult OnEntryInteract(PlayerEntity playerEntity, World world, Hand hand, Entity entity, @Nullable EntityHitResult entityHitResult) { if (!Utils.isInCrystalHollows()) { return ActionResult.PASS; } @@ -116,36 +124,6 @@ public static ActionResult OnEntryInteract(PlayerEntity playerEntity, World worl return ActionResult.PASS; } - // For the Mines of Divan, it's better to add a waypoint using MetalDetector.findCenterOfMines() at the minesCenter coordinates - public static void VerifyMinesOfDivanWaypoint(BlockPos pos) { - CrystalHollowsLocationsCategory waypointLocation = CrystalHollowsLocationsCategory.MINES_OF_DIVAN; - placeVerifiedWaypoint(waypointLocation, pos); - } - - // Check if new coords are distant enough from old coords (return false if too close) - public static boolean isNotCoordsOverlaping(BlockPos newCoords, MiningLocationLabel oldwaypoint, int searchRadius) { - if (oldwaypoint == null || searchRadius == 0) return true; - - int dx = Math.abs(newCoords.getX() - oldwaypoint.pos.getX()); - int dz = Math.abs(newCoords.getZ() - oldwaypoint.pos.getZ()); - int dy = Math.abs(newCoords.getY() - oldwaypoint.pos.getY()); - - return dx > searchRadius || dz > searchRadius || dy > 3; - } - - public static void placeVerifiedWaypoint(CrystalHollowsLocationsCategory waypoint, BlockPos pos) { - String waypointName = waypoint.getName(); - - if (!verifiedWaypoints.contains(waypointName)) { - if (isNotCoordsOverlaping(pos, activeWaypoints.get(waypointName), waypoint.getSearchRadius())) { - addCustomWaypoint(waypointName, pos); - trySendWaypoint2Socket(waypoint); - } - verifiedWaypoints.add(waypointName); - } - } - - private static boolean extractLocationFromMessage(Text message, Boolean overlay) { if (!SkyblockerConfigManager.get().mining.crystalsWaypoints.findInChat || !Utils.isInCrystalHollows() || overlay) { return true; @@ -187,6 +165,17 @@ private static boolean extractLocationFromMessage(Text message, Boolean overlay) CLIENT.player.sendMessage(getLocationMenu(location, false), false); } } + else { // if not user message, trying to find key words in the chat to place waypoint more accurate, if not already verifed + if (CLIENT.player != null && SkyblockerConfigManager.get().mining.crystalsWaypoints.enabled) { + // Iterate only through waypoints with crystal names + for (MiningLocationLabel.CrystalHollowsLocationsCategory waypointLocation : CHAT_VERIFIABLE_WAYPOINTS) { + @Nullable String waypointLinkedMessage = waypointLocation.getNpcName(); + if (waypointLinkedMessage != null && text.contains(waypointLinkedMessage)) { + placeVerifiedWaypoint(waypointLocation, CLIENT.player.getBlockPos()); + } + } + } + } } catch (Exception e) { LOGGER.error("[Skyblocker Crystals Locations Manager] Encountered an exception while extracing a location from a chat message!", e); @@ -454,4 +443,28 @@ private static void trySendWaypoint2Socket(MiningLocationLabel.CrystalHollowsLoc WsMessageHandler.sendMessage(Service.CRYSTAL_WAYPOINTS, new CrystalsWaypointMessage(category, CLIENT.player.getBlockPos())); waypointsSent2Socket.add(category); } -} + + // Check if new coords are distant enough from old coords (return true if too close) + private static boolean isWaypointsOverlaping(BlockPos newCoords, MiningLocationLabel oldwaypoint, int searchRadius) { + if (oldwaypoint == null || searchRadius == 0) return true; + + int diffx = Math.abs(newCoords.getX() - oldwaypoint.pos.getX()); + int diffz = Math.abs(newCoords.getZ() - oldwaypoint.pos.getZ()); + int diffy = Math.abs(newCoords.getY() - oldwaypoint.pos.getY()); + + return diffx < searchRadius || diffz < searchRadius || diffy < searchRadius; + } + + private static void placeVerifiedWaypoint(CrystalHollowsLocationsCategory waypoint, BlockPos pos) { + String waypointName = waypoint.getName(); + + if (!verifiedWaypoints.contains(waypointName)) { + // Check that we dont already have same waypoint nearby (e.g from Socket), should (theoretically) save some trafic for the server + if (!isWaypointsOverlaping(pos, activeWaypoints.get(waypointName), waypoint.getSearchRadius())) { + trySendWaypoint2Socket(waypoint); + } + addCustomWaypoint(waypointName, pos); + verifiedWaypoints.add(waypointName); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MetalDetector.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MetalDetector.java index 6d54d2047bb..a870e55b3ed 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MetalDetector.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MetalDetector.java @@ -225,8 +225,6 @@ private static void findCenterOfMines() { Vec3i offset = keeperOffsets.get(nameMatcher.group(1)); minesCenter = armorStand.getBlockPos().add(offset); CLIENT.player.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dwarvenMines.metalDetectorHelper.foundCenter").formatted(Formatting.GREEN)), false); - - CrystalsLocationsManager.VerifyMinesOfDivanWaypoint(new BlockPos(minesCenter)); return; } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java index 5b7137df8ca..b0fff05ff2e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -10,6 +10,8 @@ import java.awt.*; +import javax.annotation.Nullable; + public class MiningLocationLabel extends DistancedNamedWaypoint { private final Category category; @@ -164,29 +166,33 @@ public int getColor() { */ public enum CrystalHollowsLocationsCategory implements Category, StringIdentifiable { UNKNOWN("Unknown", Color.WHITE, null, 0), //used when a location is known but what's at the location is not known + // These waypoints are verified by interacting with the corresponding NPC (e.g., by clicking on Odawa) JUNGLE_TEMPLE("Jungle Temple", new Color(DyeColor.PURPLE.getSignColor()), "Kalhuiki Door Guardian", 10), LOST_PRECURSOR_CITY("Lost Precursor City", Color.CYAN, "Professor Robot", 8), - CORLEONE("Corleone", Color.WHITE, "Boss Corleone", 20), KING_YOLKAR("King Yolkar", Color.RED, "King Yolkar", 8), ODAWA("Odawa", Color.MAGENTA, "Odawa", 8), + CORLEONE("Corleone", Color.WHITE, "Boss Corleone", 20), KEY_GUARDIAN("Key Guardian", Color.LIGHT_GRAY, "Key Guardian", 10), - MINES_OF_DIVAN("Mines of Divan", Color.GREEN, null, 0), // no npcName because we are getting coords from MetalDetector - KHAZAD_DUM("Khazad-dûm", Color.YELLOW, null, 0), + // Look for chat message containing npcName (crystal name) + KHAZAD_DUM("Khazad-dûm", Color.YELLOW, " Topaz Crystal", 20), + GOBLIN_QUEENS_DEN("Goblin Queen's Den", new Color(DyeColor.ORANGE.getSignColor()), " Amber Crystal", 20), + MINES_OF_DIVAN("Mines of Divan", Color.GREEN, " Jade Crystal", 20), + // These cannot be found automatically yet. FAIRY_GROTTO("Fairy Grotto", Color.PINK, null, 0), - DRAGONS_LAIR("Dragon's Lair", Color.BLACK, null, 0), - GOBLIN_QUEENS_DEN("Goblin Queen's Den", new Color(DyeColor.ORANGE.getSignColor()), null, 0); + DRAGONS_LAIR("Dragon's Lair", Color.BLACK, null, 0); + public static final Codec CODEC = StringIdentifiable.createBasicCodec(CrystalHollowsLocationsCategory::values); public final Color color; - private final String name; + private final @Nullable String name; private final String npcName; private final int searchRadius; CrystalHollowsLocationsCategory(String name, Color color, String npcName, int searchRadius) { this.name = name; this.color = color; - this.npcName = npcName == null ? "" : npcName.toLowerCase(); + this.npcName = npcName; this.searchRadius = searchRadius; } @@ -200,6 +206,10 @@ public int getColor() { return this.color.getRGB(); } + public @Nullable String getNpcName() { + return this.npcName; + } + public int getSearchRadius() { return this.searchRadius; } @@ -212,10 +222,9 @@ public String asString() { // npcName search public static CrystalHollowsLocationsCategory findNpcNameBySubstring(String query) { if (query == null || query.isBlank()) return null; - String q = query.toLowerCase(); for (CrystalHollowsLocationsCategory c : values()) { - if (!c.npcName.isEmpty() && q.contains(c.npcName)) { + if (c.npcName != null && !c.npcName.isBlank() && query.contains(c.npcName)) { return c; } } From b706fc4924ff41785c1a68bed4bd916ceb7198fd Mon Sep 17 00:00:00 2001 From: Alexey Rakhmaninov <46868290+TheMychenik@users.noreply.github.com> Date: Fri, 22 Aug 2025 00:02:57 +0200 Subject: [PATCH 5/7] cleanup --- .../skyblocker/skyblock/dwarven/CrystalsLocationsManager.java | 4 ++-- .../skyblocker/skyblock/dwarven/MiningLocationLabel.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java index 665caf5e728..e9288e419c2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -457,7 +457,7 @@ private static boolean isWaypointsOverlaping(BlockPos newCoords, MiningLocationL private static void placeVerifiedWaypoint(CrystalHollowsLocationsCategory waypoint, BlockPos pos) { String waypointName = waypoint.getName(); - + if (!verifiedWaypoints.contains(waypointName)) { // Check that we dont already have same waypoint nearby (e.g from Socket), should (theoretically) save some trafic for the server if (!isWaypointsOverlaping(pos, activeWaypoints.get(waypointName), waypoint.getSearchRadius())) { @@ -467,4 +467,4 @@ private static void placeVerifiedWaypoint(CrystalHollowsLocationsCategory waypoi verifiedWaypoints.add(waypointName); } } -} \ No newline at end of file +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java index b0fff05ff2e..eccc3c7f405 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -178,9 +178,8 @@ public enum CrystalHollowsLocationsCategory implements Category, StringIdentifia GOBLIN_QUEENS_DEN("Goblin Queen's Den", new Color(DyeColor.ORANGE.getSignColor()), " Amber Crystal", 20), MINES_OF_DIVAN("Mines of Divan", Color.GREEN, " Jade Crystal", 20), // These cannot be found automatically yet. - FAIRY_GROTTO("Fairy Grotto", Color.PINK, null, 0), + FAIRY_GROTTO("Fairy Grotto", Color.PINK, null, 0), DRAGONS_LAIR("Dragon's Lair", Color.BLACK, null, 0); - public static final Codec CODEC = StringIdentifiable.createBasicCodec(CrystalHollowsLocationsCategory::values); From 818df5d2e3c246348ce706b065979c0523f556b7 Mon Sep 17 00:00:00 2001 From: Alexey Rakhmaninov <46868290+TheMychenik@users.noreply.github.com> Date: Fri, 22 Aug 2025 00:06:02 +0200 Subject: [PATCH 6/7] cleanup x2 --- .../hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java index eccc3c7f405..14326e61396 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -178,7 +178,7 @@ public enum CrystalHollowsLocationsCategory implements Category, StringIdentifia GOBLIN_QUEENS_DEN("Goblin Queen's Den", new Color(DyeColor.ORANGE.getSignColor()), " Amber Crystal", 20), MINES_OF_DIVAN("Mines of Divan", Color.GREEN, " Jade Crystal", 20), // These cannot be found automatically yet. - FAIRY_GROTTO("Fairy Grotto", Color.PINK, null, 0), + FAIRY_GROTTO("Fairy Grotto", Color.PINK, null, 0), DRAGONS_LAIR("Dragon's Lair", Color.BLACK, null, 0); public static final Codec CODEC = StringIdentifiable.createBasicCodec(CrystalHollowsLocationsCategory::values); From 74671f521d9a78dd86d5e0058e7bd0ce1c406cfd Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sat, 20 Sep 2025 00:14:17 -0400 Subject: [PATCH 7/7] Refactor crystal locations --- .../dwarven/CrystalsLocationsManager.java | 29 +++++++--------- .../skyblock/dwarven/MiningLocationLabel.java | 34 ++++++++++--------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java index e9288e419c2..1a6f2b241b1 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -103,21 +103,21 @@ public static void init() { WorldRenderEvents.AFTER_TRANSLUCENT.register(NucleusWaypoints::render); // Verify waypoints by left- or right-clicking on an NPC such as Odawa, Boss Corleone, or Kalhuiki Door Guardian, etc. - AttackEntityCallback.EVENT.register(CrystalsLocationsManager::OnEntryInteract); - UseEntityCallback.EVENT.register(CrystalsLocationsManager::OnEntryInteract); + AttackEntityCallback.EVENT.register(CrystalsLocationsManager::onEntryInteract); + UseEntityCallback.EVENT.register(CrystalsLocationsManager::onEntryInteract); } // Verify waypoints when interacting with an NPC - private static ActionResult OnEntryInteract(PlayerEntity playerEntity, World world, Hand hand, Entity entity, @Nullable EntityHitResult entityHitResult) { + private static ActionResult onEntryInteract(PlayerEntity playerEntity, World world, Hand hand, Entity entity, @Nullable EntityHitResult entityHitResult) { if (!Utils.isInCrystalHollows()) { return ActionResult.PASS; } // Searching for an invisible armor stand behind an entity (it has the entity’s name) String npcName = MobGlow.getArmorStandName(entity); - CrystalHollowsLocationsCategory waypointLocation = CrystalHollowsLocationsCategory.findNpcNameBySubstring(npcName); + CrystalHollowsLocationsCategory waypointLocation = CrystalHollowsLocationsCategory.fromContainsIdentifyingText(npcName); if (waypointLocation != null && !npcName.isBlank()) { - BlockPos pos = CLIENT.player.getBlockPos(); + BlockPos pos = entity.getBlockPos(); placeVerifiedWaypoint(waypointLocation, pos); } @@ -164,12 +164,11 @@ private static boolean extractLocationFromMessage(Text message, Boolean overlay) CLIENT.player.sendMessage(getLocationMenu(location, false), false); } - } - else { // if not user message, trying to find key words in the chat to place waypoint more accurate, if not already verifed + } else { // if not a user message, try to find keywords in the chat to place waypoint more accurate, if not already verifed if (CLIENT.player != null && SkyblockerConfigManager.get().mining.crystalsWaypoints.enabled) { // Iterate only through waypoints with crystal names for (MiningLocationLabel.CrystalHollowsLocationsCategory waypointLocation : CHAT_VERIFIABLE_WAYPOINTS) { - @Nullable String waypointLinkedMessage = waypointLocation.getNpcName(); + String waypointLinkedMessage = waypointLocation.getIdentifyingText(); if (waypointLinkedMessage != null && text.contains(waypointLinkedMessage)) { placeVerifiedWaypoint(waypointLocation, CLIENT.player.getBlockPos()); } @@ -445,22 +444,18 @@ private static void trySendWaypoint2Socket(MiningLocationLabel.CrystalHollowsLoc } // Check if new coords are distant enough from old coords (return true if too close) - private static boolean isWaypointsOverlaping(BlockPos newCoords, MiningLocationLabel oldwaypoint, int searchRadius) { - if (oldwaypoint == null || searchRadius == 0) return true; - - int diffx = Math.abs(newCoords.getX() - oldwaypoint.pos.getX()); - int diffz = Math.abs(newCoords.getZ() - oldwaypoint.pos.getZ()); - int diffy = Math.abs(newCoords.getY() - oldwaypoint.pos.getY()); + private static boolean areWaypointsOverlapping(BlockPos newPos, MiningLocationLabel oldWaypoint, int searchRadius) { + if (oldWaypoint == null || searchRadius == 0) return false; - return diffx < searchRadius || diffz < searchRadius || diffy < searchRadius; + return newPos.getSquaredDistance(oldWaypoint.pos) < searchRadius * searchRadius; } private static void placeVerifiedWaypoint(CrystalHollowsLocationsCategory waypoint, BlockPos pos) { String waypointName = waypoint.getName(); if (!verifiedWaypoints.contains(waypointName)) { - // Check that we dont already have same waypoint nearby (e.g from Socket), should (theoretically) save some trafic for the server - if (!isWaypointsOverlaping(pos, activeWaypoints.get(waypointName), waypoint.getSearchRadius())) { + // Check that we don't already have the same waypoint nearby (e.g., from Socket), should (theoretically) save some traffic for the server + if (!areWaypointsOverlapping(pos, activeWaypoints.get(waypointName), waypoint.getSearchRadius())) { trySendWaypoint2Socket(waypoint); } addCustomWaypoint(waypointName, pos); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java index 14326e61396..17b9977a101 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -7,11 +7,10 @@ import net.minecraft.util.DyeColor; import net.minecraft.util.StringIdentifiable; import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; import java.awt.*; -import javax.annotation.Nullable; - public class MiningLocationLabel extends DistancedNamedWaypoint { private final Category category; @@ -162,10 +161,10 @@ public int getColor() { } /** - * enum for the different waypoints used int the crystals hud each with a {@link CrystalHollowsLocationsCategory#name} and associated {@link CrystalHollowsLocationsCategory#color} + * Enum for the different waypoints used in the crystals hud each with a {@link CrystalHollowsLocationsCategory#name} and associated {@link CrystalHollowsLocationsCategory#color}. */ public enum CrystalHollowsLocationsCategory implements Category, StringIdentifiable { - UNKNOWN("Unknown", Color.WHITE, null, 0), //used when a location is known but what's at the location is not known + UNKNOWN("Unknown", Color.WHITE), // Used when a location is known but what's at the location is not known // These waypoints are verified by interacting with the corresponding NPC (e.g., by clicking on Odawa) JUNGLE_TEMPLE("Jungle Temple", new Color(DyeColor.PURPLE.getSignColor()), "Kalhuiki Door Guardian", 10), LOST_PRECURSOR_CITY("Lost Precursor City", Color.CYAN, "Professor Robot", 8), @@ -173,25 +172,29 @@ public enum CrystalHollowsLocationsCategory implements Category, StringIdentifia ODAWA("Odawa", Color.MAGENTA, "Odawa", 8), CORLEONE("Corleone", Color.WHITE, "Boss Corleone", 20), KEY_GUARDIAN("Key Guardian", Color.LIGHT_GRAY, "Key Guardian", 10), - // Look for chat message containing npcName (crystal name) + // Look for chat messages containing the crystal name KHAZAD_DUM("Khazad-dûm", Color.YELLOW, " Topaz Crystal", 20), GOBLIN_QUEENS_DEN("Goblin Queen's Den", new Color(DyeColor.ORANGE.getSignColor()), " Amber Crystal", 20), MINES_OF_DIVAN("Mines of Divan", Color.GREEN, " Jade Crystal", 20), // These cannot be found automatically yet. - FAIRY_GROTTO("Fairy Grotto", Color.PINK, null, 0), - DRAGONS_LAIR("Dragon's Lair", Color.BLACK, null, 0); + FAIRY_GROTTO("Fairy Grotto", Color.PINK), + DRAGONS_LAIR("Dragon's Lair", Color.BLACK); public static final Codec CODEC = StringIdentifiable.createBasicCodec(CrystalHollowsLocationsCategory::values); public final Color color; - private final @Nullable String name; - private final String npcName; + private final String name; + private final @Nullable String identifyingText; private final int searchRadius; - CrystalHollowsLocationsCategory(String name, Color color, String npcName, int searchRadius) { + CrystalHollowsLocationsCategory(String name, Color color) { + this(name, color, null, 0); + } + + CrystalHollowsLocationsCategory(String name, Color color, @Nullable String identifyingText, int searchRadius) { this.name = name; this.color = color; - this.npcName = npcName; + this.identifyingText = identifyingText; this.searchRadius = searchRadius; } @@ -205,8 +208,8 @@ public int getColor() { return this.color.getRGB(); } - public @Nullable String getNpcName() { - return this.npcName; + public @Nullable String getIdentifyingText() { + return this.identifyingText; } public int getSearchRadius() { @@ -218,12 +221,11 @@ public String asString() { return name(); } - // npcName search - public static CrystalHollowsLocationsCategory findNpcNameBySubstring(String query) { + public static CrystalHollowsLocationsCategory fromContainsIdentifyingText(String query) { if (query == null || query.isBlank()) return null; for (CrystalHollowsLocationsCategory c : values()) { - if (c.npcName != null && !c.npcName.isBlank() && query.contains(c.npcName)) { + if (c.identifyingText != null && !c.identifyingText.isBlank() && query.contains(c.identifyingText)) { return c; } }