diff options
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java')
-rw-r--r-- | src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java | 252 |
1 files changed, 194 insertions, 58 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 83167c18..22e494ab 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -21,16 +21,15 @@ import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.minecraft.client.MinecraftClient; import net.minecraft.command.CommandRegistryAccess; import net.minecraft.text.ClickEvent; +import net.minecraft.text.HoverEvent; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; import org.slf4j.Logger; -import java.awt.*; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,12 +51,15 @@ public class CrystalsLocationsManager { private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); /** - * A look-up table to convert between location names and waypoint in the {@link CrystalsWaypoint.Category} values. + * A look-up table to convert between location names and waypoint in the {@link MiningLocationLabel.CrystalHollowsLocationsCategory} values. */ - private static final Map<String, CrystalsWaypoint.Category> WAYPOINT_LOCATIONS = Arrays.stream(CrystalsWaypoint.Category.values()).collect(Collectors.toMap(CrystalsWaypoint.Category::toString, Function.identity())); - private static final Pattern TEXT_CWORDS_PATTERN = Pattern.compile("([0-9][0-9][0-9]) ([0-9][0-9][0-9]?) ([0-9][0-9][0-9])"); + private static final Map<String, MiningLocationLabel.CrystalHollowsLocationsCategory> WAYPOINT_LOCATIONS = Arrays.stream(MiningLocationLabel.CrystalHollowsLocationsCategory.values()).collect(Collectors.toMap(MiningLocationLabel.CrystalHollowsLocationsCategory::getName, Function.identity())); + //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; - protected static Map<String, CrystalsWaypoint> activeWaypoints = new HashMap<>(); + protected static Map<String, MiningLocationLabel> activeWaypoints = new HashMap<>(); + protected static List<String> verifiedWaypoints = new ArrayList<>(); public static void init() { // Crystal Hollows Waypoints @@ -72,47 +74,65 @@ public class CrystalsLocationsManager { } private static void extractLocationFromMessage(Text message, Boolean overlay) { - if (!SkyblockerConfigManager.get().mining.crystalsWaypoints.findInChat || !Utils.isInCrystalHollows()) { + if (!SkyblockerConfigManager.get().mining.crystalsWaypoints.findInChat || !Utils.isInCrystalHollows() || overlay) { return; } - + String text = Formatting.strip(message.getString()); try { - //get the message text - String value = message.getString(); - Matcher matcher = TEXT_CWORDS_PATTERN.matcher(value); - //if there are coordinates in the message try to get them and what they are talking about - if (matcher.find()) { - String location = matcher.group(); - int[] coordinates = Arrays.stream(location.split(" ", 3)).mapToInt(Integer::parseInt).toArray(); - BlockPos blockPos = new BlockPos(coordinates[0], coordinates[1], coordinates[2]); - - //if position is not in the hollows do not add it - if (!checkInCrystals(blockPos)) { - return; - } + //make sure that it is only reading user messages and not from skyblocker + if (text.contains(":") && !text.startsWith(Constants.PREFIX.get().getString())) { + String userMessage = text.split(":", 2)[1]; + + //get the message text + Matcher matcher = TEXT_CWORDS_PATTERN.matcher(userMessage); + //if there are coordinates in the message try to get them and what they are talking about + if (matcher.find()) { + BlockPos blockPos = new BlockPos(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(3))); + String location = blockPos.getX() + " " + blockPos.getY() + " " + blockPos.getZ(); + //if position is not in the hollows do not add it + if (!checkInCrystals(blockPos)) { + return; + } - //see if there is a name of a location to add to this - for (String waypointLocation : WAYPOINT_LOCATIONS.keySet()) { - if (value.toLowerCase().contains(waypointLocation.toLowerCase())) { //todo be more lenient - //all data found to create waypoint - addCustomWaypoint(waypointLocation, blockPos); + //see if there is a name of a location to add to this + for (String waypointLocation : WAYPOINT_LOCATIONS.keySet()) { + if (Arrays.stream(waypointLocation.toLowerCase().split(" ")).anyMatch(word -> userMessage.toLowerCase().contains(word))) { //check if contains a word of location + //all data found to create waypoint + //make sure the waypoint does not already exist in active waypoints, so waypoints can not get randomly moved + if (!activeWaypoints.containsKey(waypointLocation)) { + addCustomWaypoint(waypointLocation, blockPos); + } + return; + } + } + + //if the location is not found ask the user for the location (could have been in a previous chat message) + if (CLIENT.player == null || CLIENT.getNetworkHandler() == null) { return; } - } - //if the location is not found ask the user for the location (could have been in a previous chat message) - if (CLIENT.player == null || CLIENT.getNetworkHandler() == null) { - return; + CLIENT.player.sendMessage(getLocationMenu(location, false), false); } - - CLIENT.player.sendMessage(getLocationInputText(location), false); } + } catch (Exception e) { 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); + } + } + } } - protected static Boolean checkInCrystals(BlockPos pos) { + protected static boolean checkInCrystals(BlockPos pos) { //checks if a location is inside crystal hollows bounds return pos.getX() >= 202 && pos.getX() <= 823 && pos.getZ() >= 202 && pos.getZ() <= 823 @@ -122,16 +142,44 @@ public class CrystalsLocationsManager { private static void registerWaypointLocationCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { dispatcher.register(literal(SkyblockerMod.NAMESPACE) .then(literal("crystalWaypoints") - .then(argument("pos", ClientBlockPosArgumentType.blockPos()) + .then(literal("add") + .executes(context -> { + if (CLIENT.player == null) { + return 0; + } + CLIENT.player.sendMessage(getLocationMenu((int) CLIENT.player.getX() + " " + (int) CLIENT.player.getY() + " " + (int) CLIENT.player.getZ(), true), false); + return Command.SINGLE_SUCCESS; + }) + .then(argument("pos", ClientBlockPosArgumentType.blockPos()) + .then(argument("place", StringArgumentType.greedyString()) + .suggests((context, builder) -> suggestMatching(WAYPOINT_LOCATIONS.keySet(), builder)) + .executes(context -> addWaypointFromCommand(context.getSource(), getString(context, "place"), context.getArgument("pos", ClientPosArgument.class))) + ) + )) + .then(literal("share") + .executes(context -> { + if (CLIENT.player == null) { + return 0; + } + CLIENT.player.sendMessage(getPlacesMenu("share"), false); + return Command.SINGLE_SUCCESS; + }) .then(argument("place", StringArgumentType.greedyString()) .suggests((context, builder) -> suggestMatching(WAYPOINT_LOCATIONS.keySet(), builder)) - .executes(context -> addWaypointFromCommand(context.getSource(), getString(context, "place"), context.getArgument("pos", ClientPosArgument.class))) + .executes(context -> shareWaypoint(getString(context, "place"))) ) ) - .then(literal("share") + .then(literal("remove") + .executes(context -> { + if (CLIENT.player == null) { + return 0; + } + CLIENT.player.sendMessage(getPlacesMenu("remove"), false); + return Command.SINGLE_SUCCESS; + }) .then(argument("place", StringArgumentType.greedyString()) .suggests((context, builder) -> suggestMatching(WAYPOINT_LOCATIONS.keySet(), builder)) - .executes(context -> shareWaypoint(getString(context, "place"))) + .executes(context -> removeWaypoint(getString(context, "place"))) ) ) ) @@ -139,21 +187,77 @@ public class CrystalsLocationsManager { } protected static Text getSetLocationMessage(String location, BlockPos blockPos) { - MutableText text = Constants.PREFIX.get(); - text.append(Text.literal("Added waypoint for ")); - Color locationColor = WAYPOINT_LOCATIONS.get(location).color; - text.append(Text.literal(location).withColor(locationColor.getRGB())); - text.append(Text.literal(" at : " + blockPos.getX() + " " + blockPos.getY() + " " + blockPos.getZ() + ".")); + int locationColor = WAYPOINT_LOCATIONS.get(location).getColor(); - return text; + // Minecraft transforms all arguments (`%s`, `%d`, whatever) to `%$1s` DURING LOADING in `Language#load(InputStream, BiConsumer<String,String>)` for some unknown reason. + // And then `TranslatableTextContent#forEachPart` only accepts `%s` for some other unknown reason. + // So that's why the arguments are all `%s`. Wtf mojang????????? + return Constants.PREFIX.get().append(Text.translatableWithFallback("skyblocker.config.mining.crystalsWaypoints.addedWaypoint", "Added waypoint for '%s' at %s %s %s.", Text.literal(location).withColor(locationColor), blockPos.getX(), blockPos.getY(), blockPos.getZ())); } - private static Text getLocationInputText(String location) { - MutableText text = Constants.PREFIX.get(); + /** + * Creates a formated text with a list of possible places to add a waypoint for + * + * @param location the location where the waypoint will be created + * @param excludeUnknown if the {@link de.hysky.skyblocker.skyblock.dwarven.MiningLocationLabel.CrystalHollowsLocationsCategory#UNKNOWN Unknown} location should be available to add + * @return text for a message to send to the player + */ + private static Text getLocationMenu(String location, boolean excludeUnknown) { + + //if the user has all available waypoints active warn them instead of an empty list (excused unknown from check when disabled) + if (activeWaypoints.size() == WAYPOINT_LOCATIONS.size() || (excludeUnknown && WAYPOINT_LOCATIONS.size() - activeWaypoints.size() == 1 && !activeWaypoints.containsKey(MiningLocationLabel.CrystalHollowsLocationsCategory.UNKNOWN.getName()))) { + return Constants.PREFIX.get().append(Text.translatable("skyblocker.config.mining.crystalsWaypoints.allActive").formatted(Formatting.RED)); + } + + //add starting message + MutableText text = Text.empty(); + //add possible locations to the message for (String waypointLocation : WAYPOINT_LOCATIONS.keySet()) { - Color locationColor = WAYPOINT_LOCATIONS.get(waypointLocation).color; - text.append(Text.literal("[" + waypointLocation + "]").withColor(locationColor.getRGB()).styled(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/skyblocker crystalWaypoints " + location + " " + waypointLocation)))); + //do not show option to add waypoints for existing locations or unknown if its disabled + if (activeWaypoints.containsKey(waypointLocation) || (excludeUnknown && Objects.equals(waypointLocation, MiningLocationLabel.CrystalHollowsLocationsCategory.UNKNOWN.getName()))) { + continue; + } + int locationColor = WAYPOINT_LOCATIONS.get(waypointLocation).getColor(); + text.append(Text.literal("[" + waypointLocation + "]").withColor(locationColor).styled(style -> style + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/skyblocker crystalWaypoints add " + location + " " + waypointLocation)) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.translatable("skyblocker.config.mining.crystalsWaypoints.getLocationHover.add").withColor(locationColor)))) + ); + } + + return Constants.PREFIX.get().append(Text.translatable("skyblocker.config.mining.crystalsWaypoints.markLocation", location, text)); + } + + /** + * Creates a formated text with a list of found places to remove / share a waypoint for + * + * @param action the action the command should perform (remove / share) + * @return text for a message to send to the player + */ + private static Text getPlacesMenu(String action) { + MutableText text = Constants.PREFIX.get(); + + //if the user has no active warn them instead of an empty list + if (activeWaypoints.isEmpty()) { + return text.append(Text.translatable("skyblocker.config.mining.crystalsWaypoints.noActive").formatted(Formatting.RED)); + } + + //depending on the action load the correct prefix and hover message + MutableText hoverMessage; + if (action.equals("remove")) { + text.append(Text.translatable("skyblocker.config.mining.crystalsWaypoints.getLocationHover.remove").append(Text.literal(": "))); + hoverMessage = Text.translatable("skyblocker.config.mining.crystalsWaypoints.getLocationHover.remove"); + } else { + text.append(Text.translatable("skyblocker.config.mining.crystalsWaypoints.getLocationHover.share").append(Text.literal(": "))); + hoverMessage = Text.translatable("skyblocker.config.mining.crystalsWaypoints.getLocationHover.share"); + } + + for (String waypointLocation : activeWaypoints.keySet()) { + int locationColor = WAYPOINT_LOCATIONS.get(waypointLocation).getColor(); + text.append(Text.literal("[" + waypointLocation + "]").withColor(locationColor).styled(style -> style + .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/skyblocker crystalWaypoints " + action + " " + waypointLocation)) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverMessage.withColor(locationColor)))) + ); } return text; @@ -178,8 +282,8 @@ public class CrystalsLocationsManager { public static int shareWaypoint(String place) { if (activeWaypoints.containsKey(place)) { - BlockPos pos = activeWaypoints.get(place).pos; - MessageScheduler.INSTANCE.sendMessageAfterCooldown(Constants.PREFIX.get().getString() + " " + place + ": " + pos.getX() + ", " + pos.getY() + ", " + pos.getZ()); + Vec3d pos = activeWaypoints.get(place).centerPos(); + MessageScheduler.INSTANCE.sendMessageAfterCooldown(Constants.PREFIX.get().getString() + " " + place + ": " + (int) pos.getX() + ", " + (int) pos.getY() + ", " + (int) pos.getZ()); } else { //send fail message if (CLIENT.player == null || CLIENT.getNetworkHandler() == null) { @@ -191,25 +295,57 @@ public class CrystalsLocationsManager { return Command.SINGLE_SUCCESS; } + public static int removeWaypoint(String place) { + if (CLIENT.player == null || CLIENT.getNetworkHandler() == null) { + return 0; + } + if (activeWaypoints.containsKey(place)) { + CLIENT.player.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.config.mining.crystalsWaypoints.removeSuccess").formatted(Formatting.GREEN)).append(Text.literal(place).withColor(WAYPOINT_LOCATIONS.get(place).getColor())), false); + activeWaypoints.remove(place); + verifiedWaypoints.remove(place); + } else { + //send fail message + CLIENT.player.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.config.mining.crystalsWaypoints.removeFail").formatted(Formatting.RED)), false); + } - private static void addCustomWaypoint(String waypointName, BlockPos pos) { - CrystalsWaypoint.Category category = WAYPOINT_LOCATIONS.get(waypointName); - CrystalsWaypoint waypoint = new CrystalsWaypoint(category, Text.literal(waypointName), pos); + return Command.SINGLE_SUCCESS; + } + + + protected static void addCustomWaypoint(String waypointName, BlockPos pos) { + removeUnknownNear(pos); + MiningLocationLabel.CrystalHollowsLocationsCategory category = WAYPOINT_LOCATIONS.get(waypointName); + MiningLocationLabel waypoint = new MiningLocationLabel(category, pos); activeWaypoints.put(waypointName, waypoint); } + /** + * Removes unknown waypoint from active waypoints if it's close to a location + * + * @param location center location + */ + private static void removeUnknownNear(BlockPos location) { + String name = MiningLocationLabel.CrystalHollowsLocationsCategory.UNKNOWN.getName(); + MiningLocationLabel unknownWaypoint = activeWaypoints.getOrDefault(name, null); + if (unknownWaypoint != null) { + double distance = unknownWaypoint.centerPos().distanceTo(location.toCenterPos()); + if (distance < REMOVE_UNKNOWN_DISTANCE) { + activeWaypoints.remove(name); + } + } + } + public static void render(WorldRenderContext context) { if (SkyblockerConfigManager.get().mining.crystalsWaypoints.enabled) { - for (CrystalsWaypoint crystalsWaypoint : activeWaypoints.values()) { - if (crystalsWaypoint.shouldRender()) { - crystalsWaypoint.render(context); - } + for (MiningLocationLabel crystalsWaypoint : activeWaypoints.values()) { + crystalsWaypoint.render(context); } } } private static void reset() { activeWaypoints.clear(); + verifiedWaypoints.clear(); } public static void update() { |