diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DebugCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DebugCategory.java index 9ff08e2f65..a09040a0d0 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DebugCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DebugCategory.java @@ -44,6 +44,13 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.debug.dumpFormat = newValue) .controller(opt -> EnumControllerBuilder.create(opt).enumClass(Debug.DumpFormat.class)) // ConfigUtils::createEnumCyclingListController causes a NPE for some reason .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.debug.corpseFinderDebug")) + .binding(defaults.debug.corpseFinderDebug, + () -> config.debug.corpseFinderDebug, + newValue -> config.debug.corpseFinderDebug = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java index 74be78cbb6..cd864768ec 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java @@ -264,12 +264,36 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .collapsed(false) .option(Option.createBuilder() .name(Text.translatable("skyblocker.config.mining.glacite.coldOverlay")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.glacite.coldOverlay@Tooltip"))) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.glacite.coldOverlay.@Tooltip"))) .binding(defaults.mining.glacite.coldOverlay, () -> config.mining.glacite.coldOverlay, newValue -> config.mining.glacite.coldOverlay = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.mining.glacite.enableCorpseFinder")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.glacite.enableCorpseFinder.@Tooltip"))) + .binding(defaults.mining.glacite.enableCorpseFinder, + () -> config.mining.glacite.enableCorpseFinder, + newValue -> config.mining.glacite.enableCorpseFinder = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.mining.glacite.enableParsingChatCorpseFinder")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.glacite.enableParsingChatCorpseFinder.@Tooltip"))) + .binding(defaults.mining.glacite.enableParsingChatCorpseFinder, + () -> config.mining.glacite.enableParsingChatCorpseFinder, + newValue -> config.mining.glacite.enableParsingChatCorpseFinder = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.mining.glacite.autoShareCorpses")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.glacite.autoShareCorpses.@Tooltip"))) + .binding(defaults.mining.glacite.autoShareCorpses, + () -> config.mining.glacite.autoShareCorpses, + newValue -> config.mining.glacite.autoShareCorpses = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .build()) .build(); } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/DebugConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/DebugConfig.java index 8002363117..df0a69c646 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/DebugConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/DebugConfig.java @@ -15,4 +15,7 @@ public class DebugConfig { @SerialEntry public boolean webSocketDebug = false; + + @SerialEntry + public boolean corpseFinderDebug = false; } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java index 2ef5a4dc89..fdd3b6e7c0 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java @@ -141,6 +141,15 @@ public String toString() { public static class Glacite { @SerialEntry public boolean coldOverlay = true; + + @SerialEntry + public boolean enableCorpseFinder = true; + + @SerialEntry + public boolean enableParsingChatCorpseFinder = true; + + @SerialEntry + public boolean autoShareCorpses = false; } public enum DwarvenHudStyle { diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index fe35e7d2d6..2ffa7965d5 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -12,6 +12,7 @@ import de.hysky.skyblocker.skyblock.crimson.slayer.FirePillarAnnouncer; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; +import de.hysky.skyblocker.skyblock.dwarven.CorpseFinder; import de.hysky.skyblocker.skyblock.dwarven.WishingCompassSolver; import de.hysky.skyblocker.skyblock.dwarven.CrystalsChestHighlighter; import de.hysky.skyblocker.skyblock.end.EnderNodes; @@ -124,6 +125,7 @@ public abstract class ClientPlayNetworkHandlerMixin { if (SkyblockerConfigManager.get().slayers.blazeSlayer.firePillarCountdown != SlayersConfig.BlazeSlayer.FirePillar.OFF) FirePillarAnnouncer.checkFirePillar(entity); EggFinder.checkIfEgg(armorStandEntity); + CorpseFinder.checkIfCorpse(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) { @@ -134,5 +136,6 @@ public abstract class ClientPlayNetworkHandlerMixin { @Inject(method = "onEntityEquipmentUpdate", at = @At(value = "TAIL")) private void skyblocker$onEntityEquip(EntityEquipmentUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { EggFinder.checkIfEgg(entity); + CorpseFinder.checkIfCorpse(entity); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java index 299cfef96d..5b92a05d92 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -4,6 +4,7 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.debug.Debug; import de.hysky.skyblocker.events.SkyblockEvents; import de.hysky.skyblocker.utils.*; import de.hysky.skyblocker.utils.command.argumenttypes.EggTypeArgumentType; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseFinder.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseFinder.java new file mode 100644 index 0000000000..b77578db45 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseFinder.java @@ -0,0 +1,288 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import com.google.common.collect.ImmutableBiMap; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.annotations.Init; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.debug.Debug; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.utils.*; +import de.hysky.skyblocker.utils.command.argumenttypes.CorpseTypeArgumentType; +import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientBlockPosArgumentType; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import de.hysky.skyblocker.utils.waypoint.Waypoint; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +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.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Util; +import net.minecraft.util.math.BlockPos; +import org.apache.commons.text.WordUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +public class CorpseFinder { + private static boolean isLocationCorrect = false; + private static final Pattern CORPSE_FOUND_PATTERN = Pattern.compile("([A-Z]+) CORPSE LOOT!"); + private static final Pattern COORDS_PATTERN = Pattern.compile("x: (?-?\\d+), y: (?-?\\d+), z: (?-?\\d+)"); + private static final String PREFIX = "[Skyblocker Corpse Finder] "; + private static final Logger LOGGER = LoggerFactory.getLogger(CorpseFinder.class); + private static final Map> corpsesByType = new HashMap<>(); + private static final String LAPIS_HELMET = "LAPIS_ARMOR_HELMET"; + private static final String UMBER_HELMET = "ARMOR_OF_YOG_HELMET"; + private static final String TUNGSTEN_HELMET = "MINERAL_HELMET"; + private static final String VANGUARD_HELMET = "VANGUARD_HELMET"; + private static final ImmutableBiMap ITEM_IDS = ImmutableBiMap.of( + "LAPIS", LAPIS_HELMET, + "UMBER", UMBER_HELMET, + "TUNGSTEN", TUNGSTEN_HELMET, + "VANGUARD", VANGUARD_HELMET + ); + + @Init + public static void init() { + ClientPlayConnectionEvents.JOIN.register((ignored, ignored2, ignored3) -> { + isLocationCorrect = false; + corpsesByType.clear(); + }); + SkyblockEvents.LOCATION_CHANGE.register(CorpseFinder::handleLocationChange); + ClientReceiveMessageEvents.GAME.register(CorpseFinder::onChatMessage); + WorldRenderEvents.AFTER_TRANSLUCENT.register(CorpseFinder::renderWaypoints); + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if (!SkyblockerConfigManager.get().mining.glacite.enableCorpseFinder || client.player == null) return; + if (!isLocationCorrect) return; + for (List corpses : corpsesByType.values()) { + for (Corpse corpse : corpses) { + if (!corpse.seen && client.player.canSee(corpse.entity)) { + setSeen(corpse); + } + } + } + }); + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE) + .then(literal("corpseHelper") + .then(literal("shareLocation") + .then(argument("blockPos", ClientBlockPosArgumentType.blockPos()) + .then(argument("corpseType", CorpseTypeArgumentType.corpseType()) + .executes(context -> { + shareLocation(ClientBlockPosArgumentType.getBlockPos(context, "blockPos"), StringArgumentType.getString(context, "corpseType")); + return Command.SINGLE_SUCCESS; + }) + ) + ) + ) + ) + )); + } + + private static boolean seenDebugWarning = false; + + private static void handleLocationChange(Location location) { + isLocationCorrect = location == Location.GLACITE_MINESHAFT; + } + + public static void checkIfCorpse(Entity entity) { + if (entity instanceof ArmorStandEntity armorStand) checkIfCorpse(armorStand); + } + + public static void checkIfCorpse(ArmorStandEntity armorStand) { + if (!isLocationCorrect || !SkyblockerConfigManager.get().mining.glacite.enableCorpseFinder) return; + if (armorStand.hasCustomName() || armorStand.isInvisible() || armorStand.shouldShowBasePlate()) return; + handleArmorStand(armorStand); + } + + private static void handleArmorStand(ArmorStandEntity armorStand) { + String itemId = ItemUtils.getItemId(armorStand.getEquippedStack(EquipmentSlot.HEAD)); + if (!ITEM_IDS.containsValue(itemId)) return; + + LOGGER.debug(PREFIX + "Triggered code for handleArmorStand and matched with ITEM_IDS"); + List corpses = corpsesByType.computeIfAbsent(itemId, k -> new ArrayList<>()); + if (corpses.stream().noneMatch(c -> c.entity.getBlockPos().equals(armorStand.getBlockPos()))) { + Waypoint corpseWaypoint; + float[] color = getColors(getColor(armorStand)); + corpseWaypoint = new Waypoint(armorStand.getBlockPos().up(), Waypoint.Type.OUTLINED_WAYPOINT, color); + if (Debug.debugEnabled() && SkyblockerConfigManager.get().debug.corpseFinderDebug && !seenDebugWarning && (seenDebugWarning = true)) { + MinecraftClient.getInstance().player.sendMessage( + Constants.PREFIX.get().append( + Text.literal("Corpse finder debug mode is active! Please use it only for the sake of debugging corpse detection!") + .formatted(Formatting.GOLD, Formatting.BOLD) + ), false); + } + Corpse newCorpse = new Corpse(armorStand, corpseWaypoint, ITEM_IDS.inverse().getOrDefault(itemId, "UNKNOWN")); + corpses.add(newCorpse); + } + } + + private static void renderWaypoints(WorldRenderContext context) { + if (!SkyblockerConfigManager.get().mining.glacite.enableCorpseFinder || !isLocationCorrect) return; + for (List corpses : corpsesByType.values()) { + for (Corpse corpse : corpses) { + if (corpse.waypoint.shouldRender() && (corpse.seen || (Debug.debugEnabled() && SkyblockerConfigManager.get().debug.corpseFinderDebug))) { + corpse.waypoint.render(context); + } + } + } + } + + private static void onChatMessage(Text text, boolean overlay) { + if (overlay || !isLocationCorrect || !SkyblockerConfigManager.get().mining.glacite.enableCorpseFinder || MinecraftClient.getInstance().player == null) return; + String string = text.getString(); + if (string.contains(MinecraftClient.getInstance().getSession().getUsername())) return; // Ignore your own messages + if (SkyblockerConfigManager.get().mining.glacite.enableParsingChatCorpseFinder) parseCords(text); // parsing cords from chat + + Matcher matcherCorpse = CORPSE_FOUND_PATTERN.matcher(string); + if (!matcherCorpse.find()) return; + + LOGGER.debug(PREFIX + "Triggered code for onChatMessage"); + LOGGER.debug(PREFIX + "State of corpsesByType: {}", corpsesByType); + String corpseType = matcherCorpse.group(1).toUpperCase(); + String key = ITEM_IDS.getOrDefault(corpseType, null); + + List corpses = corpsesByType.get(key); + if (corpses == null) { + LOGGER.warn(PREFIX + "Couldn't get corpses! corpseType: {}, key: {}", corpseType, key); + return; + } + corpses.stream() // Since squared distance comparison will yield the same result as normal distance comparison, we can use squared distance to avoid square root calculation + .min(Comparator.comparingDouble(corpse -> corpse.entity.squaredDistanceTo(MinecraftClient.getInstance().player))) + .ifPresentOrElse( + corpse -> { + LOGGER.info(PREFIX + "Found corpse, marking as found! {}: {}", corpse.entity.getType(), corpse.entity.getBlockPos().toShortString()); + corpse.waypoint.setFound(); + }, + () -> LOGGER.warn(PREFIX + "Couldn't find the closest corpse despite triggering onChatMessage!") + ); + } + + @SuppressWarnings("DataFlowIssue") + private static void setSeen(Corpse corpse) { + corpse.seen = true; + if (SkyblockerConfigManager.get().mining.glacite.autoShareCorpses) { + shareLocation(corpse.entity.getBlockPos().up(), corpse.name); + return; // There's no need to send the message twice, so we return here. + } + if (Util.getMeasuringTimeMs() - corpse.messageLastSent < 300) return; + + corpse.messageLastSent = Util.getMeasuringTimeMs(); + + MinecraftClient.getInstance().player.sendMessage( + Constants.PREFIX.get() + .append("Found a ") + .append(Text.literal(WordUtils.capitalizeFully(corpse.name) + " Corpse") + .withColor(corpse.color.getColorValue())) + .append(" at " + corpse.entity.getBlockPos().up().toShortString() + "!") + .styled(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/skyblocker corpseHelper shareLocation " + PosUtils.toSpaceSeparatedString(corpse.waypoint.pos) + " " + corpse.name)) + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal("Click to share the location in chat!").formatted(Formatting.GREEN)))), false); + } + + private static Formatting getColor(ArmorStandEntity entity) { + String itemId = ItemUtils.getItemId(entity.getEquippedStack(EquipmentSlot.HEAD)); + if (ITEM_IDS.containsValue(itemId)) { + switch (itemId) { + case LAPIS_HELMET, VANGUARD_HELMET: + return Formatting.BLUE; // dark blue looks bad and those two never exist in same shaft + case UMBER_HELMET: + return Formatting.RED; + case TUNGSTEN_HELMET: + return Formatting.GRAY; + } + } + + LOGGER.warn(PREFIX + "Couldn't match a color! Something probably went very wrong!"); + return Formatting.YELLOW; + } + + private static void shareLocation(BlockPos pos, String corpseType) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/pc " + toSkyhanniFormat(pos) + " | (" + WordUtils.capitalizeFully(corpseType) + " Corpse)", true); + } + + @SuppressWarnings("DataFlowIssue") + private static float[] getColors(Formatting color) { + return ColorUtils.getFloatComponents(color.getColorValue()); + } + + // Since read in their format, might as well send in their format too. + // Some other mods seem to send in this same format, so it'll help any other mods that might be listening for this format. + private static String toSkyhanniFormat(BlockPos pos) { + return String.format("x: %d, y: %d, z: %d", pos.getX() + 1, pos.getY(), pos.getZ() + 1); + } + + private static void parseCords(Text text) { + String message = text.getString(); + Matcher matcher = COORDS_PATTERN.matcher(message); + if (!matcher.find()) return; + + int x = Integer.parseInt(matcher.group("x")); + int y = Integer.parseInt(matcher.group("y")); + int z = Integer.parseInt(matcher.group("z")); + LOGGER.debug(PREFIX + "Parsed message! X:{}, Y:{}, Z:{}", x, y, z); + boolean foundCorpse = false; + BlockPos parsedPos = new BlockPos(x - 1, y, z - 1); // skyhanni cords format difference is -1, 0, -1 + + for (List corpses : corpsesByType.values()) { + for (Corpse corpse : corpses) { + if (corpse.waypoint.pos.equals(parsedPos)) { + corpse.seen = true; + foundCorpse = true; + LOGGER.info(PREFIX + "Setting corpse {} as seen!", corpse.entity); + MinecraftClient.getInstance().player.sendMessage( + Constants.PREFIX.get() + .append("Parsed message from chat, adding corpse at ") + .append(corpse.entity.getBlockPos().toShortString()), false); + break; + } + } + } + if (!foundCorpse) { + LOGGER.warn(PREFIX + "Did NOT find any match for corpses! corpsesByType.values(): {}", corpsesByType.values()); + LOGGER.info(PREFIX + "Proceeding to iterate over all corpses!"); + for (List corpses : corpsesByType.values()) { + for (Corpse corpse : corpses) { + LOGGER.info(PREFIX + "Corpse: {}, BlockPos: {}", corpse.entity, corpse.entity.getBlockPos()); + } + } + } + } + + static class Corpse { + private final ArmorStandEntity entity; + /** + * Waypoint position is always 1 above entity position + */ + private final Waypoint waypoint; + private boolean seen; + private long messageLastSent = 0; + private final Formatting color; + /** + * Type of the corpse, fully uppercased. + */ + private final String name; + + Corpse(ArmorStandEntity entity, Waypoint waypoint, String name) { + this.entity = entity; + this.waypoint = waypoint; + this.seen = false; + this.color = getColor(entity); + this.name = name; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/CorpseTypeArgumentType.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/CorpseTypeArgumentType.java new file mode 100644 index 0000000000..e67d53cd36 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/CorpseTypeArgumentType.java @@ -0,0 +1,43 @@ +package de.hysky.skyblocker.utils.command.argumenttypes; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.command.CommandSource; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public final class CorpseTypeArgumentType implements ArgumentType { + + private static final List CORPSE_TYPES = List.of("LAPIS", "UMBER", "TUNGSTEN", "VANGUARD"); + + @Override + public String parse(StringReader reader) throws CommandSyntaxException { + String name = reader.readUnquotedString(); + if (CORPSE_TYPES.contains(name.toUpperCase())) { + return name.toUpperCase(); + } + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create(); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return context.getSource() instanceof CommandSource + ? CommandSource.suggestMatching(CORPSE_TYPES.stream().map(String::toLowerCase), builder) + : Suggestions.empty(); + } + + @Override + public Collection getExamples() { + return CORPSE_TYPES; + } + + public static CorpseTypeArgumentType corpseType() { + return new CorpseTypeArgumentType(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java b/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java index da927cb7f4..661469c2c0 100644 --- a/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java +++ b/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java @@ -36,6 +36,10 @@ public Waypoint(BlockPos pos, Type type, float[] colorComponents, float alpha) { this(pos, () -> type, colorComponents, alpha, DEFAULT_LINE_WIDTH); } + public Waypoint(BlockPos pos, Type type, float[] colorComponents, boolean throughWalls) { + this(pos, () -> type, colorComponents, DEFAULT_HIGHLIGHT_ALPHA, DEFAULT_LINE_WIDTH, throughWalls); + } + public Waypoint(BlockPos pos, Supplier typeSupplier, float[] colorComponents, float alpha, float lineWidth) { this(pos, typeSupplier, colorComponents, alpha, lineWidth, true); } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index c590d37605..2767a1760a 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -68,6 +68,7 @@ "skyblocker.config.debug.dumpRange.@Tooltip": "The range in blocks around the player to dump entities within.", "skyblocker.config.debug.showInvisibleArmorStands": "Show Invisible Armor Stands", "skyblocker.config.debug.debugWebSockets": "Enable Websocket Debug Messages", + "skyblocker.config.debug.corpseFinderDebug": "Debug Corpse Finder", "skyblocker.config.dungeons": "Dungeons", @@ -607,7 +608,13 @@ "skyblocker.config.mining.glacite": "Glacite Tunnels", "skyblocker.config.mining.glacite.coldOverlay": "Cold Overlay", - "skyblocker.config.mining.glacite.coldOverlay@Tooltip": "Shows a frosty overlay in the Glacite mines that gets stronger as you get colder.", + "skyblocker.config.mining.glacite.coldOverlay.@Tooltip": "Shows a frosty overlay in the Glacite mines that gets stronger as you get colder.", + "skyblocker.config.mining.glacite.enableCorpseFinder": "Corpse Finder", + "skyblocker.config.mining.glacite.enableCorpseFinder.@Tooltip": "Creates waypoints for corpses. Allows for sharing them.", + "skyblocker.config.mining.glacite.enableParsingChatCorpseFinder": "Parse Corpses from chat", + "skyblocker.config.mining.glacite.enableParsingChatCorpseFinder.@Tooltip": "If enabled, will listen to chat and process corpses shared by other players. Should be compatible with other mods.", + "skyblocker.config.mining.glacite.autoShareCorpses": "Automatically Share Found Corpses In Chat", + "skyblocker.config.mining.glacite.autoShareCorpses.@Tooltip": "Sends a message in party chat with the location of the found corpse when you find one.", "skyblocker.config.misc": "Misc", diff --git a/src/main/resources/assets/skyblocker/lang/ru_ru.json b/src/main/resources/assets/skyblocker/lang/ru_ru.json index 77bb3db083..55f1cddbb1 100644 --- a/src/main/resources/assets/skyblocker/lang/ru_ru.json +++ b/src/main/resources/assets/skyblocker/lang/ru_ru.json @@ -876,7 +876,7 @@ "skyblocker.profileviewer.inventory.pets": "Питомцы", "skyblocker.profileviewer.inventory.backpack": "Рюкзак", "skyblocker.profileviewer.inventory.accessoryBag": "Сумка для аксессуаров", - "skyblocker.config.mining.glacite.coldOverlay@Tooltip": "Показывает морозное наложение в шахтах Гласита, которое становится сильнее по мере того, как становится холоднее.", + "skyblocker.config.mining.glacite.coldOverlay.@Tooltip": "Показывает морозное наложение в шахтах Гласита, которое становится сильнее по мере того, как становится холоднее.", "skyblocker.crimson.dojo.controlHelper": "Включить помощник Control", "skyblocker.config.mining.crystalsWaypoints.getLocationHover.add": "Добавить локацию", "skyblocker.config.mining.crystalsWaypoints.getLocationHover.remove": "Удалить локацию", diff --git a/src/main/resources/assets/skyblocker/lang/zh_cn.json b/src/main/resources/assets/skyblocker/lang/zh_cn.json index 3b89f23727..563a607f78 100644 --- a/src/main/resources/assets/skyblocker/lang/zh_cn.json +++ b/src/main/resources/assets/skyblocker/lang/zh_cn.json @@ -650,7 +650,7 @@ "skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder.@Tooltip": "当时间塔停止运作时,在聊天栏发送一条提示信息。", "skyblocker.config.mining.glacite.coldOverlay": "寒冷效果动画修改", "skyblocker.config.mining.glacite": "极寒隧道", - "skyblocker.config.mining.glacite.coldOverlay@Tooltip": "显示极寒隧道中的霜冻效果,随着温度的降低,霜冻效果会变得更强。", + "skyblocker.config.mining.glacite.coldOverlay.@Tooltip": "显示极寒隧道中的霜冻效果,随着温度的降低,霜冻效果会变得更强。", "skyblocker.config.misc": "杂项", "skyblocker.config.general.searchOverlay.maxPet.@Tooltip": "只显示最高等级的宠物", "skyblocker.config.general.searchOverlay.starsTooltip": "地牢物品的星星数量", diff --git a/src/main/resources/assets/skyblocker/lang/zh_tw.json b/src/main/resources/assets/skyblocker/lang/zh_tw.json index 71f28fb8d8..d3d39652f5 100644 --- a/src/main/resources/assets/skyblocker/lang/zh_tw.json +++ b/src/main/resources/assets/skyblocker/lang/zh_tw.json @@ -650,7 +650,7 @@ "skyblocker.config.helpers.chocolateFactory.waypointType": "彩蛋路徑點類型", "skyblocker.config.mining.glacite": "極寒隧道", "skyblocker.config.mining.glacite.coldOverlay": "寒冷效果動畫修改", - "skyblocker.config.mining.glacite.coldOverlay@Tooltip": "顯示極寒隧道中的霜凍效果,隨著溫度的降低,霜凍效果會變得更強。", + "skyblocker.config.mining.glacite.coldOverlay.@Tooltip": "顯示極寒隧道中的霜凍效果,隨著溫度的降低,霜凍效果會變得更強。", "skyblocker.config.misc": "雜項", "skyblocker.config.general.searchOverlay.starsTooltip": "地下城物品的星星數量", "skyblocker.config.general.searchOverlay.maxPet": "最高寵物等級",