diff options
42 files changed, 2928 insertions, 289 deletions
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml deleted file mode 100644 index f1ec8445..00000000 --- a/.github/workflows/labeler.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: Automatic Labeling - -on: - pull_request_target: - types: [opened, synchronize, reopened, ready_for_review, converted_to_draft, closed] - pull_request_review: - types: [submitted, dismissed] - -jobs: - labeler: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set Labels - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GH_LABELER }} - script: | - const { owner, repo, number: pull_number } = context.issue; - const { data: pullRequest } = await github.rest.pulls.get({ owner, repo, pull_number }); - const labels = pullRequest.labels.map(label => label.name); - - let label = ''; - - if (pullRequest.draft) { - label = 'wip'; - } else if (pullRequest.mergeable_state === 'dirty') { - label = 'merge conflicts'; - } else if (pullRequest.merged) { - label = '' - } else { - const { data: reviews } = await github.rest.pulls.listReviews({ owner, repo, pull_number }); - const { data: commits } = await github.rest.pulls.listCommits({ owner, repo, pull_number }); - - const filteredReviews = reviews.filter(review => review.state === 'CHANGES_REQUESTED' || review.state === 'APPROVED'); - const lastReview = filteredReviews.sort((a, b) => new Date(b.submitted_at) - new Date(a.submitted_at))[0]; - const lastCommit = commits.sort((a, b) => new Date(b.commit.committer.date) - new Date(a.commit.committer.date))[0]; - - if (!lastReview || new Date(lastReview.submitted_at) < new Date(lastCommit.commit.committer.date)) { - label = 'reviews needed'; - } else if (lastReview.state === 'CHANGES_REQUESTED') { - label = 'changes requested'; - } else if (lastReview.state === 'APPROVED') { - label = 'merge me please'; - } - } - - // if label is not set change the label - if (!labels.includes(label)) { - // list of labels to be removed - const labelsToRemove = ['merge me please', 'reviews needed', 'merge conflicts', 'wip', 'changes requested']; - - // remove all label from list - const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({ owner, repo, issue_number: pull_number }); - for (const { name } of currentLabels) { - if (labelsToRemove.includes(name)) { - await github.rest.issues.removeLabel({ - owner, - repo, - issue_number: pull_number, - name - }); - } - } - - // add label - if (label) { - await github.rest.issues.addLabels({ - owner, - repo, - issue_number: pull_number, - labels: [label] - }); - } - }
\ No newline at end of file diff --git a/.github/workflows/webhook_translate.yml b/.github/workflows/webhook_translate.yml index 5d1208fa..1998ee73 100644 --- a/.github/workflows/webhook_translate.yml +++ b/.github/workflows/webhook_translate.yml @@ -15,10 +15,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 2 + fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '1.20' @@ -39,13 +39,14 @@ jobs: OUTPUT="\`\`\`diff $OUTPUT \`\`\`" + OUTPUT=$(jq -Rn --arg str "$OUTPUT" '$str' | sed -E "s|^(['\"])(.*)\1$|\2|g") JSON_PAYLOAD='{ "content": "<@&1134566053138145330>", "embeds": [ { "title": "Something Changed (Click me)", - "description": "'"${OUTPUT//$'\n'/\\n}"'", + "description": "'"${OUTPUT}"'", "url": "https://translate.hysky.de/projects/Skyblocker/skyblocker", "color": 5808639, "image": { @@ -56,4 +57,9 @@ jobs: "attachments": [] }' - curl -H "Content-Type: application/json" --data-binary "$JSON_PAYLOAD" "${{ secrets.DISCORD_TRANSLATE_WEBHOOK }}" + if echo "$JSON_PAYLOAD" | jq . &>/dev/null; then + curl -H "Content-Type: application/json" --data-binary "$JSON_PAYLOAD" "${{ secrets.DISCORD_TRANSLATE_WEBHOOK }}" + else + echo "Error:" + echo "$JSON_PAYLOAD" + fi
\ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ea73c37..74bd6550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +# Release 1.19.1 + +## Highlight +* Fix Croesus Solver crash +* Basic garden mouse locking feature (set sensitivity to 0) +* New Year Cakes Helper +* add Zodd room (Dungeon Secrets) + +## What's Changed +* Add New Year Cakes Helper by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/603 +* Basic garden mouse locking feature by @viciscat in https://github.com/SkyblockerMod/Skyblocker/pull/607 +* Fix Commissions Hud & Wiki Lookup by @AzureAaron in https://github.com/SkyblockerMod/Skyblocker/pull/616 +* Redraw square rarity background by @AzureAaron in https://github.com/SkyblockerMod/Skyblocker/pull/619 +* Update IF conflict by @AzureAaron in https://github.com/SkyblockerMod/Skyblocker/pull/620 +* Croesus Solver crash fix by @viciscat in https://github.com/SkyblockerMod/Skyblocker/pull/621 +* Fix Dwarven Hud by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/617 +* Update Dungeon Secrets (Zodd Room) by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/618 +* Remove Visitor from Visitor Helper when you refuse their offer by @viciscat in https://github.com/SkyblockerMod/Skyblocker/pull/626 +* Fix labeler by @LifeIsAParadox in https://github.com/SkyblockerMod/Skyblocker/pull/604 + +**Full Changelog**: https://github.com/SkyblockerMod/Skyblocker/compare/v1.19.0...v1.19.1 +___ # Release 1.19.0 ## Highlight diff --git a/FEATURES.md b/FEATURES.md index 4b9a15f0..698a34cb 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -93,6 +93,7 @@ - **Visitor Helper** - buy items that visitors require from bazaar by clicking the text - **Disable title and chat messages for Melon/Pumpkin Dicer** +- **Basic garden mouse locking feature (set mouse sensitivity to 0)** ### Visual Enhancements: - **Fancy Tab HUD:** Fully configurable with a resource pack. @@ -130,6 +131,7 @@ - **Quicknav:** (Fully customizeable) Fast navigation between pets, armor, enderchest, skill, collection, crafting, enchant, anvil, warp dungeon, and warp hub. - **Recipe Book:** Lists all Skyblock items in the vanilla recipe book, allowing you to see the recipe of the item. - **Backpack Preview:** After clicking your backpack or enderchest once, you can hover over the backpack or enderchest and hold Shift to preview its contents. +- **New Year Cakes Helper** ### Barn Features: - **Barn Solver:** @@ -113,6 +113,7 @@ Installation guide is [here](https://github.com/SkyblockerMod/Skyblocker/wiki/in - **Visitor Helper** - buy items that visitors require from bazaar by clicking the text - **Disable title and chat messages for Melon/Pumpkin Dicer** +- **Basic garden mouse locking feature (set mouse sensitivity to 0)** ### Visual Enhancements: - **Fancy Tab HUD:** Fully configurable with a resource pack. @@ -150,6 +151,7 @@ Installation guide is [here](https://github.com/SkyblockerMod/Skyblocker/wiki/in - **Quicknav:** (Fully customizeable) Fast navigation between pets, armor, enderchest, skill, collection, crafting, enchant, anvil, warp dungeon, and warp hub. - **Recipe Book:** Lists all Skyblock items in the vanilla recipe book, allowing you to see the recipe of the item. - **Backpack Preview:** After clicking your backpack or enderchest once, you can hover over the backpack or enderchest and hold Shift to preview its contents. +- **New Year Cakes Helper** ### Barn Features: - **Barn Solver:** diff --git a/gradle.properties b/gradle.properties index 702a292b..3fff28a6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,7 +34,7 @@ jgit_version = 6.8.0.202311291450-r commons_math_version = 3.6.1 # Mod Properties -mod_version = 1.19.0 +mod_version = 1.19.1 maven_group = de.hysky archives_base_name = skyblocker modrinth_id=y6DuFGwJ diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 0c7ad7bd..485a2103 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -26,6 +26,7 @@ import de.hysky.skyblocker.skyblock.garden.FarmingHud; import de.hysky.skyblocker.skyblock.garden.LowerSensitivity; import de.hysky.skyblocker.skyblock.garden.VisitorHelper; import de.hysky.skyblocker.skyblock.item.*; +import de.hysky.skyblocker.skyblock.item.tooltip.AccessoriesHelper; import de.hysky.skyblocker.skyblock.item.tooltip.BackpackPreview; import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; @@ -39,6 +40,7 @@ import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster; import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; import de.hysky.skyblocker.skyblock.waypoint.FairySouls; import de.hysky.skyblocker.skyblock.waypoint.MythologicalRitual; +import de.hysky.skyblocker.skyblock.waypoint.OrderedWaypoints; import de.hysky.skyblocker.skyblock.waypoint.Relics; import de.hysky.skyblocker.utils.ApiUtils; import de.hysky.skyblocker.utils.NEURepoManager; @@ -103,10 +105,12 @@ public class SkyblockerMod implements ClientModInitializer { PlayerHeadHashCache.init(); HotbarSlotLock.init(); ItemTooltip.init(); + AccessoriesHelper.init(); WikiLookup.init(); FairySouls.init(); Relics.init(); MythologicalRitual.init(); + OrderedWaypoints.init(); BackpackPreview.init(); QuickNav.init(); ItemCooldowns.init(); @@ -163,6 +167,8 @@ public class SkyblockerMod implements ClientModInitializer { containerSolverManager.init(); statusBarTracker.init(); BeaconHighlighter.init(); + WarpAutocomplete.init(); + Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 200); Scheduler.INSTANCE.scheduleCyclic(LividColor::update, 10); diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index a8737598..418cc4d1 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -161,6 +161,9 @@ public class SkyblockerConfig { public boolean betterPartyFinder = true; @SerialEntry + public boolean fancyCraftingTable = true; + + @SerialEntry public boolean backpackPreviewWithoutShift = false; @SerialEntry @@ -562,6 +565,9 @@ public class SkyblockerConfig { @SerialEntry public boolean enableExoticTooltip = true; + + @SerialEntry + public boolean enableAccessoriesHelper = true; } public static class ItemInfoDisplay { diff --git a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java index fe73a6a7..e310cb85 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java @@ -42,6 +42,13 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.fancyCraftingTable")) + .binding(defaults.general.fancyCraftingTable, + () -> config.general.fancyCraftingTable, + newValue -> config.general.fancyCraftingTable = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift")) .binding(defaults.general.backpackPreviewWithoutShift, () -> config.general.backpackPreviewWithoutShift, @@ -442,6 +449,16 @@ public class GeneralCategory { newValue -> config.general.itemTooltip.enableExoticTooltip = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[0]"), Text.literal("\n\n✔ Collected").formatted(Formatting.GREEN), Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[1]"), + Text.literal("\n✦ Upgrade").withColor(0x218bff), Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[2]"), Text.literal("\n↑ Upgradable").withColor(0xf8d048), Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[3]"), + Text.literal("\n↓ Downgrade").formatted(Formatting.GRAY), Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[4]"), Text.literal("\n✖ Missing").formatted(Formatting.RED), Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[5]"))) + .binding(defaults.general.itemTooltip.enableAccessoriesHelper, + () -> config.general.itemTooltip.enableAccessoriesHelper, + newValue -> config.general.itemTooltip.enableAccessoriesHelper = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .build()) //Item Info Display diff --git a/src/main/java/de/hysky/skyblocker/mixin/CommandTreeS2CPacketMixin.java b/src/main/java/de/hysky/skyblocker/mixin/CommandTreeS2CPacketMixin.java new file mode 100644 index 00000000..1cc1b8de --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/CommandTreeS2CPacketMixin.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; +import de.hysky.skyblocker.skyblock.WarpAutocomplete; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.command.CommandSource; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(targets = "net.minecraft.network.packet.s2c.play.CommandTreeS2CPacket$CommandTree") +public class CommandTreeS2CPacketMixin { + @ModifyExpressionValue(method = "getNode", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/CommandTreeS2CPacket$CommandTree;getNode(I)Lcom/mojang/brigadier/tree/CommandNode;", ordinal = 1)) + public CommandNode<? extends CommandSource> modifyCommandSuggestions(CommandNode<CommandSource> original) { + if (Utils.isOnHypixel() && WarpAutocomplete.commandNode != null && original instanceof LiteralCommandNode<?> literalCommandNode && literalCommandNode.getLiteral().equals("warp")) { + return WarpAutocomplete.commandNode; + } + return original; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenProviderMixin.java b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenProviderMixin.java index 94eb53a5..975c9c51 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenProviderMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenProviderMixin.java @@ -3,6 +3,9 @@ package de.hysky.skyblocker.mixin; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen; +import de.hysky.skyblocker.skyblock.item.SkyblockCraftingTableScreenHandler; +import de.hysky.skyblocker.skyblock.item.SkyblockCraftingTableScreen; +import de.hysky.skyblocker.utils.Utils; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.ingame.HandledScreens; import net.minecraft.client.network.ClientPlayerEntity; @@ -19,11 +22,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public interface HandledScreenProviderMixin<T extends ScreenHandler> { @Inject(method = "open", at = @At("HEAD"), cancellable = true) default void skyblocker$open(Text name, ScreenHandlerType<T> type, MinecraftClient client, int id, CallbackInfo ci) { - if (!SkyblockerConfigManager.get().general.betterPartyFinder) return; ClientPlayerEntity player = client.player; if (player == null) return; + if (!Utils.isOnSkyblock()) return; T screenHandler = type.create(id, player.getInventory()); - if (screenHandler instanceof GenericContainerScreenHandler containerScreenHandler && PartyFinderScreen.possibleInventoryNames.contains(name.getString().toLowerCase())) { + if (SkyblockerConfigManager.get().general.betterPartyFinder && screenHandler instanceof GenericContainerScreenHandler containerScreenHandler && PartyFinderScreen.possibleInventoryNames.contains(name.getString().toLowerCase())) { if (client.currentScreen != null) { String lowerCase = client.currentScreen.getTitle().getString().toLowerCase(); if (lowerCase.contains("group builder")) return; @@ -42,6 +45,11 @@ public interface HandledScreenProviderMixin<T extends ScreenHandler> { } ci.cancel(); + } else if (SkyblockerConfigManager.get().general.fancyCraftingTable && screenHandler instanceof GenericContainerScreenHandler containerScreenHandler && name.getString().toLowerCase().contains("craft item")) { + SkyblockCraftingTableScreenHandler skyblockCraftingTableScreenHandler = new SkyblockCraftingTableScreenHandler(containerScreenHandler, player.getInventory()); + client.player.currentScreenHandler = skyblockCraftingTableScreenHandler; + client.setScreen(new SkyblockCraftingTableScreen(skyblockCraftingTableScreenHandler, player.getInventory(), Text.literal("Craft Item"))); + ci.cancel(); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/WarpAutocomplete.java b/src/main/java/de/hysky/skyblocker/skyblock/WarpAutocomplete.java new file mode 100644 index 00000000..8862185f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/WarpAutocomplete.java @@ -0,0 +1,46 @@ +package de.hysky.skyblocker.skyblock; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.tree.LiteralCommandNode; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.utils.Http; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandSource; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +/** + * the mixin {@link de.hysky.skyblocker.mixin.CommandTreeS2CPacketMixin} + */ +public class WarpAutocomplete { + private static final Logger LOGGER = LoggerFactory.getLogger(WarpAutocomplete.class); + @Nullable + public static LiteralCommandNode<FabricClientCommandSource> commandNode; + + public static void init() { + CompletableFuture.supplyAsync(() -> { + try { + JsonArray jsonElements = SkyblockerMod.GSON.fromJson(Http.sendGetRequest("https://hysky.de/api/locations"), JsonArray.class); + return jsonElements.asList().stream().map(JsonElement::getAsString).toList(); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to download warps list", e); + } + return List.<String>of(); + }).thenAccept(warps -> commandNode = literal("warp") + .requires(fabricClientCommandSource -> Utils.isOnSkyblock()) + .then(argument("destination", StringArgumentType.string()) + .suggests((context, builder) -> CommandSource.suggestMatching(warps, builder)) + ).build() + ); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java index d5d57e70..4f5da8de 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java @@ -86,7 +86,7 @@ import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.lit public class DungeonManager { protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonManager.class); private static final String DUNGEONS_PATH = "dungeons"; - private static final Path CUSTOM_WAYPOINTS_DIR = SkyblockerMod.CONFIG_DIR.resolve("custom_secret_waypoints.json"); + private static Path CUSTOM_WAYPOINTS_DIR; private static final Pattern KEY_FOUND = Pattern.compile("^(?:\\[.+] )?(?<name>\\w+) has obtained (?<type>Wither|Blood) Key!$"); /** * Maps the block identifier string to a custom numeric block id used in dungeon rooms data. @@ -214,6 +214,7 @@ public class DungeonManager { * Use {@link #isRoomsLoaded()} to check for completion of loading. */ public static void init() { + CUSTOM_WAYPOINTS_DIR = SkyblockerMod.CONFIG_DIR.resolve("custom_secret_waypoints.json"); if (!SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableRoomMatching) { return; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java index b4ffd409..3297ef5a 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java @@ -43,8 +43,8 @@ public class DwarvenHud { "First Event", "(?:Ruby|Amber|Sapphire|Jade|Amethyst|Topaz) Gemstone Collector", "(?:Amber|Sapphire|Jade|Amethyst|Topaz) Crystal Hunter", - "Chest Looter").map(s -> Pattern.compile("(" + s + "): (\\d+\\.?\\d*%|DONE)")) - .collect(Collectors.toList()); + "Chest Looter").map(s -> Pattern.compile("(" + s + "): (\\d+\\.?\\d*%|DONE)") + ).collect(Collectors.toList()); public static final Pattern MITHRIL_PATTERN = Pattern.compile("Mithril Powder: [0-9,]+"); public static final Pattern GEMSTONE_PATTERN = Pattern.compile("Gemstone Powder: [0-9,]+"); @@ -52,10 +52,13 @@ public class DwarvenHud { ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") .then(ClientCommandManager.literal("hud") .then(ClientCommandManager.literal("dwarven") - .executes(Scheduler.queueOpenScreenCommand(DwarvenHudConfigScreen::new)))))); + .executes(Scheduler.queueOpenScreenCommand(DwarvenHudConfigScreen::new)) + ) + ) + )); HudRenderCallback.EVENT.register((context, tickDelta) -> { - if ((!SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions && !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder) + if (!SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions && !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder || client.options.playerListKey.isPressed() || client.player == null || (!Utils.isInDwarvenMines() && !Utils.isInCrystalHollows())) { @@ -103,24 +106,21 @@ public class DwarvenHud { percentage = 100f; } - context - .drawTextWithShadow(client.textRenderer, - Text.literal(commission.commission + ": ").formatted(Formatting.AQUA) - .append(Text.literal(commission.progression).formatted(Colors.hypixelProgressColor(percentage))), - comHudX + 5, comHudY + y + 5, 0xFFFFFFFF); + context.drawTextWithShadow(client.textRenderer, + Text.literal(commission.commission + ": ").formatted(Formatting.AQUA) + .append(Text.literal(commission.progression).formatted(Colors.hypixelProgressColor(percentage))), + comHudX + 5, comHudY + y + 5, 0xFFFFFFFF); y += 20; } } - if(SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder) { + if (SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder) { //render mithril powder then gemstone - context - .drawTextWithShadow(client.textRenderer, - Text.literal("Mithril: " + mithrilPowder).formatted(Formatting.AQUA), - powderHudX + 5, powderHudY + 5, 0xFFFFFFFF); - context - .drawTextWithShadow(client.textRenderer, - Text.literal("Gemstone: " + gemStonePowder).formatted(Formatting.DARK_PURPLE), - powderHudX + 5, powderHudY + 25, 0xFFFFFFFF); + context.drawTextWithShadow(client.textRenderer, + Text.literal("Mithril: " + mithrilPowder).formatted(Formatting.AQUA), + powderHudX + 5, powderHudY + 5, 0xFFFFFFFF); + context.drawTextWithShadow(client.textRenderer, + Text.literal("Gemstone: " + gemStonePowder).formatted(Formatting.DARK_PURPLE), + powderHudX + 5, powderHudY + 25, 0xFFFFFFFF); } } @@ -146,22 +146,22 @@ public class DwarvenHud { hcw.update(); hcw.setX(comHudX); hcw.setY(comHudY); - hcw.render(context, - SkyblockerConfigManager.get().general.tabHud.enableHudBackground); + hcw.render(context, SkyblockerConfigManager.get().general.tabHud.enableHudBackground); } if (SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder) { hpw.update(); hpw.setX(powderHudX); hpw.setY(powderHudY); - hpw.render(context, - SkyblockerConfigManager.get().general.tabHud.enableHudBackground); + hpw.render(context, SkyblockerConfigManager.get().general.tabHud.enableHudBackground); } } public static void update() { - if (client.player == null || client.getNetworkHandler() == null || (!SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions && !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder) - || (!Utils.isInCrystalHollows() && !Utils.isInDwarvenMines())) + if (client.player == null || client.getNetworkHandler() == null + || !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions && !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder + || !Utils.isInCrystalHollows() && !Utils.isInDwarvenMines()) { return; + } commissionList = new ArrayList<>(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java index bde8b3ea..6640d413 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java @@ -78,7 +78,7 @@ public class VisitorHelper { } public static void onSlotClick(Slot slot, int slotId, String title) { - if (slotId == 29 || slotId == 13) { + if (slotId == 29 || slotId == 13 || slotId == 33) { itemMap.remove(title); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreen.java new file mode 100644 index 00000000..14ddb238 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreen.java @@ -0,0 +1,194 @@ +package de.hysky.skyblocker.skyblock.item; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.skyblock.itemlist.ItemListWidget; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ButtonTextures; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.TexturedButtonWidget; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.SimpleInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeEntry; +import net.minecraft.recipe.RecipeMatcher; +import net.minecraft.recipe.book.RecipeBookCategory; +import net.minecraft.screen.AbstractRecipeScreenHandler; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.screen.slot.Slot; +import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class SkyblockCraftingTableScreen extends HandledScreen<SkyblockCraftingTableScreenHandler> { + private static final Identifier TEXTURE = new Identifier("textures/gui/container/crafting_table.png"); + protected static final ButtonTextures MORE_CRAFTS_TEXTURES = new ButtonTextures( + new Identifier(SkyblockerMod.NAMESPACE, "quick_craft/more_button"), + new Identifier(SkyblockerMod.NAMESPACE, "quick_craft/more_button_disabled"), + new Identifier(SkyblockerMod.NAMESPACE, "quick_craft/more_button_highlighted") + ); + + protected static final Identifier QUICK_CRAFT = new Identifier(SkyblockerMod.NAMESPACE, "quick_craft/quick_craft_overlay"); + private final ItemListWidget recipeBook = new ItemListWidget(); + private boolean narrow; + private TexturedButtonWidget moreCraftsButton; + + public SkyblockCraftingTableScreen(SkyblockCraftingTableScreenHandler handler, PlayerInventory inventory, Text title) { + super(handler, inventory, title); + this.backgroundWidth += 22; + } + + @Override + protected void init() { + super.init(); + this.narrow = this.width < 379; + this.recipeBook.initialize(this.width, this.height, this.client, this.narrow, new DummyRecipeScreenHandler()); + this.x = this.recipeBook.findLeftEdge(this.width, this.backgroundWidth) + 11; + this.addDrawableChild(new TexturedButtonWidget(this.x + 5, this.height / 2 - 49, 20, 18, RecipeBookWidget.BUTTON_TEXTURES, button -> { + this.recipeBook.toggleOpen(); + this.x = this.recipeBook.findLeftEdge(this.width, this.backgroundWidth) + 11; + button.setPosition(this.x + 5, this.height / 2 - 49); + if (moreCraftsButton != null) moreCraftsButton.setPosition(this.x + 174, this.y + 62); + })); + moreCraftsButton = new TexturedButtonWidget(this.x + 174, y + 62, 16, 16, MORE_CRAFTS_TEXTURES, + button -> this.onMouseClick(handler.slots.get(26), handler.slots.get(26).id, 0, SlotActionType.PICKUP)); + moreCraftsButton.setTooltipDelay(250); + moreCraftsButton.setTooltip(Tooltip.of(Text.literal("More Crafts"))); + this.addDrawableChild(moreCraftsButton); + assert (client != null ? client.player : null) != null; + client.player.currentScreenHandler = handler; // recipe book replaces it with the Dummy one fucking DUMBASS + this.addSelectableChild(this.recipeBook); + this.setInitialFocus(this.recipeBook); + this.titleX = 29; + } + + @Override + public void handledScreenTick() { + super.handledScreenTick(); + this.recipeBook.update(); + if (moreCraftsButton == null) return; + ItemStack stack = handler.slots.get(26).getStack(); + moreCraftsButton.active = stack.isEmpty() || stack.isOf(Items.PLAYER_HEAD); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + if (this.recipeBook.isOpen() && this.narrow) { + this.renderBackground(context, mouseX, mouseY, delta); + this.recipeBook.render(context, mouseX, mouseY, delta); + } else { + super.render(context, mouseX, mouseY, delta); + this.recipeBook.render(context, mouseX, mouseY, delta); + this.recipeBook.drawGhostSlots(context, this.x, this.y, true, delta); + } + this.drawMouseoverTooltip(context, mouseX, mouseY); + this.recipeBook.drawTooltip(context, this.x, this.y, mouseX, mouseY); + } + + + @Override + protected void drawSlot(DrawContext context, Slot slot) { + if (slot.id == 23 && slot.getStack().isOf(Items.BARRIER)) return; + super.drawSlot(context, slot); + } + + @Override + protected void drawBackground(DrawContext context, float delta, int mouseX, int mouseY) { + int i = this.x; + int j = (this.height - this.backgroundHeight) / 2; + context.drawTexture(TEXTURE, i, j, 0, 0, this.backgroundWidth, this.backgroundHeight); + context.drawGuiTexture(QUICK_CRAFT, i + 173, j, 0, 25, 84); + } + + @Override + protected boolean isPointWithinBounds(int x, int y, int width, int height, double pointX, double pointY) { + return (!this.narrow || !this.recipeBook.isOpen()) && super.isPointWithinBounds(x, y, width, height, pointX, pointY); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (this.recipeBook.mouseClicked(mouseX, mouseY, button)) { + this.setFocused(this.recipeBook); + return true; + } + if (this.narrow && this.recipeBook.isOpen()) { + return true; + } + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + protected boolean isClickOutsideBounds(double mouseX, double mouseY, int left, int top, int button) { + boolean bl = mouseX < (double) left || mouseY < (double) top || mouseX >= (double) (left + this.backgroundWidth) || mouseY >= (double) (top + this.backgroundHeight); + return this.recipeBook.isClickOutsideBounds(mouseX, mouseY, this.x, this.y, this.backgroundWidth, this.backgroundHeight, button) && bl; + } + + @Override + protected void onMouseClick(Slot slot, int slotId, int button, SlotActionType actionType) { + super.onMouseClick(slot, slotId, button, actionType); + this.recipeBook.slotClicked(slot); + } + + + static class DummyRecipeScreenHandler extends AbstractRecipeScreenHandler<SimpleInventory> { + + public DummyRecipeScreenHandler() { + super(ScreenHandlerType.GENERIC_9X6, -69); + } + + @Override + public void populateRecipeFinder(RecipeMatcher finder) {} + + @Override + public void clearCraftingSlots() {} + + @Override + public boolean matches(RecipeEntry<? extends Recipe<SimpleInventory>> recipe) { + return false; + } + + @Override + public int getCraftingResultSlotIndex() { + return 0; + } + + @Override + public int getCraftingWidth() { + return 0; + } + + @Override + public int getCraftingHeight() { + return 0; + } + + @Override + public int getCraftingSlotCount() { + return 0; + } + + @Override + public RecipeBookCategory getCategory() { + return RecipeBookCategory.CRAFTING; + } + + @Override + public boolean canInsertIntoSlot(int index) { + return false; + } + + @Override + public ItemStack quickMove(PlayerEntity player, int slot) { + return ItemStack.EMPTY; + } + + @Override + public boolean canUse(PlayerEntity player) { + return false; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreenHandler.java b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreenHandler.java new file mode 100644 index 00000000..04974ade --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockCraftingTableScreenHandler.java @@ -0,0 +1,69 @@ +package de.hysky.skyblocker.skyblock.item; + +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.Inventory; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.screen.slot.Slot; + +import java.util.Arrays; + +public class SkyblockCraftingTableScreenHandler extends GenericContainerScreenHandler { + + private static final int[] normalSlots = new int[]{ + 10, 11, 12, 16, + 19, 20, 21, 23, 25, + 28, 29, 30, 34 + }; + + public SkyblockCraftingTableScreenHandler(ScreenHandlerType<?> type, int syncId, PlayerInventory playerInventory, Inventory inventory, int rows) { + super(type, syncId, playerInventory, inventory, rows); + for (int i = 0; i < rows * 9; i++) { + Slot originalSlot = slots.get(i); + if (Arrays.binarySearch(normalSlots, i) >= 0) { + int[] coords = getCoords(i); + Slot slot = new Slot(originalSlot.inventory, originalSlot.getIndex(), coords[0], coords[1]); + slot.id = i; + slots.set(i, slot); + } else { + DisabledSlot slot = new DisabledSlot(originalSlot.inventory, originalSlot.getIndex(), originalSlot.x, originalSlot.y); + slot.id = i; + slots.set(i, slot); + } + } + int yOffset = (rows - 4) * 18 + 19; + for (int i = rows * 9; i < slots.size(); i++) { + Slot originalSlot = slots.get(i); + Slot slot = new Slot(originalSlot.inventory, originalSlot.getIndex(), originalSlot.x, originalSlot.y - yOffset); + slot.id = i; + slots.set(i, slot); + } + } + + public SkyblockCraftingTableScreenHandler(GenericContainerScreenHandler handler, PlayerInventory playerInventory) { + this(handler.getType(), handler.syncId, playerInventory, handler.getInventory(), handler.getRows()); + } + + private int[] getCoords(int slot) { + if (slot == 23) return new int[]{124, 35}; + if (slot == 16 || slot == 25 || slot == 34) { + int y = (slot / 9 - 1) * 18 + 8; + return new int[]{174, y}; + } + int gridX = slot % 9 - 1; + int gridY = slot / 9 - 1; + return new int[]{30 + gridX * 18, 17 + gridY * 18}; + } + + public static class DisabledSlot extends Slot { + + public DisabledSlot(Inventory inventory, int index, int x, int y) { + super(inventory, index, x, y); + } + + @Override + public boolean isEnabled() { + return false; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/AccessoriesHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/AccessoriesHelper.java new file mode 100644 index 00000000..69bc6f1c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/AccessoriesHelper.java @@ -0,0 +1,222 @@ +package de.hysky.skyblocker.skyblock.item.tooltip; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import java.util.function.ToIntFunction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.slf4j.Logger; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.mojang.util.UndashedUuid; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.Utils; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.slot.Slot; + +public class AccessoriesHelper { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Path FILE = SkyblockerMod.CONFIG_DIR.resolve("collected_accessories.json"); + private static final Pattern ACCESSORY_BAG_TITLE = Pattern.compile("Accessory Bag \\((?<page>\\d+)/\\d+\\)"); + //UUID -> Profile Id & Data + private static final Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, ProfileAccessoryData>> COLLECTED_ACCESSORIES = new Object2ObjectOpenHashMap<>(); + private static final Predicate<String> NON_EMPTY = s -> !s.isEmpty(); + private static final Predicate<Accessory> HAS_FAMILY = Accessory::hasFamily; + private static final ToIntFunction<Accessory> ACCESSORY_TIER = Accessory::tier; + + private static Map<String, Accessory> ACCESSORY_DATA = new Object2ObjectOpenHashMap<>(); + //remove?? + private static CompletableFuture<Void> loaded; + + public static void init() { + ClientLifecycleEvents.CLIENT_STARTED.register((_client) -> load()); + ClientLifecycleEvents.CLIENT_STOPPING.register((_client) -> save()); + ScreenEvents.BEFORE_INIT.register((_client, screen, _scaledWidth, _scaledHeight) -> { + if (Utils.isOnSkyblock() && TooltipInfoType.ACCESSORIES.isTooltipEnabled() && !Utils.getProfileId().isEmpty() && screen instanceof GenericContainerScreen genericContainerScreen) { + Matcher matcher = ACCESSORY_BAG_TITLE.matcher(genericContainerScreen.getTitle().getString()); + + if (matcher.matches()) { + ScreenEvents.afterTick(screen).register(_screen -> { + GenericContainerScreenHandler handler = genericContainerScreen.getScreenHandler(); + + collectAccessories(handler.slots.subList(0, handler.getRows() * 9), Integer.parseInt(matcher.group("page"))); + }); + } + } + }); + } + + //Note: JsonOps.COMPRESSED must be used if you're using maps with non-string keys + private static void load() { + loaded = CompletableFuture.runAsync(() -> { + try (BufferedReader reader = Files.newBufferedReader(FILE)) { + COLLECTED_ACCESSORIES.putAll(ProfileAccessoryData.SERIALIZATION_CODEC.parse(JsonOps.COMPRESSED, JsonParser.parseReader(reader)).result().orElseThrow()); + } catch (NoSuchFileException ignored) { + } catch (Exception e) { + LOGGER.error("[Skyblocker Accessory Helper] Failed to load accessory file!", e); + } + }); + } + + private static void save() { + try (BufferedWriter writer = Files.newBufferedWriter(FILE)) { + SkyblockerMod.GSON.toJson(ProfileAccessoryData.SERIALIZATION_CODEC.encodeStart(JsonOps.COMPRESSED, COLLECTED_ACCESSORIES).result().orElseThrow(), writer); + } catch (Exception e) { + LOGGER.error("[Skyblocker Accessory Helper] Failed to save accessory file!", e); + } + } + + private static void collectAccessories(List<Slot> slots, int page) { + //Is this even needed? + if (!loaded.isDone()) return; + + List<String> accessoryIds = slots.stream() + .map(Slot::getStack) + .map(ItemUtils::getItemId) + .filter(NON_EMPTY) + .toList(); + + String uuid = UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull()); + + COLLECTED_ACCESSORIES.computeIfAbsent(uuid, _uuid -> new Object2ObjectOpenHashMap<>()).computeIfAbsent(Utils.getProfileId(), profileId -> ProfileAccessoryData.createDefault()).pages() + .put(page, new ObjectOpenHashSet<>(accessoryIds)); + } + + static Pair<AccessoryReport, String> calculateReport4Accessory(String accessoryId) { + if (!ACCESSORY_DATA.containsKey(accessoryId) || Utils.getProfileId().isEmpty()) return Pair.of(AccessoryReport.INELIGIBLE, null); + + Accessory accessory = ACCESSORY_DATA.get(accessoryId); + String uuid = UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull()); + Set<Accessory> collectedAccessories = COLLECTED_ACCESSORIES.computeIfAbsent(uuid, _uuid -> new Object2ObjectOpenHashMap<>()).computeIfAbsent(Utils.getProfileId(), profileId -> ProfileAccessoryData.createDefault()).pages().values().stream() + .flatMap(ObjectOpenHashSet::stream) + .filter(ACCESSORY_DATA::containsKey) + .map(ACCESSORY_DATA::get) + .collect(Collectors.toSet()); + + //If the player has this accessory, and it doesn't belong to a family + if (collectedAccessories.contains(accessory) && accessory.family().isEmpty()) return Pair.of(AccessoryReport.HAS_HIGHEST_TIER, null); + + Predicate<Accessory> HAS_SAME_FAMILY = accessory::hasSameFamily; + Set<Accessory> collectedAccessoriesInTheSameFamily = collectedAccessories.stream() + .filter(HAS_FAMILY) + .filter(HAS_SAME_FAMILY) + .collect(Collectors.toSet()); + + //If the player doesn't have any collected accessories with same family + if (collectedAccessoriesInTheSameFamily.isEmpty()) return Pair.of(AccessoryReport.MISSING, null); + + Set<Accessory> accessoriesInTheSameFamily = ACCESSORY_DATA.values().stream() + .filter(HAS_FAMILY) + .filter(HAS_SAME_FAMILY) + .collect(Collectors.toSet()); + + ///If the player has the highest tier accessory in this family + //Take the accessories in the same family as {@code accessory}, then get the one with the highest tier + Optional<Accessory> highestTierOfFamily = accessoriesInTheSameFamily.stream() + .max(Comparator.comparingInt(ACCESSORY_TIER)); + int maxTierInFamily = highestTierOfFamily.orElse(Accessory.EMPTY).tier(); + + if (collectedAccessoriesInTheSameFamily.stream().anyMatch(ca -> ca.tier() == maxTierInFamily)) return Pair.of(AccessoryReport.HAS_HIGHEST_TIER, null); + + //If this accessory is a higher tier than all the other collected accessories in the same family + OptionalInt highestTierOfAllCollectedInFamily = collectedAccessoriesInTheSameFamily.stream() + .mapToInt(ACCESSORY_TIER) + .max(); + + if (accessory.tier() > highestTierOfAllCollectedInFamily.getAsInt()) return Pair.of(AccessoryReport.IS_GREATER_TIER, String.format("(%d→%d/%d)", highestTierOfAllCollectedInFamily.orElse(0), accessory.tier(), maxTierInFamily)); + + //If this accessory is a lower tier than one already obtained from same family + if (accessory.tier() < highestTierOfAllCollectedInFamily.getAsInt()) return Pair.of(AccessoryReport.OWNS_BETTER_TIER, String.format("(%d→%d/%d)", highestTierOfAllCollectedInFamily.orElse(0), accessory.tier(), maxTierInFamily)); + + //If there is an accessory in the same family that has a higher tier + //Take the accessories in the same family, then check if there is an accessory whose tier is greater than {@code accessory} + boolean hasGreaterTierInFamily = accessoriesInTheSameFamily.stream() + .anyMatch(ca -> ca.tier() > accessory.tier()); + + if (hasGreaterTierInFamily) return Pair.of(AccessoryReport.HAS_GREATER_TIER, String.format("(%d/%d)", highestTierOfAllCollectedInFamily.orElse(0), maxTierInFamily)); + + return Pair.of(AccessoryReport.MISSING, null); + } + + static void refreshData(JsonObject data) { + try { + ACCESSORY_DATA = Accessory.MAP_CODEC.parse(JsonOps.INSTANCE, data).result().orElseThrow(); + } catch (Exception e) { + LOGGER.error("[Skyblocker Accessory Helper] Failed to parse data!", e); + } + } + + private record ProfileAccessoryData(Int2ObjectOpenHashMap<ObjectOpenHashSet<String>> pages) { + private static final Codec<ProfileAccessoryData> CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.unboundedMap(Codec.INT, Codec.STRING.listOf().xmap(ObjectOpenHashSet::new, ObjectArrayList::new)) + .xmap(Int2ObjectOpenHashMap::new, Int2ObjectOpenHashMap::new).fieldOf("pages").forGetter(ProfileAccessoryData::pages)) + .apply(instance, ProfileAccessoryData::new)); + private static final Codec<Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, ProfileAccessoryData>>> SERIALIZATION_CODEC = Codec.unboundedMap(Codec.STRING, Codec.unboundedMap(Codec.STRING, CODEC) + .xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new)) + .xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new); + + private static ProfileAccessoryData createDefault() { + return new ProfileAccessoryData(new Int2ObjectOpenHashMap<>()); + } + } + + /** + * @author AzureAaron + * @implSpec <a href="https://github.com/AzureAaron/aaron-mod/blob/1.20/src/main/java/net/azureaaron/mod/commands/MagicalPowerCommand.java#L475">Aaron's Mod</a> + */ + private record Accessory(String id, Optional<String> family, int tier) { + private static final Codec<Accessory> CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("id").forGetter(Accessory::id), + Codec.STRING.optionalFieldOf("family").forGetter(Accessory::family), + Codec.INT.optionalFieldOf("tier", 0).forGetter(Accessory::tier)) + .apply(instance, Accessory::new)); + private static final Codec<Map<String, Accessory>> MAP_CODEC = Codec.unboundedMap(Codec.STRING, CODEC); + private static final Accessory EMPTY = new Accessory("", Optional.empty(), 0); + + private boolean hasFamily() { + return family.isPresent(); + } + + private boolean hasSameFamily(Accessory other) { + return other.family().equals(this.family); + } + } + + enum AccessoryReport { + HAS_HIGHEST_TIER, //You've collected the highest tier - Collected + IS_GREATER_TIER, //This accessory is an upgrade from the one in the same family that you already have - Upgrade -- Shows you what tier this accessory is in its family + HAS_GREATER_TIER, //This accessory has a higher tier upgrade - Upgradable -- Shows you the highest tier accessory you've collected in that family + OWNS_BETTER_TIER, //You've collected an accessory in this family with a higher tier - Downgrade -- Shows you the highest tier accessory you've collected in that family + MISSING, //You don't have any accessories in this family - Missing + INELIGIBLE + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java index e8b6ebc8..62c50735 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java @@ -5,10 +5,12 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.item.MuseumItemCache; +import de.hysky.skyblocker.skyblock.item.tooltip.AccessoriesHelper.AccessoryReport; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.scheduler.Scheduler; +import it.unimi.dsi.fastutil.Pair; import net.minecraft.client.MinecraftClient; import net.minecraft.client.item.TooltipContext; import net.minecraft.item.DyeableItem; @@ -239,6 +241,27 @@ public class ItemTooltip { } } } + + if (TooltipInfoType.ACCESSORIES.isTooltipEnabledAndHasOrNullWarning(internalID)) { + Pair<AccessoryReport, String> report = AccessoriesHelper.calculateReport4Accessory(internalID); + + if (report.left() != AccessoryReport.INELIGIBLE) { + MutableText title = Text.literal(String.format("%-19s", "Accessory: ")).withColor(0xf57542); + + Text stateText = switch (report.left()) { + case HAS_HIGHEST_TIER -> Text.literal("✔ Collected").formatted(Formatting.GREEN); + case IS_GREATER_TIER -> Text.literal("✦ Upgrade ").withColor(0x218bff).append(Text.literal(report.right()).withColor(0xf8f8ff)); + case HAS_GREATER_TIER -> Text.literal("↑ Upgradable ").withColor(0xf8d048).append(Text.literal(report.right()).withColor(0xf8f8ff)); + case OWNS_BETTER_TIER -> Text.literal("↓ Downgrade ").formatted(Formatting.GRAY).append(Text.literal(report.right()).withColor(0xf8f8ff)); + case MISSING -> Text.literal("✖ Missing").formatted(Formatting.RED); + + //Should never be the case + default -> Text.literal("? Unknown").formatted(Formatting.GRAY); + }; + + lines.add(title.append(stateText)); + } + } } private static void addExoticTooltip(List<Text> lines, String internalID, NbtCompound nbt, String colorHex, String expectedHex, String existingTooltip) { @@ -391,6 +414,7 @@ public class ItemTooltip { TooltipInfoType.MOTES.downloadIfEnabled(futureList); TooltipInfoType.MUSEUM.downloadIfEnabled(futureList); TooltipInfoType.COLOR.downloadIfEnabled(futureList); + TooltipInfoType.ACCESSORIES.downloadIfEnabled(futureList); CompletableFuture.allOf(futureList.toArray(CompletableFuture[]::new)).exceptionally(e -> { LOGGER.error("Encountered unknown error while downloading tooltip data", e); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java index 4aba040d..6edee8d6 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java @@ -6,13 +6,15 @@ import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.utils.Http; import de.hysky.skyblocker.utils.Utils; -import org.jetbrains.annotations.Nullable; import java.net.http.HttpHeaders; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; import java.util.function.Predicate; +import org.jetbrains.annotations.Nullable; + public enum TooltipInfoType implements Runnable { NPC("https://hysky.de/api/npcprice", itemTooltip -> itemTooltip.enableNPCPrice, true), BAZAAR("https://hysky.de/api/bazaar", itemTooltip -> itemTooltip.enableBazaarPrice || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.croesusProfit || SkyblockerConfigManager.get().general.chestValue.enableChestValue, itemTooltip -> itemTooltip.enableBazaarPrice, false), @@ -22,7 +24,8 @@ public enum TooltipInfoType implements Runnable { MOTES("https://hysky.de/api/motesprice", itemTooltip -> itemTooltip.enableMotesPrice, itemTooltip -> itemTooltip.enableMotesPrice && Utils.isInTheRift(), true), OBTAINED(itemTooltip -> itemTooltip.enableObtainedDate), MUSEUM("https://hysky.de/api/museum", itemTooltip -> itemTooltip.enableMuseumInfo, true), - COLOR("https://hysky.de/api/color", itemTooltip -> itemTooltip.enableExoticTooltip, true); + COLOR("https://hysky.de/api/color", itemTooltip -> itemTooltip.enableExoticTooltip, true), + ACCESSORIES("https://hysky.de/api/accessories", itemTooltip -> itemTooltip.enableAccessoriesHelper, true, AccessoriesHelper::refreshData); private final String address; private final Predicate<SkyblockerConfig.ItemTooltip> dataEnabled; @@ -30,12 +33,23 @@ public enum TooltipInfoType implements Runnable { private JsonObject data; private final boolean cacheable; private long hash; + private final Consumer<JsonObject> callback; /** * Use this for when you're adding tooltip info that has no data associated with it */ TooltipInfoType(Predicate<SkyblockerConfig.ItemTooltip> enabled) { - this(null, itemTooltip -> false, enabled, null, false); + this(null, itemTooltip -> false, enabled, false, null); + } + + /** + * @param address the address to download the data from + * @param enabled the predicate to check if the data should be downloaded and the tooltip should be shown + * @param cacheable whether the data should be cached + * @param callback called when the {@code data} is refreshed + */ + TooltipInfoType(String address, Predicate<SkyblockerConfig.ItemTooltip> enabled, boolean cacheable, Consumer<JsonObject> callback) { + this(address, enabled, enabled, cacheable, callback); } /** @@ -44,7 +58,7 @@ public enum TooltipInfoType implements Runnable { * @param cacheable whether the data should be cached */ TooltipInfoType(String address, Predicate<SkyblockerConfig.ItemTooltip> enabled, boolean cacheable) { - this(address, enabled, enabled, null, cacheable); + this(address, enabled, enabled, cacheable, null); } /** @@ -54,7 +68,7 @@ public enum TooltipInfoType implements Runnable { * @param cacheable whether the data should be cached */ TooltipInfoType(String address, Predicate<SkyblockerConfig.ItemTooltip> dataEnabled, Predicate<SkyblockerConfig.ItemTooltip> tooltipEnabled, boolean cacheable) { - this(address, dataEnabled, tooltipEnabled, null, cacheable); + this(address, dataEnabled, tooltipEnabled, cacheable, null); } /** @@ -64,12 +78,13 @@ public enum TooltipInfoType implements Runnable { * @param data the data * @param cacheable whether the data should be cached */ - TooltipInfoType(String address, Predicate<SkyblockerConfig.ItemTooltip> dataEnabled, Predicate<SkyblockerConfig.ItemTooltip> tooltipEnabled, @Nullable JsonObject data, boolean cacheable) { + TooltipInfoType(String address, Predicate<SkyblockerConfig.ItemTooltip> dataEnabled, Predicate<SkyblockerConfig.ItemTooltip> tooltipEnabled, boolean cacheable, @Nullable Consumer<JsonObject> callback) { this.address = address; this.dataEnabled = dataEnabled; this.tooltipEnabled = tooltipEnabled; - this.data = data; + this.data = null; this.cacheable = cacheable; + this.callback = callback; } /** @@ -146,6 +161,8 @@ public enum TooltipInfoType implements Runnable { else this.hash = hash; } data = SkyblockerMod.GSON.fromJson(Http.sendGetRequest(address), JsonObject.class); + + if (callback != null) callback.accept(data); } catch (Exception e) { ItemTooltip.LOGGER.warn("[Skyblocker] Failed to download " + this + " prices!", e); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java new file mode 100644 index 00000000..93c6b3f4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java @@ -0,0 +1,444 @@ +package de.hysky.skyblocker.skyblock.waypoint; + +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import static com.mojang.brigadier.arguments.StringArgumentType.word; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Semaphore; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import org.slf4j.Logger; + +import com.google.common.primitives.Floats; +import com.google.gson.Gson; +import com.google.gson.JsonParser; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.skyblock.item.CustomArmorDyeColors; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.waypoint.Waypoint; +import it.unimi.dsi.fastutil.floats.FloatArrayList; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +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.client.network.ClientPlayerEntity; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.command.CommandSource; +import net.minecraft.command.argument.BlockPosArgumentType; +import net.minecraft.command.argument.PosArgument; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +public class OrderedWaypoints { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Codec<Map<String, OrderedWaypointGroup>> SERIALIZATION_CODEC = Codec.unboundedMap(Codec.STRING, OrderedWaypointGroup.CODEC).xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new); + private static final String PREFIX = "[Skyblocker::OrderedWaypoints::v1]"; + private static final Path PATH = SkyblockerMod.CONFIG_DIR.resolve("ordered_waypoints.json"); + private static final Map<String, OrderedWaypointGroup> WAYPOINTS = new Object2ObjectOpenHashMap<>(); + private static final Semaphore SEMAPHORE = new Semaphore(1); + private static final Object2IntOpenHashMap<String> INDEX_STORE = new Object2IntOpenHashMap<>(); + private static final int RADIUS = 2; + private static final float[] LIGHT_GRAY = { 192 / 255f, 192 / 255f, 192 / 255f }; + + private static CompletableFuture<Void> loaded; + + public static void init() { + ClientLifecycleEvents.CLIENT_STARTED.register(_client -> load()); + ClientLifecycleEvents.CLIENT_STOPPING.register(_client -> save()); + ClientCommandRegistrationCallback.EVENT.register(OrderedWaypoints::registerCommands); + WorldRenderEvents.AFTER_TRANSLUCENT.register(OrderedWaypoints::render); + } + + private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(literal(SkyblockerMod.NAMESPACE) + .then(literal("waypoints") + .then(literal("ordered") + .then(literal("add") + .then(argument("groupName", word()) + .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) + .then(argument("pos", BlockPosArgumentType.blockPos()) + .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), Integer.MIN_VALUE, null)) + .then(argument("hex", word()) + .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), Integer.MIN_VALUE, getString(context, "hex"))))))) + .then(literal("addAt") + .then(argument("groupName", word()) + .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) + .then(argument("index", IntegerArgumentType.integer(0)) + .then(argument("pos", BlockPosArgumentType.blockPos()) + .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), IntegerArgumentType.getInteger(context, "index"), null)) + .then(argument("hex", word()) + .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), IntegerArgumentType.getInteger(context, "index"), getString(context, "hex")))))))) + .then(literal("remove") + .then(argument("groupName", word()) + .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) + .executes(context -> removeWaypointGroup(context.getSource(), getString(context, "groupName"))) + .then(argument("pos", BlockPosArgumentType.blockPos()) + .executes(context -> removeWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), Integer.MIN_VALUE))))) + .then(literal("removeAt") + .then(argument("groupName", word()) + .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) + .then(argument("index", IntegerArgumentType.integer(0)) + .executes(context -> removeWaypoint(context.getSource(), getString(context, "groupName"), null, IntegerArgumentType.getInteger(context, "index")))))) + .then(literal("toggle") + .then(argument("groupName", word()) + .suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder)) + .executes(context -> toggleGroup(context.getSource(), getString(context, "groupName"))))) + .then(literal("import") + .then(literal("coleWeight") + .then(argument("groupName", word()) + .executes(context -> fromColeWeightFormat(context.getSource(), getString(context, "groupName"))))) + .then(literal("skyblocker") + .executes(context -> fromSkyblockerFormat(context.getSource())))) + .then(literal("export") + .executes(context -> export(context.getSource())))))); + } + + private static int addWaypoint(FabricClientCommandSource source, String groupName, PosArgument posArgument, int index, String hex) { + BlockPos pos = posArgument.toAbsoluteBlockPos(new ServerCommandSource(null, source.getPosition(), source.getRotation(), null, 0, null, null, null, null)); + + SEMAPHORE.acquireUninterruptibly(); + + if (hex != null && !CustomArmorDyeColors.isHexadecimalColor(hex)) { + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.add.invalidHexColor"))); + SEMAPHORE.release(); + + return Command.SINGLE_SUCCESS; + } + + int rgb = hex != null ? Integer.decode("0x" + hex.replace("#", "")) : Integer.MIN_VALUE; + float[] colorComponents = rgb != Integer.MIN_VALUE ? new float[] { ((rgb >> 16) & 0xFF) / 255f, ((rgb >> 8) & 0xFF) / 255f, (rgb & 0xFF) / 255f } : new float[0]; + + OrderedWaypointGroup group = WAYPOINTS.computeIfAbsent(groupName, name -> new OrderedWaypointGroup(name, true, new ObjectArrayList<>())); + OrderedWaypoint waypoint = new OrderedWaypoint(pos, colorComponents); + + if (index != Integer.MIN_VALUE) { + int indexToAddAt = MathHelper.clamp(index, 0, group.waypoints().size()); + + group.waypoints().add(indexToAddAt, waypoint); + INDEX_STORE.removeInt(group.name()); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.addAt.success", group.name(), indexToAddAt))); + } else { + group.waypoints().add(waypoint); + INDEX_STORE.removeInt(group.name()); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.add.success", group.name(), pos.toShortString()))); + } + + SEMAPHORE.release(); + + return Command.SINGLE_SUCCESS; + } + + private static int removeWaypointGroup(FabricClientCommandSource source, String groupName) { + if (WAYPOINTS.containsKey(groupName)) { + SEMAPHORE.acquireUninterruptibly(); + WAYPOINTS.remove(groupName); + INDEX_STORE.removeInt(groupName); + SEMAPHORE.release(); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.removeGroup.success", groupName))); + } else { + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.groupNonExistent", groupName))); + } + + return Command.SINGLE_SUCCESS; + } + + private static int removeWaypoint(FabricClientCommandSource source, String groupName, PosArgument posArgument, int index) { + if (WAYPOINTS.containsKey(groupName)) { + SEMAPHORE.acquireUninterruptibly(); + OrderedWaypointGroup group = WAYPOINTS.get(groupName); + + if (posArgument != null) { + BlockPos pos = posArgument.toAbsoluteBlockPos(new ServerCommandSource(null, source.getPosition(), source.getRotation(), null, 0, null, null, null, null)); + + group.waypoints().removeIf(waypoint -> waypoint.getPos().equals(pos)); + INDEX_STORE.removeInt(group.name()); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.remove.success", pos.toShortString(), group.name()))); + } + + if (index != Integer.MIN_VALUE) { + int indexToRemove = MathHelper.clamp(index, 0, group.waypoints().size() - 1); + + group.waypoints().remove(indexToRemove); + INDEX_STORE.removeInt(group.name()); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.removeAt.success", indexToRemove, group.name()))); + } + + SEMAPHORE.release(); + } else { + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.groupNonExistent", groupName))); + } + + return Command.SINGLE_SUCCESS; + } + + private static int toggleGroup(FabricClientCommandSource source, String groupName) { + if (WAYPOINTS.containsKey(groupName)) { + SEMAPHORE.acquireUninterruptibly(); + WAYPOINTS.put(groupName, WAYPOINTS.get(groupName).toggle()); + SEMAPHORE.release(); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.toggle.success", groupName))); + } else { + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.groupNonExistent", groupName))); + } + + return Command.SINGLE_SUCCESS; + } + + private static void render(WorldRenderContext wrc) { + if ((Utils.isInCrystalHollows() || Utils.isInDwarvenMines()) && loaded.isDone() && SEMAPHORE.tryAcquire()) { + for (OrderedWaypointGroup group : WAYPOINTS.values()) { + if (group.enabled()) { + List<OrderedWaypoint> waypoints = group.waypoints(); + ClientPlayerEntity player = MinecraftClient.getInstance().player; + int centreIndex = INDEX_STORE.computeIfAbsent(group.name(), name -> 0); + + for (int i = 0; i < waypoints.size(); i++) { + OrderedWaypoint waypoint = waypoints.get(i); + + if (waypoint.getPos().isWithinDistance(player.getPos(), RADIUS)) { + centreIndex = i; + INDEX_STORE.put(group.name(), i); + + break; + } + } + + int previousIndex = (centreIndex - 1 + waypoints.size()) % waypoints.size(); + int currentIndex = (centreIndex + waypoints.size()) % waypoints.size(); + int nextIndex = (centreIndex + 1) % waypoints.size(); + + OrderedWaypoint previous = waypoints.get(previousIndex); + OrderedWaypoint current = waypoints.get(currentIndex); + OrderedWaypoint next = waypoints.get(nextIndex); + + previous.render(wrc, RelativeIndex.PREVIOUS, previousIndex); + current.render(wrc, RelativeIndex.CURRENT, currentIndex); + next.render(wrc, RelativeIndex.NEXT, nextIndex); + + RenderHelper.renderLineFromCursor(wrc, Vec3d.ofCenter(next.getPos().up()), LIGHT_GRAY, 1f, 5f); + } + } + + SEMAPHORE.release(); + } + } + + private static void load() { + loaded = CompletableFuture.runAsync(() -> { + try (BufferedReader reader = Files.newBufferedReader(PATH)) { + WAYPOINTS.putAll(SERIALIZATION_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).result().orElseThrow()); + } catch (NoSuchFileException ignored) { + } catch (Exception e) { + LOGGER.error("[Skyblocker Ordered Waypoints] Failed to load the waypoints! :(", e); + } + }); + } + + private static void save() { + try (BufferedWriter writer = Files.newBufferedWriter(PATH)) { + SkyblockerMod.GSON.toJson(SERIALIZATION_CODEC.encodeStart(JsonOps.INSTANCE, WAYPOINTS).result().orElseThrow(), writer); + } catch (Exception e) { + LOGGER.error("[Skyblocker Ordered Waypoints] Failed to save the waypoints! :(", e); + } + } + + private static int export(FabricClientCommandSource source) { + try { + String json = new Gson().toJson(SERIALIZATION_CODEC.encodeStart(JsonOps.INSTANCE, WAYPOINTS).result().orElseThrow()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(out); + + gzip.write(json.getBytes()); + gzip.close(); + + String encoded = new String(Base64.getEncoder().encode(out.toByteArray())); + String exportCode = PREFIX + encoded; + + MinecraftClient.getInstance().keyboard.setClipboard(exportCode); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.export.success"))); + } catch (Exception e) { + LOGGER.error("[Skyblocker Ordered Waypoints] Failed to export waypoints!", e); + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.export.fail"))); + } + + return Command.SINGLE_SUCCESS; + } + + //TODO in future handle for when the group names clash? + private static int fromSkyblockerFormat(FabricClientCommandSource source) { + try { + String importCode = MinecraftClient.getInstance().keyboard.getClipboard(); + + if (importCode.startsWith(PREFIX)) { + String encoded = importCode.replace(PREFIX, ""); + byte[] decoded = Base64.getDecoder().decode(encoded); + + String json = new String(new GZIPInputStream(new ByteArrayInputStream(decoded)).readAllBytes()); + Map<String, OrderedWaypointGroup> importedWaypoints = SERIALIZATION_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseString(json)).result().orElseThrow(); + + SEMAPHORE.acquireUninterruptibly(); + WAYPOINTS.putAll(importedWaypoints); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.import.skyblocker.success"))); + SEMAPHORE.release(); + } else { + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.import.skyblocker.unknownFormatHeader"))); + } + } catch (Exception e) { + LOGGER.error("[Skyblocker Ordered Waypoints] Failed to import waypoints!", e); + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.import.skyblocker.fail"))); + } + + return Command.SINGLE_SUCCESS; + } + + private static int fromColeWeightFormat(FabricClientCommandSource source, String groupName) { + try { + if (WAYPOINTS.containsKey(groupName)) { + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.import.coleWeight.groupAlreadyExists", groupName))); + + return Command.SINGLE_SUCCESS; + } + + String json = MinecraftClient.getInstance().keyboard.getClipboard(); + List<ColeWeightWaypoint> coleWeightWaypoints = ColeWeightWaypoint.LIST_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseString(json)).result().orElseThrow(); + ObjectArrayList<OrderedWaypoint> convertedWaypoints = new ObjectArrayList<>(); + + for (ColeWeightWaypoint waypoint : coleWeightWaypoints) { + if (waypoint.x().isPresent() && waypoint.y().isPresent() && waypoint.z().isPresent()) { + //I think Cole Weight ignores the colors and overrides them so we will comment this out + //float[] colorComponents = (waypoint.r().isPresent() && waypoint.g().isPresent() && waypoint.b().isPresent()) ? new float[] { waypoint.r().get() / 255f, waypoint.g().get() / 255f, waypoint.b().get() / 255f } : new float[0]; + + convertedWaypoints.add(new OrderedWaypoint(new BlockPos(waypoint.x().get(), waypoint.y().get(), waypoint.z().get()), new float[0])); + } + } + + SEMAPHORE.acquireUninterruptibly(); + WAYPOINTS.put(groupName, new OrderedWaypointGroup(groupName, true, convertedWaypoints)); + source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.import.coleWeight.success"))); + SEMAPHORE.release(); + } catch (Exception e) { + LOGGER.error("[Skyblocker Ordered Waypoints] Failed to import waypoints from the Cole Weight format!", e); + source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.import.coleWeight.fail"))); + } + + return Command.SINGLE_SUCCESS; + } + + private record OrderedWaypointGroup(String name, boolean enabled, ObjectArrayList<OrderedWaypoint> waypoints) { + static final Codec<OrderedWaypointGroup> CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("name").forGetter(OrderedWaypointGroup::name), + Codec.BOOL.fieldOf("enabled").forGetter(OrderedWaypointGroup::enabled), + OrderedWaypoint.LIST_CODEC.fieldOf("waypoints").xmap(ObjectArrayList::new, ObjectArrayList::new).forGetter(OrderedWaypointGroup::waypoints)) + .apply(instance, OrderedWaypointGroup::new)); + + OrderedWaypointGroup toggle() { + return new OrderedWaypointGroup(name, !enabled, waypoints); + } + } + + private static class OrderedWaypoint extends Waypoint { + static final Codec<OrderedWaypoint> CODEC = RecordCodecBuilder.create(instance -> instance.group( + BlockPos.CODEC.fieldOf("pos").forGetter(OrderedWaypoint::getPos), + Codec.floatRange(0, 1).listOf().xmap(Floats::toArray, FloatArrayList::new).optionalFieldOf("colorComponents", new float[0]).forGetter(inst -> inst.colorComponents.length == 3 ? inst.colorComponents : new float[0])) + .apply(instance, OrderedWaypoint::new)); + static final Codec<List<OrderedWaypoint>> LIST_CODEC = CODEC.listOf(); + static final float[] RED = { 1f, 0f, 0f }; + static final float[] WHITE = { 1f, 1f, 1f }; + static final float[] GREEN = { 0f, 1f, 0f }; + + private RelativeIndex relativeIndex; + private int waypointIndex; + + OrderedWaypoint(BlockPos pos, float[] colorComponents) { + super(pos, Type.WAYPOINT, colorComponents); + } + + private BlockPos getPos() { + return this.pos; + } + + @Override + protected float[] getColorComponents() { + if (this.colorComponents.length != 3) { + return switch (this.relativeIndex) { + case PREVIOUS -> RED; + case CURRENT -> WHITE; + case NEXT -> GREEN; + }; + } + + return this.colorComponents; + } + + private void render(WorldRenderContext context, RelativeIndex relativeIndex, int waypointIndex) { + this.relativeIndex = relativeIndex; + this.waypointIndex = waypointIndex; + + render(context); + } + + @Override + public void render(WorldRenderContext context) { + super.render(context); + RenderHelper.renderText(context, Text.of(String.valueOf(waypointIndex)), Vec3d.ofCenter(pos.up(2)), true); + } + } + + private record ColeWeightWaypoint(Optional<Integer> x, Optional<Integer> y, Optional<Integer> z, Optional<Integer> r, Optional<Integer> g, Optional<Integer> b, Optional<Options> options) { + static final Codec<ColeWeightWaypoint> CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.INT.optionalFieldOf("x").forGetter(ColeWeightWaypoint::x), + Codec.INT.optionalFieldOf("y").forGetter(ColeWeightWaypoint::y), + Codec.INT.optionalFieldOf("z").forGetter(ColeWeightWaypoint::z), + Codec.INT.optionalFieldOf("r").forGetter(ColeWeightWaypoint::r), + Codec.INT.optionalFieldOf("g").forGetter(ColeWeightWaypoint::g), + Codec.INT.optionalFieldOf("b").forGetter(ColeWeightWaypoint::b), + Options.CODEC.optionalFieldOf("options").forGetter(ColeWeightWaypoint::options)) + .apply(instance, ColeWeightWaypoint::new)); + static final Codec<List<ColeWeightWaypoint>> LIST_CODEC = CODEC.listOf(); + + //Even though we don't import the name this is still here incase that eventually changes + record Options(Optional<String> name) { + static final Codec<Options> CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.optionalFieldOf("name").forGetter(Options::name)) + .apply(instance, Options::new)); + } + } + + private enum RelativeIndex { + PREVIOUS, + CURRENT, + NEXT + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/InstancedUtils.java b/src/main/java/de/hysky/skyblocker/utils/InstancedUtils.java new file mode 100644 index 00000000..1da4fa11 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/InstancedUtils.java @@ -0,0 +1,104 @@ +package de.hysky.skyblocker.utils; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.runtime.ObjectMethods; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +import org.slf4j.Logger; + +import com.mojang.logging.LogUtils; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; + +/** + * @implNote If implementing any of these onto a class, ensure that all subclasses have an implementation of the methods too. + */ +public class InstancedUtils { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Map<Class<?>, MethodHandle> EQUALS_CACHE = new ConcurrentHashMap<>(); + private static final Map<Class<?>, MethodHandle> HASH_CODE_CACHE = new ConcurrentHashMap<>(); + private static final Map<Class<?>, MethodHandle> TO_STRING_CACHE = new ConcurrentHashMap<>(); + + public static MethodHandle equals(Class<?> type) { + if (EQUALS_CACHE.containsKey(type)) return EQUALS_CACHE.get(type); + + try { + Field[] fields = getClassFields(type); + MethodHandle[] getters = getFieldGetters(fields); + + //The field names param can be anything as equals and hashCode don't care about it. + MethodHandle equalsHandle = (MethodHandle) ObjectMethods.bootstrap(MethodHandles.lookup(), "equals", MethodHandle.class, type, "", getters); + + EQUALS_CACHE.put(type, equalsHandle); + + return equalsHandle; + } catch (Throwable t) { + LOGGER.error("[Skyblocked Instanced Utils] Failed to create an equals method handle.", t); + + throw new RuntimeException(); + } + } + + public static MethodHandle hashCode(Class<?> type) { + if (HASH_CODE_CACHE.containsKey(type)) return HASH_CODE_CACHE.get(type); + + try { + Field[] fields = getClassFields(type); + MethodHandle[] getters = getFieldGetters(fields); + + //The field names param can be anything as equals and hashCode don't care about it. + MethodHandle hashCodeHandle = (MethodHandle) ObjectMethods.bootstrap(MethodHandles.lookup(), "hashCode", MethodHandle.class, type, "", getters); + + HASH_CODE_CACHE.put(type, hashCodeHandle); + + return hashCodeHandle; + } catch (Throwable t) { + LOGGER.error("[Skyblocked Instanced Utils] Failed to create a hashCode method handle.", t); + + throw new RuntimeException(); + } + } + + public static MethodHandle toString(Class<?> type) { + if (TO_STRING_CACHE.containsKey(type)) return TO_STRING_CACHE.get(type); + + try { + Field[] fields = getClassFields(type); + MethodHandle[] getters = getFieldGetters(fields); + String fieldNames = String.join(";", Arrays.stream(fields).map(Field::getName).toArray(String[]::new)); + + MethodHandle toStringHandle = (MethodHandle) ObjectMethods.bootstrap(MethodHandles.lookup(), "toString", MethodHandle.class, type, fieldNames, getters); + + TO_STRING_CACHE.put(type, toStringHandle); + + return toStringHandle; + } catch (Throwable t) { + LOGGER.error("[Skyblocked Instanced Utils] Failed to create a toString method handle.", t); + + throw new RuntimeException(); + } + } + + private static Field[] getClassFields(Class<?> type) { + return Stream.concat(Arrays.stream(type.getDeclaredFields()), Arrays.stream(type.getFields())).distinct().toArray(Field[]::new); + } + + private static MethodHandle[] getFieldGetters(Field[] fields) throws Throwable { + ObjectOpenHashSet<MethodHandle> handles = new ObjectOpenHashSet<>(); + + for (Field field : fields) { + field.setAccessible(true); + + MethodHandle getter = MethodHandles.lookup().unreflectGetter(field); + + handles.add(getter); + } + + return handles.toArray(MethodHandle[]::new); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java index 05514d02..e39b5364 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java @@ -187,6 +187,56 @@ public class RenderHelper { RenderSystem.depthFunc(GL11.GL_LEQUAL); } + public static void renderLineFromCursor(WorldRenderContext context, Vec3d point, float[] colorComponents, float alpha, float lineWidth) { + Vec3d camera = context.camera().getPos(); + MatrixStack matrices = context.matrixStack(); + + matrices.push(); + matrices.translate(-camera.x, -camera.y, -camera.z); + + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + Matrix4f positionMatrix = matrices.peek().getPositionMatrix(); + + GL11.glEnable(GL11.GL_LINE_SMOOTH); + GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST); + + RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram); + RenderSystem.setShaderColor(1f, 1f, 1f, 1f); + RenderSystem.lineWidth(lineWidth); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.disableCull(); + RenderSystem.enableDepthTest(); + RenderSystem.depthFunc(GL11.GL_ALWAYS); + + Vec3d offset = Vec3d.fromPolar(context.camera().getPitch(), context.camera().getYaw()); + Vec3d cameraPoint = camera.add(offset); + + buffer.begin(DrawMode.LINES, VertexFormats.LINES); + Vector3f normal = new Vector3f((float) offset.x, (float) offset.y, (float) offset.z); + buffer + .vertex(positionMatrix, (float) cameraPoint.x , (float) cameraPoint.y, (float) cameraPoint.z) + .color(colorComponents[0], colorComponents[1], colorComponents[2], alpha) + .normal(normal.x, normal.y, normal.z) + .next(); + + buffer + .vertex(positionMatrix, (float) point.getX(), (float) point.getY(), (float) point.getZ()) + .color(colorComponents[0], colorComponents[1], colorComponents[2], alpha) + .normal(normal.x, normal.y, normal.z) + .next(); + + + tessellator.draw(); + + matrices.pop(); + GL11.glDisable(GL11.GL_LINE_SMOOTH); + RenderSystem.lineWidth(1f); + RenderSystem.enableCull(); + RenderSystem.depthFunc(GL11.GL_LEQUAL); + } + public static void renderQuad(WorldRenderContext context, Vec3d[] points, float[] colorComponents, float alpha, boolean throughWalls) { Vec3d camera = context.camera().getPos(); MatrixStack matrices = context.matrixStack(); 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 7f3d4eda..622e1658 100644 --- a/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java +++ b/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java @@ -14,7 +14,7 @@ public class Waypoint implements Renderable { public final BlockPos pos; final Box box; final Supplier<Type> typeSupplier; - final float[] colorComponents; + protected final float[] colorComponents; final float alpha; final float lineWidth; final boolean throughWalls; diff --git a/src/main/resources/assets/skyblocker/dungeons/catacombs/1x1/temple-3.skeleton b/src/main/resources/assets/skyblocker/dungeons/catacombs/1x1/temple-3.skeleton Binary files differindex be716268..9e7d3e45 100644 --- a/src/main/resources/assets/skyblocker/dungeons/catacombs/1x1/temple-3.skeleton +++ b/src/main/resources/assets/skyblocker/dungeons/catacombs/1x1/temple-3.skeleton diff --git a/src/main/resources/assets/skyblocker/dungeons/catacombs/1x1/zodd-1.skeleton b/src/main/resources/assets/skyblocker/dungeons/catacombs/1x1/zodd-1.skeleton new file mode 100644 index 00000000..280dd405 --- /dev/null +++ b/src/main/resources/assets/skyblocker/dungeons/catacombs/1x1/zodd-1.skeleton @@ -0,0 +1,4 @@ +xI쳲*ˬzEѨ@hGo3<n<uǃ787K|}_<5O?gOo'~O}gzzWz_FoNAIE_M~C/O> ;`v;`v;`v;`O>艹^^~;}'}}7MC?y$dCv!;dCv!;dCv!;dCv;bG#vĎ;bG#vĎ;bG#vĎ;bG#vĎ1;fcv̎1;fcw}߱;fcv̎1;fcvN ;a'석vN ;a'석vN ;a'석vN ;a'석{^{^{^Ň7Oœ{g`/`/`/d/Kd/Kd/KxKޒ-yKޒ-yKޒ-y+ފx+ފx+ފx+ʵ\ߊx+ފx+ފx+ފykޚykޚykޚ]ykޚykޚykޚyކmxކmxކmxކmxކmxކmxކmx[ޖmy[ޖmy[ޖmy[zvY햽eo[eo[eo[c;c;c;c[b[c;c;c;c{g{g{g{g{g{g{g{SvN);e씝SvN);e씝SvN);e씝SvN);e쌝3v;cg쌝3v;cg쌝3v;cg쌝3v;c윝sv9;g윝sv9;g윝sv9;g윝sv9`]v.`]v.`]v.`]v.%d]Kv.%d]Kv.%d]Kv.%d]Kv>>>>>>>>bW]+vŮbW]+vŮbW]+vŮbW]+vŮ5f]kvͮ5f]kvͮ5f]kvͮ5f]kv>#>#>#>#>#>#>#>#>O>O>O>O>O>O>O>O3>3>3>3>3>3>3>3>vn
a7ݰvn
a7ݰvn
a7ݰvn
a7ݲ[vn-eݲ[vn-eݲ[vn-eݲ[vn-e¾/¾/¾/¾/¾/¾/¾/¾;vcwݱ;vcwݱ;vcwݱ;vcwݳ{v=gݳ{v=gݳ{v=gݳ{v=g={`={`={`={`_W}e_W}e_W}e_W}e_W}e_W}e_W}e_W}c7}c7}c7}c7}c7}c7}c7}c7}gw}gw}gw}gw}gw}gw}gw}g#{d=G#{d=G#{d=G#{d=GȞ{bO='Ğ{bO='Ğ{bO='Ğ{bO='̞3{f=g̞3{f=g̞3{f=g̞3{f=vy8ᜇsy8ᜇsy8ӧ>yߧzzzEoV^QY_U7?Ӏv;`v;`v;`v;`Ꙟ^^[{}G}g}W}ӯПK?S<}Cv!;dCv!;dCv!;dCv!;d#vĎ;bG#vĎ;bG#vĎ;bG#vĎ;bcv̎1;fcv̎}߱;}Ǿcv̎1;fcv̎1;a'석vN ;a'석vN ;a'석vN ;a'석vN`/`/`/ÛG`/`/`/d/Kd/Kd/K钳,9KΒ,9KΒ,9KΊ8+Ί8+Ί8+ʵ+Ɗb+Ɗb+ƊbkƚfkƚfkƚfyfkƚfkƚfkƚalƆalƆalƆy惾}6
o6
o6
o-o-o-o~ݖl9[Ζl9[Ζl9[Ύ8;Ύ8;Ύ8;Ύ>~~;c;c;c{g{g{g{7goߜ={={={={N);e씝SvN);e씝SvN);e씝SvN);e씝Sv;cg쌝3v;cg쌝3v;cg쌝3v;cg쌝sv9;g윝sv9;g윝sv9;g윝sv9;g]v.`]v.`]v.`]v.`]Kv.%d]Kv.%d]Kv.%d]Kv.%d}`}`}`}`}`}`}`]+vŮbW]+vŮbW]+vŮbW]+vŮbW]kvͮ5f]kvͮ5f]kvͮ5f]kvͮ5fG}dG}dG}dG}dG}dG}dG}dG}b'}b'}b'}b'}b'}b'}b'}b'}fg}fg}fg}fg}fg}fg}fg}f
a7ݰvn
a7ݰvn
a7ݰvn
a7ݰvn-eݲ[vn-eݲ[vn-eݲ[vn-eݲ[}a_}a_}a_}a_}a_}a_}a_}a_cwݱ;vcwݱ;vcwݱ;vcwݱ;v=gݳ{v=gݳ{v=gݳ{v=gݳ{{`={`={`={`=+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾoƾoƾoƾoƾoƾoƾoƾoξ;ξ;ξ;ξ;ξ;ξ;ξ;ξ=G#{d=G#{d=G#{d=G#{dO='Ğ{bO='Ğ{bO='Ğ{bO='Ğ{f=g̞3{f=g̞3{f=g̞3{f=g̞>H>g>3=+/zz_]w?~?ov;`v;`v;`v'OT\/RZ>>닾~w_[y!;dCv!;dCv!;dCv!;d#vĎ;bG#vĎ;bG#vĎ;bG#vĎ;bGcv̎1;fcv̎1;}Ǿcw̎1;fcv̎1;f석vN ;a'석vN ;a'석vN ;a'석vN ;a/ٛgzzEoV^/>9~{˳7?*`/⇟`/Ku/Kd/Ku/w-]ҵ-7?/]/וr_W}]++ue~lՏ/V6W6W666666666666~͵͵͵__nnnnnnnnnnnno_mo͟アm:~ӯ_m]ǖl9[ױl9[Ζ8;Ύ8;Ύ8;Ύ;<;ε;wcX;lڱ=kڳ=kڳ=kڳi|lI˳3̽l?7g]㞻ܔrSnM)7ܔrSkMw&w*w*Τ5妞锛zgRL}M3g:Lה3v;cg쌝3v;cg쌝3v;cg쌝3v;cg쌝sv9;g윝sv9;g윝sv9;g윝sv9;g]v.`]v.`]v.`]v.`]Kv.%d]Kv.%d]Kv.%d]Kv.%>>>>>>>>]+vŮbW]+vŮbW]+vŮbW]+vŮb]kvͮ5f]kvͮ5f]kvͮ5f]kvͮ5>#>#>#>#>#>#>#>>O>O>O>O>O>O>O>3>3>3>3>3>3>3>3n
a7ݰvn
a7ݰvn
a7ݰvn
a7ݰvn-eݲ[vn-eݲ[vn-eݲ[vn-eݲ/¾/¾/¾/¾/¾/¾/¾/cwݱ;vcwݱ;vcwݱ;vcwݱ;v=gݳ{v=gݳ{v=gݳ{v=gݳ{`={`={`={`}e_W}e_W}e_W}e_W}e_W}e_W}e_W}e_W}c7}c7}c7}c7}c7}c7}c7}cw}gw}gw}gw}gw}gw}gw}gw}g=G#{d=G#{d=G#{d=G#{bO='Ğ{bO='Ğ{bO='Ğ{bO='Ğ3{f=g̞3{f=g̞3{f=g̞3{f=g~p|0aaaaaaaaaaa>yߧzzzEoV^QY_U7?v;`v;`v;`v;`Ꙟ^^[{}G}g}W}ӯПK?S=!;dCv!;dCv!;dCv!;dCvĎ;bG#vĎ;bG#vĎ;bG#vĎ;bGcv̎1;fcv̎1;fcv̎1;fcv̎1;f옝vN ;a'석vN ;a'석vN ;a'석vN ;a'{^<_/ًG_/>8~'}7?M9?d/ytK|%o7ҵ-K?SvvWvWvWiewew~\\rM++VVVU6}msmsw\\=7}yq?7~ύ͍ͭͭͭͭͭͭͭͭͭͭͭ/Ϸ??~wϷ_=?[Ζlu
[emY;֎cX;֎cX;֎c8;Ύ*Ǝc8;Ύ9{Ξ9{Ξ9{}ڳ~ys=ww/ދ{߸{nM)7ܔrSnM)7N;z'SdL{z'SdMw2NkN٩1e쌝3v;cg쌝3v;cg쌝3v;cg쌝3v;c윝sv9;g윝sv9;g윝sv9;g윝sv9`]v.`]v.`]v.`]v.%d]Kv.%d]Kv.%d]Kv.%d]Kv>>>>>>>>bW]+vŮbW]+vŮbW]+vŮbW]+vŮ5f]kvͮ5f]kvͮ5f]kvͮ5f]kv>#>#>#>#>#>#>#>#>O>O>O>O>O>O>O>O3>3>3>3>3>3>3>3>vn
a7ݰvn
a7ݰvn
a7ݰvn
a7ݲ[vn-eݲ[vn-eݲ[vn-eݲ[vn-e¾/¾/¾/¾/¾/¾/¾/¾;vcwݱ;vcwݱ;vcwݱ;vcwݳ{v=gݳ{v=gݳ{v=gݳ{v=g={`={`={`={`_W}e_W}e_W}e_W}e_W}e_W}e_W}e_W}c7}c7}c7}c7}c7}c7}c7}c7}gw}gw}gw}gw}gw}gw}gw}g#{d=G#{d=G#{d=G#{d=GȞ{bO='Ğ{bO='Ğ{bO='Ğ{bO='̞3{f=g̞3{f=g̞3{f=g̞3{f=}0{zzzzzzzzzzzſO^^~;}'}}7MC?y"`v;`v;`v;`v=yzzzEoV^QY_U7?rCv!;dCv!;dCv!;dCv!;d#vĎ;bG#vĎ;bG#vĎ;bG#vĎ;bGcv̎1;fcv̎1;fcv̎1;fcv̎1;f'석vN ;a'석vN ;a'석vN ;a'석vN {^X<{ґx>9_|q*Wb/~wd/؋C?^%{^/^%{麗/X~poU~\+״r?WvW\]w_^~+篜vk篝vk篝vk^kkoqoqoqo\;}G<ۏ_^lں_[ikwkwkwgwgwgwgwgwgwgwgwgwgwgwgwgww}'.v߿fZv͎c{枹g{枹g{枹{枹g?bwܻν3̔2SfL)3e̔2SfJ_zSWJ_+ޯzSWO=+u+e쌝3v;cg쌝3v;cg쌝3v;cg쌝3v;c윝sv9;g윝sv9;g윝sv9;g윝sv9`]v.`]v.`]v.`]v.%d]Kv.%d]Kv.%d]Kv.%d]Kv>>>>>>>>bW]+vŮbW]+vŮbW]+vŮbW]+vŮ5f]kvͮ5f]kvͮ5f]kvͮ5f]kv>#>#>#>#>#>#>#>#>O>O>O>O>O>O>O>O3>3>3>3>3>3>3>3>vn
a7ݰvn
a7ݰvn
a7ݰvn
a7ݲ[vn-eݲ[vn-eݲ[vn-eݲ[vn-e¾/¾/¾/¾/¾/¾/¾/¾;vcwݱ;vcwݱ;vcwݱ;vcwݳ{v=gݳ{v=gݳ{v=gݳ{v=g={`={`={`={`_W}e_W}e_W}e_W}e_W}e_W}e_W}e_W}c7}c7}c7}c7}c7}c7}c7}c7}gw}gw}gw}gw}gw}gw}gw}g#{d=G#{d=G#{d=G#{d=GȞ{bO='Ğ{bO='Ğ{bO='Ğ{bO='̞3{f=g̞3{f=g̞3{f=g̞3{f=}0x0x0x0x0x0<}/}gzzWz_FoNAIE_M~C/O;`v;`v;`v;`O艹^^~;}'}}7MC?Cv!;dCv!;dCv!;dCv!;dG#vĎ;bG#vĎ;bG#vĎ;bG#vĎ;fcv̎1;fcv̎1;fcv̎1;fcv̎ ;a'석vN ;a'석vN ;a'석vN ;a'석v^ų7/B{Ƿb/؋~(g#{78?{^^^|r^/~f/]r-K{K{+{+{+{+{+{+ײr-+qeoZV6V6V6V6V666666];wܵs~s]s8w܍s78o㼍68oGױq);;[;[;[;[;[ݗ[ckkIesks۹;|ǻvw_ܹݯ_X;֞gY{֞gY{֞gY{×{֞w
{װwޞ]=o=/奼R^Ky)/奼SזzwRNM=w'zwRNM;7ޝԳSv;cg쌝3v;cg쌝3v;cg쌝3v;cg쌝sv9;g윝sv9;g윝sv9;g윝sv9;g]v.`]v.`]v.`]v.`]Kv.%d]Kv.%d]Kv.%d]Kv.%d}`}`}`}`}`}`}`]+vŮbW]+vŮbW]+vŮbW]+vŮbW]kvͮ5f]kvͮ5f]kvͮ5f]kvͮ5fG}dG}dG}dG}dG}dG}dG}dG}b'}b'}b'}b'}b'}b'}b'}b'}fg}fg}fg}fg}fg}fg}fg}f
a7ݰvn
a7ݰvn
a7ݰvn
a7ݰvn-eݲ[vn-eݲ[vn-eݲ[vn-eݲ[}a_}a_}a_}a_}a_}a_}a_}a_cwݱ;vcwݱ;vcwݱ;vcwݱ;v=gݳ{v=gݳ{v=gݳ{v=gݳ{{`={`={`={`=+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾoƾoƾoƾoƾoƾoƾoƾoξ;ξ;ξ;ξ;ξ;ξ;ξ;ξ=G#{d=G#{d=G#{d=G#{dO='Ğ{bO='Ğ{bO='Ğ{bO='Ğ{f=g̞3{f=g̞3{f=g̞3{f=g̞>>`>`>`>`>`>W>3=+/zz_]w?~?o_v;`v;`v;`v'O_T\/RZ>>닾~w_[y!;dCv!;dCv!;dCv!;d#vĎ;bG#vĎ;bG#vĎ;bG#vĎ;bGcv̎1;fcv̎1;fcv̎1;fcv̎1;f석vN ;a'석vN ;a'석vN ;a'석vN ;a/ٛW#{؋7o{o#{Wo~Uo?{^%{^{麗^/?ܥsW]9w垭ٕϮ~)[>?>Zg>l|fcgsݗWO^m}~o|~[u9[l}~}r/-wkgkgkgggggggggwvvv띝{gW}ןKOG;{{{{{}ݻ{ƞg{Ҟ˫'}~jw߾ڳY{5}{֞RVJY)+eRVJ]OK=?g7Ե=J='즞Գz~RO:SON;cg쌝3v;cg쌝3v;cg쌝3v;cg쌝3v9;g윝sv9;g윝sv9;g윝sv9;g윝sv.`]v.`]v.`]v.`]Kv.%d]Kv.%d]Kv.%d]Kv.%d}`}`}`}`}`}`}`}`vŮbW]+vŮbW]+vŮbW]+vŮbW]kvͮ5f]kvͮ5f]kvͮ5f]kvͮ5f}dG}dG}dG}dG}dG}dG}dG}dG}b'}b'}b'}b'}b'}b'}b'}bg}fg}fg}fg}fg}fg}fg}fg}f7ݰvn
a7ݰvn
a7ݰvn
a7ݰvn
eݲ[vn-eݲ[vn-eݲ[vn-eݲ[vn}a_}a_}a_}a_}a_}a_}a_}awݱ;vcwݱ;vcwݱ;vcwݱ;vgݳ{v=gݳ{v=gݳ{v=gݳ{v{`={`={`={`=+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ƾoƾoƾoƾoƾoƾoƾoƾo;ξ;ξ;ξ;ξ;ξ;ξ;ξ;ξG#{d=G#{d=G#{d=G#{d='Ğ{bO='Ğ{bO='Ğ{bO='Ğ{bO=g̞3{f=g̞3{f=g̞3{f=g̞3{f?>`>`>`>`>`>`>y'o_T\/RZ>>닾~w_[}v;`v;`v;`Q\_m$Fh22k{(UpkƮk?ygޛdṋ3&0Sh>/f2q:ή:ή:ή:ή:ή:ή:ή:nnnnnnnnn&n&n&n&n&n&n&nn[n[n[n[n[n[n[n6n6}cn͝777mvwemvvgv۟amv~b?yzoO:'~b?~=<6og?;yv}<7??߳ߋϋ߽ݫ߽ݫ߽뗇WW7s}8oyxK7oay4c7vqlDZ;8y8ǭ::븮㺎.]Iu.]{tӵo[t]|3?t9]N8=N8=N8=yzuy{]syz>>>>>>>>{{{{{{{{!{!{!{!{!{!{!{{G{G{G{G{G{G{G{1{1{1{1{1{1{1{1{̞' {' {' {' {' {' {' {' {ʞ){ʞ){ʞ){ʞ){ʞ){ʞ){ʞ){ʞ{ƞg{ƞg{ƞg{ƞg{ƞg{ƞg{ƞg{ƞ9{Ξ9{Ξ9{Ξ9{Ξ9{Ξ9{Ξ9{Ξ9{^{^{^{^{^{^{^{^{^%{^%{^%{^%{^%{^%{^%{^{^W{^W{^W{^W{^W{^W{^W{^5{^5{^5{^5{^5{^5{^5{^5{ް7
{ް7
{ް7
{ް7
{ް7
{ް7
{ް7
{ް7
{-{-{-{-{-{-{-{{ޱw{ޱw{ޱw{ޱw{ޱw{ޱw{ޱw{ޱ={={={={={={={={>>>>>>>>>#>#>#>#>#>#>#>>O>O>O>O>O>O>O>3>3>3>3>3>3>3>3̾/¾/¾/¾/¾/¾/¾/¾/ʽr+ʽr+ʼ2+ʼ2+ʼ2+ʼ2+ʼ2+߿neޙh>sk̽I&L6T<jo4oc{[c5v]c5v]c5v]c5v]c5v]c5v]c5v]c5߿;|0'scn͝7Ʉɦ<G|1_7m~mk^guv]guv]guv]guv]guv]guv]guv]guv`7
v`7
v`7
v`7
v`7
v`7
v`7
v`7Mvd7Mvd7Mvd7Mvd7Mvd7Mvd7Mvd7Mvd-vb-vb-vb-vb-vb-vb-vb-vfwwh>n߹em;LmW?n6?O6}>q9?qOܧ[}7n>9ٱώ}v|g{{W{W{uN뗇WyuWys7ys7ysx}+7Ǽ91t:q|w<_w<v>v<?n=':ίcWǮ]]vuMеkwpۍnv[nny~opet=Fc=Fc=Fc=+2myOzy[}n}n}n}w>`<}vg}vg}vg}=`=`=`9\{8{vy{{{!{!{!wr|9dC9dC9d#9b#9b#9b#y3e#9b#9b#9b#9fc9fc9fcxlo>̱;v}1ws1ws1w̝p'ɻɝ{Û8ωk;q9aN\ s\'_=39aN9aN9eN`>Oܚ;so M1y0boaOS=eOS=c3=c3=c3=c3=c3=c3=c3=c3=gs=gs=gs=gs=gs=gs=gs=g`/`/`/`/`/`/`/`/Kd/Kd/Kd/Kd/Kd/Kd/Kd/Kb+b+b+b+b+b+b+b+fkfkfkfkfkfkfkfaoaoaoaoaoaoaoao[eo[eo[eo[eo[eo[eo[eo[c;c;c;c;c;c;c;c;g{g{g{g{g{g{g{g}`}`}`}`}`}`}`}`G}dG}dG}dG}dG}dG}dG}dG}b'ߛ'ߛ'ߛ'ߛ'_'ߛ'ߛ'ߛ'ߛ'O|o|_||wmrE'|w|w|w'}b'}bg}fg}fg}fg}fg}fg}fg}fg}a_}a_}a_}a_}a_}a_}a_}a_ޫW{^{jޫW{^{jޫW{^w`>Oܚ;so M1y0bo0?/15v]c5v]c5v]c5v]c5v]c5v]c5v]c5v/μ7Gܘ[sgM2a)2|6_W|7e~?uv]guv]guv]guv]guv]guv]guv]guv`7
v`7
v`7
v`7
v`7
v`7
v`7
v`7
vd7Mvd7Mvd7Mvd7Mvd7Mvd7Mvd7Mvd7-vb-vb-vb-v*wwʰ[n[n[n[n|27v;eۏwkmxn^[^{=xOɹ>dܞmzٮg]gzzٞg>;g=;q/{q܋^//^8/wWߌWǼ:1oys̛co7Ǿyފ탱s<ovձcWǮ]:γ8ߎ;ηcocoގ:_70^^]uy]^uy]^uy]^u.]ev>uevyofev=fc=fc=fc]1{=x=Ǟ1{^=zyzs>s>sܾkg}}n}n}n};;;{aw|~x
;;;C;C;C;C;|xyZC=dC=dC=b#=b#=b#=b#=b#=b#=b#=b#=fc=fc=fc;=c;掹c;掹c;掹c;N`>Onnro&܉ϔ;>0'̉h};aN9aN9eNS9wӏ擹15V~~0b۔3gΔ3L9SΔ3L93Ό3s^3Ό383s8sN3Ό38383Ό383Ό383Μ3]9kΚ9kΚs9gΙs9gysޜ7ysޜ7yނ-xނ-xނ7q\d?sw]pw`/`/`/Kݒd/Kd/K<.Kd/Kd/]%{^%{^%{^Ww]qWw]qW^?+⮸+⮸+r+⮸+⮸+]5{^5{^5s\35s\3̵s]35s\35s\37
s07
s07
sx]maoao7
{ް7
{ް-{h-{-{9-{-{:-wr-wr-wqwwqwwqwwqwwqwww{ޱw{ޱw{={={={o={={={={={>>>>xO}`}{{G{dGydGy::#<2#<2=r#=r#=rOZ||ם|ם|ם|ם|ם|ם|םO{{{{oEO8N8y/|ߝ'}b'}bg}fg}fg}fg}fg}fg}fg}fg}a_}a_}a_}a_}a_}a_}a_}a_}e_W}e_W}e_W}e_W}e_W}e_W}e_W}e_s;|0'scn͝7Ʉɦ<G|1_7m~mkƮkƮkƮkƮkƮkƮkƮk?ygޛdṋ3&0Sh>/f2q:ή:ή:ή:ή:ή:ή:ή:nnnnnnnnn&n&n&n&n&n&n&nn{o7[7n̽ Mo=G?sZ_8-N8-N8mNf?Ӿ5wmV;^boo7o߿c'r
O''\)WOxc{پg]g?<?l׳=o/s9|qNjkjy}uW5~u+c{u7Ǿ1ޜ[zs[1y0979zͮ]:Kx,q:;ιckcoގ{;ߌ@۟0:2umZkr.]o߆etKt9]N<=N8=N-^=NOzyk|z^s=\kyyMx=^Y}V\>\>y{^}ߵ>>>k^y
<Gր5kmooo!o9~|:Ϡ!graw>!gr!gr!gr!gqF8rn#67rn#6rG>FsF^#ވ7x#ވ7x#ވ7x#ޘ7yc1ov{F1w\u;掹c;掹c;掹c;N8Z8 s3}k~3}Rg5aM>O& gp& gp&)gʙzLcO?GxS響7ojԾ}SM7oz?9sfΙ7sf7of̾}3f7wv5w{sqv9snιs;v.\ع.]ػwqc~/X0ǽpcX0cX0%cX2%cX2,+%gYre\%gYr%gYrgYqVcX1V՝7Uv[s\1WsVs\1Ws\1Ws\35s5w]s5w殹k殹kڹk殹k殹kn߯憹an憹Yp7
wp7
w|7
wp7
wp7
wr'ݲ-{7Öeo[eo[[eo[eo[c;zg``|ޱw{y;c|&|W\wqwwqwws=wݻ{g{E{{y{{E{3=s3=s3<8y`|y9{w`;8;;;>yGޑwyGGytGޑwtGx=G{G{G{➸'{||~]vyY9y-\ߓs=oOӓ>O>O3>3>3>3>3>3>3>3>/¾/¾/¾/¾/¾/¾/¾/¾+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾJ}_yo>擹1ܛddSLẹln6?O6?ƮkƮkƮkƮkƮkƮkƮkƮkW/μ7Gܘ[sgM2a)2|6_W|7e~?uv]guv]guv]guv]guv]guv]guv]guv`7
v`7
v`7
v`7
v`7
v`7
v`7
v`7
vd7ݥ&-a7Mvny0d7Mvd7Mvd7Mvbحw|4̍5Vrͣew-[vnݲewmSCj߶mvR0m{>dOv>1iӓ}Oww?2ǭ]Ov=l׳پg{Ggٮg?;ٱώ}vc_ڽx^><Ǿ8q/ymǼ:1ye~5z+1c^7Ǽ9c|q~z\9ͱo?͎7͎7;:vtѱ\Sc|{L:O/?{Lz?Oz?S>Sޮ]SkE'M4}I'M4}٥S.MzvivO=WgvO=iݓvO=iݓvO=ooﵢS?}K0^rt{껶}עI'ݞt{I'ݞt{IguݓvO=
fO=
VOӓNO:=ӓNO:=kt1
uuzf?[ӓNOCk
C'4zI'4zIFO=<~myqF:myi<FcZi͓6O<i͓6O<iع9ccjiIsifφ7ycɓ&O<iɓ&O<iɓ&O˓&OirkӉIK<M߄7i4q~<i͓6O<i͓6O<M4}g\)ojsnI'M4yI'M4y3g5̮u1٥ǓOz<ǓOz<ǓOsvͽ~`nO:<I'txI'txI]g®]z;-ZإNz;Nz;NKv.tvZzh촴{-vk줱N;i줱N;i줱;Ic']Vv餧~=V~=tIO'=tIO'=tZۿtIO'=֬חNZ:i餥ӚNkNZ:i餥NZ:i餥NZ:mN-tIK'-6̍IK'-tiiptIK'-tIK'-tirUKGl-tIK'ttIG'ttrutr\ttIG'ttIG'tti9WGWG'ttIG'ttIG'vߎ褣N::褣N::褣N{:-ܙ{{n>;[LG=SK=WK'-tIK'-tIK'-t:pup>Ńk}`}`غ::iꤩN:iꤩN:iꤩӑ9ktG{tdGydjꤩӑNz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧Nz:餧CO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:tӡCO=z:th7G<Dm4onfŰt4n=z:tӡCO=z:t=FChԡ_uN/nѡCG::ttѡCGGC]K폏C;Gv9s܆v7cΡ鯇1sxC3NOyǡC
/~qйsCWYƫ߽ަxu=5o^=?nsC
oѹ77c9V
ooԻwC
wcWw㜵ntҺq:7tnйsCF9hиѵC߆fݿQFczG:7tnйѳOF{g
=Fۆ
m6mhжmCۆ
m6v_c/]ט
64lhаaCÆ
6
V
WC^
z5jЫWCЮ]z5ЫMcԧ1iЧOC>
}4iЦMc亍ԦMcM1{whЦKC.
]4tiХKC.<_~=z4>h}>?GC1G
=z4hУGC gѣ14&<M{wICƄ9yIcxiФIC&
M44ihФIC&
Mkecʛޙ{|vOn_n֩SCN
:5tjЩScfn3fvҩSCN
:5tjЩSCN
s}ncۥSCN
:5tjЩSCN
:5v-\}?M^z5jЫWC^
z5jЫWci^}Kv-ҧOC>
}4iЧOC>
}+i6}44iԤIC&
M44ihФIC&
Mk;5iԠAcmu\{^Н;Cw3tgН;Cw{gl֞=CsZ36,ֆ5CkZ3fhК5CkZ3,[֖5Ck[ [z3flz3flz3fЛ7Coz3fЛ7c֛7c֛7Coz3fЛ7c֚5CkZ3fhК5CkZ3Vћ7CkZ3fyZ3fhК=3Cg:3tfЙ3Cg:3L:34fhИ1ӗ/C_m2>eЗ/C_}2eЗ/C_Ƒ/C_u/C_ԗ/C_sԗqdЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}2eЗ/C_}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}e֗Y_f}ܼ|L&LŰen>2ˬ/2ˬ/Mw|ˬ/syˬ/̭Ǭ)s뫱KWf]ue֕YWf][vj٥+̺2ҕmZ2ka%smn:hɬ%d֓YGfud֑wYCf
=f
91k<c֎Y3f͘5c~_N:1jF1kĬ z0oq9Ff-u`p:v_e헵_~h/keu]tuL1:/w=.Keu^yYeu^ݥr.k.k.k}1ۡr=\sYe=\sYe=\A--\rYe-\rYe-\rYЮ?5viᲆ.kᲆ.k.
[٣߲~-<z4~-<V
5\pYe
5\pYe
5\pyu6v:.oRqyqyW3oYe[oYe[oYe[oyzL>}vv˓dߵ[nY]QǬݲv-kݲv-kݲv-k<<ݖu[lYeZkYeZkYeZkYeZjy=.ZkYeZkYeZkYeZkYeviղVZ-kղVZ-kղVZ-kղV:-/nHeuZiYeuZiYeuZiYeuZiYcc[nXڻٿlYe͖5[lYe͖5[lYe͖5[lY啽+{u[^]٫^ݖu[mYeݖu[mYeݖu[mYeݖu[mymnk@n[^ۯ߲~-뷬߲~-뷬߲~-뷬߲~-ooY
C-oF7kٲf˚-kٲf˚-kٲf˚-kٲf[5[kYeEeZky뽱\赼구ײ^z-뵬ײ^z-뵬ײ^z-뵬ײ^;y鵬ײ^;
ײ^z-^;۲n˺-붬۲n˺-붬۲n˺-붬۲n{{n˺-붬۲n˺-붬ݲv-kݲv-kݲv-kݲvvݲv--뷬߲~p=8_
5\pYe
5\pYe
5\pYe
5\>r5\p=ᲆ.k|tu\qYs>>>fl-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rYe-\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\rE-W\i~-s{ኆ+4hኆ+hኆ+hኆ+hJCo8^Vt[mEVt[mEVt[mEVZjOo<ZjEVZjEMV[=VXyhŊ+Z?UW^E{oo߿4W\EsλǢJǹuqOv?cYEg}UoKa髢*諢*z詢*z詢*zjX:訢*:訢*:訢کh2p.ϓv*کhv*کhv*کhZhV*ZhV*ZhV*#oXTtQDe2CE=TPCE=TPCeCETtO<e<hy)hy)hyc|X&=:LxO29exO2aL):蜢s):蜢s):蜢e)Zhe)Zhe)Zhef)hYf)hYf)hYf)蓢O>)蓢O>)蓢O>)蓲PtI$EMR4I$EMR4I$EMR4I$EMRhKRtH!ERtH!ERtH!ERtH!eeطz|,hA)hA)hA)hA)hA萢Cʺ2>oH!ERtH!ERtH!ERtH!ERtH!eptHp6=R6=R6G)z葢G)z葢G)z葢G)z葲葲葢E):lY[(菢?(菢?(菢?(菢?(;(<菲鏢?(;菲c돲c돢?(菢?(菢?(菢?({[{)菢?(菲ꏢ?TQGEQGEQGEQGE<xGEEAQGEQGEQGEQG9rGZ돢?(G?(G(G?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢?(菢=(ڣh=(ڣh=(ڣh=(ڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TڣG=*QiJ{TakJ{T;so UtG;*QJwTtG;*Q鎪|0ء=*QiJ{TڣG=*Q=V]=2iq<G=*QiJsTϮިF7EoTzz;QiJkTZz5Q\gInn%w]UL*)(*5dv?C$"uY3g4h7~/W4h\Ѹ=oWqE-ϓ+W4h8뗰\ѸqE+W4h\Ѹ+W4h\ѸqE+W4h\Ѹ{[ۖ;٢Ec-[4hlآEc7gEs/_4h|E/_4hg3E/_4h|Eô\g}pg1Fc1c4h1FcY>:--glآ}wGُ+W4h\ѸqE+߽hlآEc-[4hlآEc-[4hlآEc-[4hlآEc-[4hlآEc-[4h,XDc%K4h,XDc;4vhءCc;4vhءCc
'4Nh8qB '4Nh8qB 'jsAc
46hlؠAc
46hlؠqAza}g9
glؠAc
46hlؠAc
46hlؠA2ά;qsά;サv1Bc#4Fh1Bc#4Fh1B7n{sA4>h|A4>h|A:\d#Fc#4Fh1Bc#4Fh1Bc#4Fhhٜ8= ڣٜ8= '4Nh8qB '4Nh8qBd6#'sAzA{246hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
46hlؠAc
4.h\иqA4.h\иqALИ1Ac 4&hLИ1Ac:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<y@:t<yߘ}cW:t7ߺ5<yߘ:t9s@:t9s@ +&+LV0Yad +}1&3Lf0a2d&3?ϋ?&?L~0aO4Oz#&GL19br#&G̿y})&SLL1b2d)&S̿m5c1&cLƘ1c2d>OOZ^=ə\'Ϗ=&{L1cd=&{}r߸ܚٜ|6L<2yd#G&Lwܙsww{̝;3dNkn}rgƝks-<2yd#G&L<2ydȼ7k}Z:8drȼ!w{]{3!C&L29dr!C3e3l2|03|0?&L1c?&LѬGz4ѬGc>G3c>h棙f>G=&{L1cd=&{'sGO>w;6xr|2ɼ'{2;wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;&wL1cr;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,Xܱcq;w,X7_<_7_Z_Y_?[7ߺjV5-sxc7o,Xxc7o,X?~eMk9s,X̱c1b9s,X̱c~9ryܞtk?s,Xm9Kc49륹/yOgײC,Y?=?]?-r%Kq?;g+W֫/_W{,^YWuxvYezexYyys1c,Yf3gg0sͿ۲g,YogϬ~,z8f50a,Yf1Y~Z3b0a,Yf1f1c,Y8fq1c,YV|g,Y,Xfb2e,Ygoos3g,Y<xf3g2e,Y,Xfb2e,Y,Xf}p,Y8fq1c,Y8fq1cGgp8|}ܖ>Y8fq1c,Y8fq}Xg,Y,Xfb2e,Y,Xfb2e,Y,Xfb2eˬ?=3b0a,Yf1b0O?/_,~Ye/_|3aìe/_,~Yei~Y=~v߮_,~Ye/_,~Ysa[[r'f1b0a,Yf1b03eϾG>?sjeg.],vYeb.];3;ϛ[-w?,fY̲e1b,Y,fY̲e2bu|nYܲ=nY/=˵0b,Y{ fY̲e1b,Y,fY̲e=fYf+?z0.se=trzp/_,~Ye/_GGsez|web.=,vY_,~Ye/_,~Y\/,~YO^cYOZaì's,YOcǬ'15s1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1c,Y8fq1cl9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs1c6l9fs}c6l7_[Xfߘ0_|e/_6l~e/_6l~j[gf.]6lvef.]xm6ʺZ-[6_-/Lv/|iK^.]6lv?}96ޟޟޟ^Lf3f4g~g~g~g~o6l|_y+<dz~<79|_ھ}{mkf7g{ƞ7=oyclLf5}o{kg[4i6lٿe/bl,Yff2e6_{=6_1c6l9fs1w~g;yf3g6l<yf߾zsos9i6lLf3f4i6i{i6lLf3f4i6l}Lf3f4i6lLfhGyf~?}3g6l<yfۿa6lf3f0a6x<lvef.]6lvx,lnܲes-[6lnz_XDzY6lf̲e3f,Y_e_p)S6lNٜ9es=ֶ%K6l.\ds%d'{?=6{lcf=g{?_Y{k3?;76olؼyc76o;g9Ιwμrnٴ̸3ܱcs;6wlܱcߛqoƽfܛq2;wYϺe潙fޛyo&wlܱcs;6wlܱx|_=z}[|W>ߕ|=twôlcf=6{lc?hُf?h]3{G~4G~4?c?6lc~2'~2'c~n'sc?dO>\cf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf=6{lcf={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qa={8qǹٹ[jV7A9r0a A9rp;w8qp;w/0ǹ9s[ncso>8q㼴9s8q0a9s8q0a5k8qXa5k8qXa5k8qXa=qx7o8qx5k8qXa5k8q|1Λ_7^8q0a9s8q0yk[{p;w8qp;/bw8qp;w8q~Wz}a={8qa=;{;w8qp;w8q~5k8qXa5k8q>8q0a1c8q0a+_8|q/_8|qe?[7-[8lqa-[߿zqa-[8lqa-[8lqa-[?ec8q0a1c8q0a1c8q0a1c8q0a1c8q0a1c8q0a1c8q0~><3ga={8qa={8q>?q|;[?X2E9,rXa"E9,rXa"Eo-0ɹ{a&MΝk3i(QF9r0a(QF9r0a(Q{s1νdso.gr)SN9r8p)SN9r8p)SN9k~p͜r8<5)SF9r0a(QF9r0a(QF9r_]/G3<h&>9|r'O>9|r'O>9|r'>y<y0y}2QF9rf(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF9r0a(QF97_<o̾oso̽ucp>s%%%%%%%%%%%%%~Ǐ_Z_YYm9YYYYYYYYYYYY{σ[[i˲[[[[r`````````````ޟe````````````^ W_xe/k&&&&&&&&&&&&222222222222k> _<_\/LLLLLLLLLLLLLj/;o3M0M0M0M0M0M0M0M0M0M0M0M0M75l h>Ӄmmmmmmmmmmm/^mX1N0N0N0N0N0N0N0N0NǾ::::::::::::::::::::::::::::::::::::::חֶe````````````|zփ}}Ssۭ???????????????ks| +?la`````````````````vĝkoxpڸ'w{3]#ă/''w,<<<{{{{{{{{{{{{{{{{{}t͏fOO<>hOf>KxNNN<i IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIo|7_Y_?Kɛg̻ϚO3{{{{{{{{{{{{{{{{yyyyyyyyyyyyyyy̓̓̓̓̓{k^IIɗ2ONNNNNNNNNNNNu^IIIIIIIIIIIIIW/GNNNNNNNNNNNNNNNNNNNNNNNNNNNbGIIIIIIIIIIIIIɷ{{{{{{{{{{{{{{yyyyyyyyyyyyyywwwwwwwwwwww}k99999999999999777777777777MMMMMMMMMMMMM~mִ匏dddddddddddddddddddddddddddddddddddddddK뛞kIIIIIIIIIIIIɿ#UIIIIIIIIIIIIIIɿ@3s{3y(y(y(y(y(y(y(y(y(y(y(y(y(y(y(?y^<<<<<<<<<<<<<<<<<<DDeggs(((((((((((((((((((̺3DyfuL&J&J&J&J&J&J&J&J&J&J&J&J&J&J&J&J&J&J&{3ͻ7{߫\rQrQޛEEEEEEEEEEEEEEEEEEE;>ܿaFFe\\LLLLLLLLLLLLLLLLL&G3(uģ壙f>CCCCCCCCCCCCCCCCCCCZ}2\.'Dd&%哿1.J.ʧ=kol62f]YLTLTLTLTLTLTLTLTLTLTLTLTLTLTLT?~m5kXZHHHHHHHHHHHHu5</F[{n/럖TTTTTTT/lV*V*V*V*V*V*V*V*V*V*V*Vgbbbbbbbbbbbbb^Vٵ3S1S1S1S1S1S1S1S1S1S1S1S1Sb+ggbbbbbbbbbbbzc/;=?????????????[ߚ˗WUUUUUUUUUUUU]sTqTqTqTqTqTqTqTqTqTqTqTqTs\;SSSSSSSSSSSSSSk53U1U1U1U1U1U1U1U1U1U1U1Uw=***********>ccccccccccccG~8,=rkkkkkkkkkkk_Z_o3ثثثثثثثثثثx>YgYLVLVLVLVLVLVLVLVLVLVLVLVLV8?|N+N+N+N+N+N+N+N+N+N+N+N+N+N?}so{-ϋيييييييييييييي,n3r_ϝoooooooooooooooppww쿝qqqqqqqqqqqqqqqqq,W;kq,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W&,W,WWW}/2\}̸d{ϭXq\}V\՝cbbbbbbbbbbbbbbbb7w<W<W<W<W<W<Wf\\ݛsu-ՃzhLWf3]=z4\+ebz4\+++++++++++++++''3ebzr\W\W\WOr]q]=uttttttttttttttttttttttttttt^+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.uc7f|f<1]7?ye&]lwv]lwv]lwv]lwh&]?0]]?.u]\wqu]\wqu]\w:qzθ]ֶx]aYx]w19/=y霗a.ֻ++It˗\QjphÜ8D~Z/^hzBZ/^tz/^{Bz/^{B]ֺ/t_}B/t_}ѳKE./_h~B/_h~BE=B/_Ba8/_B/_0t=w=CE=z0`ЃC=z0Fk#Fv#ߝC>}0aЇC>}cv5k}iЌC3f14chЌC3f&vMюC;v1chЎC;v1=S{.StdБ#CG:2tdБ#c=}3fvГ'CO=z2dГ'CO=sd3g:Е+CW]2teЕ+CW]i\],uf,vЙ3Cg:3tfЙ3c-^~oK;ugН;Cw3tgН;Cw+;gG+ghО=C{3ghО=C{kiРsm
:4thСCC
:4thСCcc
-;7νS
-Z4hhТEC
-Z4hhТSРAC
44hhРAC
44h켷_cwghО=C{3ghО=C{{q=3}5ghМ9Cs34ghМ9Cs{f~Gc^5Cs34ghМ9Cs34ghМqd}fGΑ9CsGќ9CsƑ9CsuӜ9Cs34ghМ9Cs34gؚ3N[s3NΪ9Cs[s[oz3fЛ7Coz3fК5?KWkş1ϞgyZ3fhК5CkZ3fhК5CgΌWkZ3.\Z3f\Z3f\<fhК5CkZ3fhК5Ckk3ԙ3Cg:3tf̸2zu^W:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tf̸nЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3Cg:3tfЙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3SgL:3ufԙ3?d?z3?z3?7SoLz3fԛ7SoLz3?z3?5{iaן?SL3gԟ?S̖wcM-Z4[)hml٩ESM-Z4hjlvg=m{ۮ.M]4uiԥKS.M]4uiԥKcgۗױG6Mm4ijԦMS6Mm4iv]Km4ivjԨٵSFM55jjԨQSFM55jj١QSFM55jjԨQSFM55jMf{ۣU^ѪUSVMZ5jjԪUSVM{s`]s}iԮ]SvM5kjԮ]SvM5k95ljaSæM
65ljaSæ͑6G>K
#;GvO-#;ljԲeS˦M-Z6ljԲeS˦M-Z6>ױc{umԴiSӦMM65mjԴiSӦMM65mN7OmԷoSߦM}6mԷoSߦ7mNlպuSMZ7njԺuSMZ7njݜgM3g;wfM͛75ojԼySM͛75ojԼySuo;wnNsN:8upSN:8upj\ػWGa77_
G/2M85qjĩS&NM8K/ӸZ8pj©SN-Z8pj©seΕ{U5pjSN
85pjS͵J͵{SMݛ7uoԽ{SMݛ77w/q6nԻwSMz7nԻwSM?뵵SM͛75ojԼySM͛75org/C玡SN
85pjSN
8
{s3?K77=s3)}8qǩS>N}8qy>8ӁSwb95W/^=pΪS3fN͜95sj̩S3fN͜9Ϊ>5sj̩S3ѵ̩S3f#s<_r˩S/^Nz9r˩S/牫S/牫S/^N'˩S/^N'^N'g̩S3fN͜95sj̩S3fN͜g.ݜ9uszͩS7NgnNݜ9usͩS7nNݜ9uspusͩͩS7nNݜnΩS;vN9sjΩS;vNWgyjΩS;vΫjΩS;{j:~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϩS?~N9sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\sϥK?~.\>Tߚ[?~.\_O~aϥK?~.\sϥK?~Ͽ-\ߙ̏槗b~Wݼ?̟KK.-]ZtiҥKK.-]o^JKOOW뫱[SWOnM]-5uc¤[S.M]4uij4uiն<mFfhץK_.}]uץK_.}]Ku{;&4wiܥKs.]4wiܥk.]Zvu}.]ZviڥKk.]ZvRo}=z߳S{C{.]ڻwiޥK{.]ڻ_߹}{wf-^}/-^ZxiK/-^Zxi5^\ρu`&K&/M^4yiK&/M^4yi5tK}K/=^zxK/=^zx٩k0wKG=^zxK/=^zxKvZxiK/-^ZxiK/-^g7\kb/=^zxK/=^zx;w8wjK6/m^ڼyiK6/m^ڼyi5c3+}^3c{;14ziKF/^4ziKF/^4z;wn4ziۭKF/^4ziKF/^7:tz-b~6_o5^tzKN/^:tzd-YKXR͗ky4{iKf/^4{iZٻwy^ٻwe>^+Vޫ~/^{K~/^k;Z۹slnM_k{|Kϗ/=_z|ػ^|ػw6^ۿ_痾/}_}Kߗ/}_[>ǭkWwc֯[{~mw_Z~iK/_Zv뼳_ίtttK/_t>_{g3ukkK=[3cK/_>8}9}`K/_\_E_ڿiK/_ڿ̣gK/_ڿiK#=ڿiK/_ڿi:qOܓ|jKW/_ڿi:qO ڿtK/_tuvu:/_tuK/:;K/_tK^/l_ڿiK/_ڿi]K/_ڿiK}KוK/_άK/_ڿ+K/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/_tK/F7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otF7otototo>غ[7otF7ot^|:?6_ow̟x~r?O'䶸-nⶾ3߿4~2mj~3n?5n+?-n涹mnv6msۼ6k;gi;cv6mV۬6:i:η_{:㜝78WauXVauXVauY]V.꺇O/Mu=.r_t9]Nt9]N8=Nϙz|v=NL=Vs-{eg=vc=vc=vc=vg}new-վk}nwm>s>s>s>p܁k;8up?gvpwooo!o?߾4Cސ5d
]!kz9C5r!gr!gr!gqF5bXk:r=G{eqF{t9ȹFoFoFoƼoǼ1o|cg~coƼ1oƼ1oƼ1oƼ1ơ&Mo|r^&8q w8;aN50' s0' s0' sʜ2)sS̩pʜ~;<zLuʝrܩs|ʝr)wʝr)wʝrgwƝ39cΜs1s1g9cΘ3̵foƛfoƛfoΛ漹sޘ,9s90w9sΜsYܹsιs9gΙs9gΙs9g.悷-|o[8sg.xfX>?3cX0cX0%cX:גf`~4?,Βݟ}5/-i5s\2%s\2s\1W>s\1WsZqVgYqVgYqVgYqVgYs֜5glkΚ9k'Z;7k暹fk暹fk暹fk暹fn憹07
s07>p7zް7
{ް7~'n/aoaoeoj[eo[eoyi-wr-w:֙-wr-wrw9;;εy;c;c;c;c;g{{]k{{g{枹g{枹gy`y`y`y`y<8p=p=p#=}rdG}dG}>#>#:r#=r#=rO<1O<1O=N<1O<O<1O<1O3<?5gp`4Y#5u>;W3p~1qO]3>3>3¾/¾/¾/3_}a_}a_¾/¾/ʾWϏ+ʾ+ʾ+ʾ+ʾ+W}e_W}e_W}c797o~t97sn7n{ӄ7rG7n{wݍ}c7}c7}gw}gw}gw}gw}gw}gw}gw}g`?`?`?`?`?`?`?`?Od?Od?Od?Od?Od?Od?Od?O_7w|g7?Ob~5Wb00i4//_ٯW+~e_ٯW+~e_ٯW+~e_ٯW+_7w|g7?Ob~5Wb00i4/㚿o7~co7~co7~co7~gw;~gw;~gw;~gw;`?`?`?`?d?ٟO'd?ٟO'd?ٟO'd?ٟOvb-vb-vb-vb-vb-vb-vb-vbmvfmvfmvf_^jj~Wwռvfmvfmvfmvawvawvawvǹ;qގvfavfavfav]fev]fev]fev]f2.2.2.2.1{1{1{1{>ߞ=vc=vc=vc=vg}vg}vg}vg}vg}vg}vg}vg}=`=`=`=`=`=`=`=`C=dC=dC=dC=dC=dC=dC=dC=d#=b#=b#=b#=b#=b#=b#=b#=fc=fc=fc=fc=fc=fc=fc=fc=aO=aO=aO=aO=aO=aO=aO=aOS=eOS=eOS=eOS=eOS=eOS=eOS=eOS=e3=c3=c3=c3=c3=c3=c3=c3=gs=gs=gs=gs=gs=gs=gs=gs`/`/`/`/`/`/`/`/Kd/ok`~4?/d/7j^O^%{^%{^{^W{^W{^W{^W{^W{^W{^W{^5{^5{^5{^5{^5{^5{^5{^5{ް7
{ް7
{ް7
{ް7
{ް7
{ް7
{ް7
{ް7
{-{-{-{-{-{-{-{{ޱw{ޱw{ޱw{ޱw{ޱw{ޱw{ޱw{ޱ={={={={={={={={>>>>>>>>>#>#>#>#>#>#>#>>Osv\;y<Nk'}\;y<Nk'ϵs>O>O>O3>3>3>3>3>3>3>3>/¾/¾/¾/¾/¾/¾/¾/¾+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ƾoƾoƾoƾoƾoƾoƾoƾo;ξ;ξ;ξ;ξ;ξ;ξ;ξ;ξ~~~~~~~~'~'~'~'~'~'~'~/߿o̷;h~2_nDŽIS1~+~e_ٯW+~e_ٯW+~e_ٯW+~e_ٯfn1ߚ|1?_̯7j^OO/o&Me\7~co7~co7~co7~cw;~gw;~gw;~gw;~g?`?`?`?`?ٟO'd?ٟO'd?ٟO'd?ٟO'n[n[n[n[n[n[n[n[n6n6n6n6n6n6n6n;;;;;;;........{{{{{{{{>>>>>>>{{{{{{{{!{!{!{!{!{!{!{!{G{G{G{G{G{G{G{G{1{1{1{1{1{1{1{ {' {' {' {' {' {' {' {){ʞ){ʞ){ʞ){ʞ){ʞ){ʞ){ʞ){ʞg{ƞg{ƞg{ƞg{ƞg{ƞg{ƞg{ƞg{Ξ9{Ξ9{Ξ9{Ξ9{Ξ9{Ξ9{Ξ9{Ξ{^{^{^{^{^{^{^{^%{^%{^%{^%{^%{^%{^%{^%{^W{^W{^W{^W{^W{^W{^W{^W{^5{^5{^5{^5{^5{^5{^5{^
{ް7
{ް7
{ް7
{ް7
{ް7
{ް7
{ް7
{ް-{-{-{-{-{-{-{-{ޱw{ޱw{ޱw{ޱw{ޱw{ޱw{ޱw{ޱw{={={={={={={={>>>>>>>>#>#>#>#>#>#>#>#>O>O>O>O>O>O>O>O>3>3>3>3>3>3>3>¾/¾/¾/¾/¾/¾/¾/¾+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾ+ʾoƾoƾoƾoƾoƾoƾoƾoξ;ξ;ξ;ξ;ξ;ξ;ξ;ξ~~~~~~~~'}r'}r'}r'|2'|2'|2'|27
\ No newline at end of file diff --git a/src/main/resources/assets/skyblocker/dungeons/dungeonrooms.json b/src/main/resources/assets/skyblocker/dungeons/dungeonrooms.json index 5303f6b3..f626623d 100644 --- a/src/main/resources/assets/skyblocker/dungeons/dungeonrooms.json +++ b/src/main/resources/assets/skyblocker/dungeons/dungeonrooms.json @@ -940,5 +940,12 @@ "secrets": 1, "fairysoul": false, "sbp": "four-banner" + }, + "Zodd-1": { + "category": "1x1", + "secrets": 1, + "fairysoul": false, + "dsg": "null", + "sbp": null } -}
\ No newline at end of file +} diff --git a/src/main/resources/assets/skyblocker/dungeons/secretlocations.json b/src/main/resources/assets/skyblocker/dungeons/secretlocations.json index 760e72d9..3815e759 100644 --- a/src/main/resources/assets/skyblocker/dungeons/secretlocations.json +++ b/src/main/resources/assets/skyblocker/dungeons/secretlocations.json @@ -263,12 +263,12 @@ "z":21 }, { - "secretName":"1 - Pearl", - "category":"pearl", - "x":25, - "y":75, - "z":27 - }, + "secretName":"1 - Pearl", + "category":"pearl", + "x":25, + "y":75, + "z":27 + }, { "secretName":"1 - Item", "category":"item", @@ -842,12 +842,12 @@ "z":21 }, { - "secretName":"2 - AOTV", - "category":"aotv", - "x":25, - "y":76, - "z":22 - }, + "secretName":"2 - AOTV", + "category":"aotv", + "x":25, + "y":76, + "z":22 + }, { "secretName":"2 - Stonk", "category":"stonk", @@ -3073,12 +3073,12 @@ "z":5 }, { - "secretName":"2 - AOTV", - "category":"aotv", - "x":77, - "y":82, - "z":16 - }, + "secretName":"2 - AOTV", + "category":"aotv", + "x":77, + "y":82, + "z":16 + }, { "secretName":"2 - Wither Essence", "category":"wither", @@ -3131,12 +3131,12 @@ "z":9 }, { - "secretName":"3 - AOTV", - "category":"aotv", - "x":7, - "y":80, - "z":15 - }, + "secretName":"3 - AOTV", + "category":"aotv", + "x":7, + "y":80, + "z":15 + }, { "secretName":"3 - Superboom", "category":"superboom", @@ -3254,12 +3254,12 @@ "z":24 }, { - "secretName":"1/2 - AOTV", - "category":"aotv", - "x":84, - "y":91, - "z":26 - }, + "secretName":"1/2 - AOTV", + "category":"aotv", + "x":84, + "y":91, + "z":26 + }, { "secretName":"1 - Bat", "category":"bat", @@ -3835,12 +3835,12 @@ "z":51 }, { - "secretName":"5 - AOTV", - "category":"aotv", - "x":16, - "y":81, - "z":53 - }, + "secretName":"5 - AOTV", + "category":"aotv", + "x":16, + "y":81, + "z":53 + }, { "secretName":"5 - Chest", "category":"chest", @@ -4114,12 +4114,12 @@ "z":30 }, { - "secretName":"5 - AOTV", - "category":"aotv", - "x":52, - "y":78, - "z":9 - }, + "secretName":"5 - AOTV", + "category":"aotv", + "x":52, + "y":78, + "z":9 + }, { "secretName":"5 - Crypt", "category":"superboom", @@ -4895,12 +4895,12 @@ "z":52 }, { - "secretName":"2 - AOTV", - "category":"aotv", - "x":45, - "y":78, - "z":47 - }, + "secretName":"2 - AOTV", + "category":"aotv", + "x":45, + "y":78, + "z":47 + }, { "secretName":"2 - Chest", "category":"chest", @@ -4937,12 +4937,12 @@ "z":41 }, { - "secretName":"5 - Pearl", - "category":"pearl", - "x":15, - "y":84, - "z":56 - }, + "secretName":"5 - Pearl", + "category":"pearl", + "x":15, + "y":84, + "z":56 + }, { "secretName":"5 - Stonk", "category":"stonk", @@ -5144,12 +5144,12 @@ "z":61 }, { - "secretName": "7 - AOTV", - "category":"aotv", - "x":39, - "y":84, - "z":58 - }, + "secretName": "7 - AOTV", + "category":"aotv", + "x":39, + "y":84, + "z":58 + }, { "secretName":"7 - Chest", "category":"chest", @@ -5402,12 +5402,12 @@ "z":43 }, { - "secretName":"3/4 - AOTV", - "category":"aotv", - "x":49, - "y":83, - "z":36 - }, + "secretName":"3/4 - AOTV", + "category":"aotv", + "x":49, + "y":83, + "z":36 + }, { "secretName":"3 - Chest", "category":"chest", @@ -5581,7 +5581,7 @@ "z":24 }, { - "secretName":"3 - Stonk (may locked)", + "secretName":"3 - Stonk", "category":"stonk", "x":63, "y":55, @@ -5598,7 +5598,7 @@ "secretName":"3 - Entrance", "category":"entrance", "x":63, - "y":55, + "y":54, "z":7 }, { @@ -5630,12 +5630,12 @@ "z":15 }, { - "secretName":"5 - Pearl", - "category":"pearl", - "x":8, - "y":59, - "z":14 - }, + "secretName":"5 - Pearl", + "category":"pearl", + "x":8, + "y":59, + "z":14 + }, { "secretName":"5 - Entrance", "category":"entrance", @@ -5919,13 +5919,13 @@ } ], "Double-Stair-3":[ - { - "secretName":"1 - Pearl", - "category":"pearl", - "x":24, - "y":72, - "z":11 - }, + { + "secretName":"1 - Pearl", + "category":"pearl", + "x":24, + "y":72, + "z":11 + }, { "secretName":"1 - Wither Essence", "category":"wither", @@ -6072,5 +6072,14 @@ "y":94, "z":26 } + ], + "Zodd-1": [ + { + "secretName":"1 - Chest", + "category":"chest", + "x":7, + "y":70, + "z":3 + } ] } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 104163db..c3b8c92e 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -90,6 +90,13 @@ "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumInfo.@Tooltip": "If this item is donatable to the museum, then the item's category in the musuem is displayed. It also displays a marker indicating whether you've donated that item to your musuem or not (freebies not yet supported).\n\nMake sure to enable your Museum API for accurate information!", "text.autoconfig.skyblocker.option.general.itemTooltip.enableExoticTooltip": "Enable Exotic Tooltip", "text.autoconfig.skyblocker.option.general.itemTooltip.enableExoticTooltip.@Tooltip": "Displays the type of exotic below the item's name if an armor piece is exotic.", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper": "Enable Accessories Helper", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[0]": "When hovering over an accessory you are informed about whether you already have it or not, and whether it's worse than what you have already collected or better. List of Statuses:", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[1]": "You have the highest tier accessory from that family.", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[2]": "This accessory is an upgrade from the one in the same family that you already have. Also shows you what tier this accessory is in it's family.", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[3]": "This accessory can be upgraded. Also tells you what tier of accessory you have in that family.", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[4]": "You already own an accessory in the same family that is better than this one. Also tells you what tier of accessory you have in that family.", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableAccessoriesHelper.@Tooltip[5]": "You don't own any accessory from this family.", "text.autoconfig.skyblocker.option.general.dungeonQuality": "Dungeon Quality", "text.autoconfig.skyblocker.option.general.dungeonQuality.@Tooltip": "Displays quality and tier of dungeon drops from mobs.\n\n\nReminder:\nTier 1-3 dropped from F1-F3\nTier 4-7 dropped from F4-F7 or M1-M4\nTier 8-10 are dropped only from M5-M7", "text.autoconfig.skyblocker.option.general.enableNewYearCakesHelper": "Enable New Year Cakes Helper", @@ -154,6 +161,7 @@ "text.autoconfig.skyblocker.option.general.searchOverlay.enableCommands.@Tooltip": "Opens the bazaar search with \"/bzs\" and auction house with \"/ahs\". A re-log is required for this setting to be updated.", "text.autoconfig.skyblocker.option.general.searchOverlay.historyLabel": "History:", "text.autoconfig.skyblocker.option.general.betterPartyFinder": "Better Party Finder", + "text.autoconfig.skyblocker.option.general.fancyCraftingTable": "Fancy Crafting Table UI", "skyblocker.itemTooltip.nullMessage": "§cItem price information on tooltip will renew in max 60 seconds. If not, check latest.log", "skyblocker.itemTooltip.noData": "§cNo Data", @@ -605,6 +613,23 @@ "skyblocker.crimson.kuudra.noArrowPoison": "No Arrow Poison!", "skyblocker.crimson.kuudra.lowArrowPoison": "Low on Arrow Poison!", + + "skyblocker.waypoints.ordered.groupNonExistent": "§cThe waypoint group %s doesn't exist.", + "skyblocker.waypoints.ordered.add.invalidHexColor": "§cInvalid HEX color code!", + "skyblocker.waypoints.ordered.addAt.success": "Added a waypoint in group %s at index %d.", + "skyblocker.waypoints.ordered.add.success": "Added a waypoint in group %s at %s.", + "skyblocker.waypoints.ordered.removeGroup.success": "Successfully removed the waypoint group %s.", + "skyblocker.waypoints.ordered.remove.success": "Successfully removed the waypoint at %s from group %s.", + "skyblocker.waypoints.ordered.removeAt.success": "Successfully removed the waypoint at index %d from group %s.", + "skyblocker.waypoints.ordered.toggle.success": "Toggled the waypoint group %s.", + "skyblocker.waypoints.ordered.export.success": "Successfully copied your waypoints to your clipboard!", + "skyblocker.waypoints.ordered.export.fail": "§cFailed to export your waypoints, check the latest.log for more information.", + "skyblocker.waypoints.ordered.import.skyblocker.success": "Successfully imported waypoints from the Skyblocker Ordered Waypoints format!", + "skyblocker.waypoints.ordered.import.skyblocker.unknownFormatHeader": "§cThis import code doesn't look like its in the Skyblocker Ordered Waypoints format, double check your clipboard to see if everything is correct.", + "skyblocker.waypoints.ordered.import.skyblocker.fail": "§cFailed to import waypoints from the Skyblocker Ordered Waypoints format. Make sure to have the waypoint data copied to your clipboard!", + "skyblocker.waypoints.ordered.import.coleWeight.groupAlreadyExists": "§cThere is already an ordered waypoints group under the name \"%s\", please choose another name to import your waypoints under.", + "skyblocker.waypoints.ordered.import.coleWeight.success": "Successfully imported waypoints from the Cole Weight format.", + "skyblocker.waypoints.ordered.import.coleWeight.fail": "§cFailed to import waypoints from the Cole Weight format. Make sure to have the waypoint data copied to your clipboard!", "emi.category.skyblocker.skyblock": "Skyblock" } diff --git a/src/main/resources/assets/skyblocker/lang/ja_jp.json b/src/main/resources/assets/skyblocker/lang/ja_jp.json index da096e8b..ece5c6f0 100644 --- a/src/main/resources/assets/skyblocker/lang/ja_jp.json +++ b/src/main/resources/assets/skyblocker/lang/ja_jp.json @@ -49,7 +49,7 @@ "text.autoconfig.skyblocker.category.messages": "メッセージ", "text.autoconfig.skyblocker.option.messages.hideAbility": "アビリティのクールダウンを非表示にする", "text.autoconfig.skyblocker.option.messages.hideHeal": "回復メッセージを非表示にする", - "text.autoconfig.skyblocker.option.messages.hideAOTE": "AOTEのメッセージを非表示にする", + "text.autoconfig.skyblocker.option.messages.hideAOTE": "テレポート能力のメッセージを非表示にする", "text.autoconfig.skyblocker.option.messages.hideImplosion": "Implosionのメッセージを非表示にする", "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "Molten Waveのメッセージを非表示にする", "text.autoconfig.skyblocker.option.messages.hideAds": "全体チャットの宣伝を非表示にする", @@ -95,7 +95,7 @@ "text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper": "フィッシングヘルパーを有効にするか", "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale": "tab HUDの要素の大きさ", "key.skyblocker.defaultTgl": "tabを開いたときに表示されるHUDをデフォルトに戻す", - "text.autoconfig.skyblocker.option.general.tabHud": "ファンシーなtab HUD", + "text.autoconfig.skyblocker.option.general.tabHud": "ファンシーなtab HUD(この機能は一時的に利用できなくなる)", "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "バニラGUIとの比 (%)で設定します", "text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled": "ファンシーなtab HUDを有効にする", "text.autoconfig.skyblocker.option.general.fairySouls": "フェアリーソウルヘルパー", @@ -124,5 +124,45 @@ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER2": "レイヤー2", "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "右側", "text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "オフ", - "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip": "バニラでもどこでも動きます。「/skyblocker shortcuts」で編集でき、使用するには少なくともこの下の設定のうちの一つが有効になっている必要があります。" + "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip": "バニラでもどこでも動きます。「/skyblocker shortcuts」で編集でき、使用するには少なくともこの下の設定のうちの一つが有効になっている必要があります。", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumInfo": "博物館について情報を表示する", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableObtainedDate": "アイテムを取得した日付を表示する", + "text.autoconfig.skyblocker.option.general.tabHud.enableHudBackground": "HUDの背景を有効にする", + "text.autoconfig.skyblocker.option.general.waypoints": "ウェイポイント", + "text.autoconfig.skyblocker.option.general.waypoints.enableWaypoints": "ウェイポイント有効にする", + "text.autoconfig.skyblocker.option.general.waypoints.waypointType": "ウェイポイントの種類", + "key.itemProtection": "アイテムを保護する", + "text.autoconfig.skyblocker.option.general.enableTips": "ヒント有効にする", + "text.autoconfig.skyblocker.option.general.wikiLookup.officialWiki": "Hypixelのwikiを使う", + "text.autoconfig.skyblocker.option.general.wikiLookup.officialWiki.@Tooltip": "Fandomのwikiではなく、Hypixelの公式wikiを使用する。", + "text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe": "三目並べのソルバー", + "text.autoconfig.skyblocker.option.locations.end": "ジ・エンド", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralColor": "中性の色", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling": "得点の大きさ", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold.@Tooltip": "この数値以下の利益は中性に見される。", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage": "Mimicメッセージ", + "text.autoconfig.skyblocker.option.locations.garden": "庭園", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.profitColor": "利益が出たときに使用される色", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.lossColor": "損失のときに使用される色", + "text.autoconfig.skyblocker.option.locations.dungeons.mapScreen": "ドンジョンの地図と得点の位置の設定…", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.true": "はい", + "text.autoconfig.skyblocker.option.quickNav.button.item.itemName": "アイテムのID", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.finish": "完成", + "text.autoconfig.skyblocker.option.locations.end.screen": "ジ・エンドのHUDの設定…", + "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "地図の大きさ", + "text.autoconfig.skyblocker.option.locations.end.resetText": "リセット", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore": "ダンジョンの得点", + "text.autoconfig.skyblocker.option.quickNav.button.item.nbt": "NBT", + "text.autoconfig.skyblocker.option.quickNav.button.item.count": "アイテムの数量", + "text.autoconfig.skyblocker.option.locations.garden.farmingHud.enableHud": "農業のHUDを有効にする", + "text.autoconfig.skyblocker.option.locations.garden.farmingHud.config": "農業のHUDの設定…", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.mimicMessage": "Mimicメッセージ", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage": "Mimicメッセージを有効にする", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.mapScaling": "地図の大きさ", + "text.autoconfig.skyblocker.option.locations.end.hudEnabled": "HUDを有効にする", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.editRule": "編集", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.false": "いいえ", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.regex": "正規表現か:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.hideMessage": "メッセージを非表示にする:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.none": "なし" } diff --git a/src/main/resources/assets/skyblocker/lang/zh_cn.json b/src/main/resources/assets/skyblocker/lang/zh_cn.json index 3451e7b2..6e3faf9c 100644 --- a/src/main/resources/assets/skyblocker/lang/zh_cn.json +++ b/src/main/resources/assets/skyblocker/lang/zh_cn.json @@ -10,7 +10,7 @@ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER1": "下排", "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER2": "上排", "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "快捷栏右侧", - "text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "禁用", + "text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "已禁用", "text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition": "生命条位置", "text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "法力条位置", "text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition": "防御条位置", @@ -34,9 +34,9 @@ "text.autoconfig.skyblocker.category.richPresence": "Discord活动状态", "text.autoconfig.skyblocker.option.richPresence.info": "Skyblock信息", "text.autoconfig.skyblocker.option.richPresence.info.PURSE": "钱包", - "text.autoconfig.skyblocker.option.richPresence.info.BITS": "点券", + "text.autoconfig.skyblocker.option.richPresence.info.BITS": "点数", "text.autoconfig.skyblocker.option.richPresence.info.LOCATION": "位置", - "text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "如果您正在循环模式,这个值将不会生效", + "text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "如果您处于循环模式,这个值将不会生效", "text.autoconfig.skyblocker.option.richPresence.cycleMode": "循环Skyblock信息", "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "已启用", "text.autoconfig.skyblocker.option.richPresence.customMessage": "自定义消息", @@ -46,8 +46,8 @@ "text.autoconfig.skyblocker.option.general.itemList.enableItemList": "启用物品列表", "text.autoconfig.skyblocker.category.locations": "位置", "text.autoconfig.skyblocker.option.locations.barn": "农业岛屿", - "text.autoconfig.skyblocker.option.locations.barn.solveHungryHiker": "Hungry Hikers 所需物品提示", - "text.autoconfig.skyblocker.option.locations.barn.solveTreasureHunter": "Treasure Hunter 助手", + "text.autoconfig.skyblocker.option.locations.barn.solveHungryHiker": "饥饿的旅行者所需物品提示", + "text.autoconfig.skyblocker.option.locations.barn.solveTreasureHunter": "宝藏猎人助手", "text.autoconfig.skyblocker.option.locations.dungeons": "地牢", "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Croesus助手", "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "将打开过的箱子标记为灰色。", @@ -61,18 +61,18 @@ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith": "首字母谜题终端助手", "text.autoconfig.skyblocker.option.locations.dwarvenMines": "矮人矿井", "text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel": "显示钻头燃料", - "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "解决Fetchur的迷题", - "text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "解决Puzzler的迷题", - "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud": "矮人矿井 HUD", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "解决Fetchur的谜题", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "解决Puzzler的谜题", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud": "矮人矿井HUD", "text.autoconfig.skyblocker.category.messages": "消息", - "text.autoconfig.skyblocker.option.messages.chatFilterResult.PASS": "禁用", + "text.autoconfig.skyblocker.option.messages.chatFilterResult.PASS": "已禁用", "text.autoconfig.skyblocker.option.messages.chatFilterResult.FILTER": "启用", "text.autoconfig.skyblocker.option.messages.chatFilterResult.ACTION_BAR": "移动到动作栏", "text.autoconfig.skyblocker.option.messages.hideAbility": "隐藏技能冷却", "text.autoconfig.skyblocker.option.messages.hideHeal": "隐藏治疗消息", "text.autoconfig.skyblocker.option.messages.hideAOTE": "隐藏传送类能力的提示消息", "text.autoconfig.skyblocker.option.messages.hideImplosion": "隐藏Implosion技能的提示消息", - "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "隐藏 Molten Wave 技能的提示消息", + "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "隐藏Molten Wave技能的提示消息", "text.autoconfig.skyblocker.option.messages.hideAds": "从公屏聊天中隐藏广告", "text.autoconfig.skyblocker.option.messages.hideTeleportPad": "隐藏传送点消息", "text.autoconfig.skyblocker.option.messages.hideCombo": "隐藏连杀消息", @@ -81,26 +81,26 @@ "text.autoconfig.skyblocker.option.messages.hideMana.@Tooltip": "已被更好的属性条代替", "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "隐藏菜单中分隔符的物品信息", "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "地图界面大小", - "skyblocker.updaterepository.failed": "§c更新本地数据存储库失败,请手动删库并重启游戏", + "skyblocker.updaterepository.failed": "§c更新本地数据存储库失败,请手动删除文件并重启游戏。", "text.autoconfig.skyblocker.option.general.fishing": "钓鱼助手", "text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper": "启用钓鱼助手", "skyblocker.fishing.reelNow": "收竿!", - "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorText": "启用真 Livid 的颜色提示", - "text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "提示真 Livid 的颜色", - "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorText.@Tooltip": "将真 Livid 的颜色发送到聊天栏。", - "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText": "真 Livid 颜色提示信息", - "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText.@Tooltip": "Livid Boss战时发送到聊天栏的信息, 字段 “[color]” 将被替换为真 Livid 的颜色", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorText": "启用真Livid的颜色提示文本", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "提示真Livid的颜色", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorText.@Tooltip": "将真Livid的颜色发送到聊天栏。", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText": "真Livid颜色提示信息文本", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText.@Tooltip": "Livid Boss战时发送到聊天栏的信息, 字段 “[color]” 将被替换为真Livid的颜色。", "key.skyblocker.defaultTgl": "将tab键所显示的列表改为默认空岛生存列表", "text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled": "启用更好的Tab HUD", - "text.autoconfig.skyblocker.option.general.tabHud": "更好的Tab HUD", + "text.autoconfig.skyblocker.option.general.tabHud": "更好的Tab HUD(在地牢外临时禁用)", "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale": "更好的Tab HUD缩放大小", - "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "相对于原版 GUI 的百分比大小", + "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "相对于原版GUI的百分比大小", "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style": "HUD风格", - "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]": "简约:仅显示委托及其进度百分比", - "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[1]": "\n精致:显示委托名,进度百分比与进度条以及图标", - "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]": "\n极简:仅在简单的方框内显示委托及其进度", - "text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice": "启用 Motes 价格显示", - "text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice.@Tooltip": "显示 Rift 中的物品售价,以 Mote 为单位。", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]": "简约:仅显示委托及其进度百分比。", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[1]": "\n精致:显示委托名,进度百分比与进度条以及图标。", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]": "\n极简:仅在简单的方框内显示委托及其进度。", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice": "启用Motes价格显示", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice.@Tooltip": "显示Rift中的物品售价,以Mote为单位。", "text.autoconfig.skyblocker.option.general.experiments": "实验台助手", "text.autoconfig.skyblocker.option.general.experiments.enableSuperpairsSolver": "启用超级配对实验助手", "text.autoconfig.skyblocker.option.general.experiments.enableChronomatronSolver": "启用序列记忆实验助手", @@ -109,31 +109,31 @@ "text.autoconfig.skyblocker.option.general.fairySouls": "仙女之魂助手", "text.autoconfig.skyblocker.option.general.fairySouls.enableFairySoulsHelper": "启用仙女之魂助手", "skyblocker.fairySouls.markAllFound": "§r将当前岛屿上的全部仙女之魂标记为已发现", - "text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency.@Tooltip": "值越小,更新越频繁(可能会导致卡顿)", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency.@Tooltip": "值越小,更新越频繁,可能会导致卡顿。", "text.autoconfig.skyblocker.option.general.shortcuts": "更精简的命令", "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip": "使用“/skyblocker shortcuts”以编辑命令的精简版本, 以下选项必须至少启用之一方可有效(此功能在空岛生存外也可使用)。", "text.autoconfig.skyblocker.option.general.teleportOverlay": "传送类技能目标位置显示", "text.autoconfig.skyblocker.option.general.teleportOverlay.enableTeleportOverlays": "启用传送类技能目标位置显示", - "text.autoconfig.skyblocker.option.general.teleportOverlay.enableWeirdTransmission": "启用 Weird Transmission 目标位置显示", - "text.autoconfig.skyblocker.option.general.teleportOverlay.enableInstantTransmission": "启用 Instant Transmission 技能目标位置显示", - "text.autoconfig.skyblocker.option.general.teleportOverlay.enableEtherTransmission": "启用 Ether Transmission 技能目标位置显示", - "text.autoconfig.skyblocker.option.general.teleportOverlay.enableSinrecallTransmission": "启用 Sinrecall Transmission 技能目标位置显示", - "text.autoconfig.skyblocker.option.general.teleportOverlay.enableWitherImpact": "启用 Wither Impact 目标位置显示", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableWeirdTransmission": "启用Weird Transmission目标位置显示", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableInstantTransmission": "启用Instant Transmission技能目标位置显示", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableEtherTransmission": "启用Ether Transmission技能目标位置显示", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableSinrecallTransmission": "启用Sinrecall Transmission技能目标位置显示", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableWitherImpact": "启用Wither Impact目标位置显示", "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts": "启用更精简的命令", "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts": "启用命令别名", "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts": "启用带参命令别名", "text.autoconfig.skyblocker.option.general.tabHud.nameSorting": "玩家名排列顺序", - "text.autoconfig.skyblocker.option.general.etherwarpOverlay": "Etherwarp 技能目标位置显示", - "text.autoconfig.skyblocker.option.locations.dungeons.blazeSolver.@Tooltip": "以绿色边框标记正确的烈焰人,并将下一个烈焰人以白色线条与边框一同标记.", + "text.autoconfig.skyblocker.option.general.etherwarpOverlay": "Etherwarp技能目标位置显示", + "text.autoconfig.skyblocker.option.locations.dungeons.blazeSolver.@Tooltip": "以绿色边框标记正确的烈焰人,并将下一个烈焰人以白色线条与边框一同标记。", "text.autoconfig.skyblocker.option.general.tabHud.plainPlayerNames.@Tooltip": "开启后在公共岛屿显示玩家名时不显示任何特殊效果。", "text.autoconfig.skyblocker.option.messages.hideShowOff.@Tooltip": "过滤来自 /show 命令的消息", "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow": "使星标怪物发光", - "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow.@Tooltip": "将玩家可见的星标怪物高亮", + "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow.@Tooltip": "为玩家可见的星标怪物添加发光效果。", "text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe": "井字棋谜题助手", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints": "地牢秘密路径点", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSecretWaypoints": "启用地牢秘密路径点", - "text.autoconfig.skyblocker.option.general.tabHud.nameSorting.@Tooltip": "\"Alphabetical\" 以词典序排列玩家, 而 \"Default\" 以Hypixel默认顺序排列", - "text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe.@Tooltip": "以红色方块标记井字棋的下一步!", + "text.autoconfig.skyblocker.option.general.tabHud.nameSorting.@Tooltip": "“Alphabetical”以词典序排列玩家,而“Default”以Hypixel默认顺序排列", + "text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe.@Tooltip": "以红色方块标记井字棋的下一步!", "text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarningInDungeons": "在地牢内启用箭袋提示", "text.autoconfig.skyblocker.option.general.quiverWarning": "箭袋提示", "text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarning": "启用箭袋提示", @@ -144,18 +144,18 @@ "text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls": "仅高亮附近的仙女之魂", "text.skyblocker.quit_discard": "不保存退出", "text.skyblocker.quit_config": "修改未保存", - "text.skyblocker.quit_config_sure": "确定退出吗? 你的修改将不会被保存!", - "text.autoconfig.skyblocker.option.general.wikiLookup": "查阅 wiki", - "text.autoconfig.skyblocker.option.general.wikiLookup.officialWiki": "使用Hypixel官方 Wiki", - "text.autoconfig.skyblocker.option.general.wikiLookup.officialWiki.@Tooltip": "在查阅时使用Hypixel 官方 wiki 代替 Fandom wiki", + "text.skyblocker.quit_config_sure": "确定退出吗?你的修改将不会被保存!", + "text.autoconfig.skyblocker.option.general.wikiLookup": "查阅wiki", + "text.autoconfig.skyblocker.option.general.wikiLookup.officialWiki": "使用Hypixel官方维基", + "text.autoconfig.skyblocker.option.general.wikiLookup.officialWiki.@Tooltip": "在查阅时使用Hypixel官方维基代替Fandom维基。", "text.autoconfig.skyblocker.option.general.itemTooltip.enableObtainedDate": "显示获取时间", - "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts.@Tooltip": "此功能可用较短的别名替换较长的原始命令,使用 \"/skyblocker shortcuts\" 以编辑. (“更精简的命令”功能必须启用才可生效)", + "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts.@Tooltip": "此功能可用较短的别名替换较长的原始命令,使用“/skyblocker shortcuts”以编辑。 (“更精简的命令”功能必须启用才可生效)", "text.autoconfig.skyblocker.option.general.waypoints": "路径点", "text.autoconfig.skyblocker.option.general.waypoints.enableWaypoints": "启用路径点", "text.autoconfig.skyblocker.option.general.waypoints.waypointType": "路径点类型", "text.skyblocker.open": "开启", - "text.autoconfig.skyblocker.option.general.wikiLookup.enableWikiLookup": "启用 wiki 查阅功能", - "text.autoconfig.skyblocker.option.general.wikiLookup.enableWikiLookup.@Tooltip": "指针悬停于物品之下按 F4 键打开 wiki 相关条目", + "text.autoconfig.skyblocker.option.general.wikiLookup.enableWikiLookup": "启用wiki查阅功能", + "text.autoconfig.skyblocker.option.general.wikiLookup.enableWikiLookup.@Tooltip": "指针悬停于物品上,按F4键打开wiki中的条目。", "text.autoconfig.skyblocker.option.general.mythologicalRitual": "神话仪式助手", "text.autoconfig.skyblocker.option.general.mythologicalRitual.enableMythologicalRitualHelper": "开启神话仪式助手", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enablePearlWaypoints": "启用珍珠路径点", @@ -163,7 +163,7 @@ "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableFairySoulWaypoints": "启用仙女之魂路径点", "text.autoconfig.skyblocker.option.locations.spidersDen.relics": "遗物助手", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreMessage": "启用地牢 %d 分数提示信息", - "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreMessage.@Tooltip": "在地牢中达到 %d 分时发送提示信息", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreMessage.@Tooltip": "在地牢中达到 %d 分时发送提示信息。", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableEntranceWaypoints": "启用入口路径点", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableChestWaypoints": "启用箱子路径点", "text.autoconfig.skyblocker.option.general.itemCooldown": "物品冷却提示", @@ -173,13 +173,13 @@ "text.autoconfig.skyblocker.option.locations.spidersDen.relics.enableRelicsHelper": "启用遗物助手", "text.autoconfig.skyblocker.option.locations.spidersDen.relics.highlightFoundRelics": "高亮已发现的遗物", "text.autoconfig.skyblocker.option.general.compactorDeletorPreview": "启用个人压缩器/删除器预览", - "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip": "将物品背景颜色改为其稀有度所对应的颜色", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip": "将物品背景颜色改为其稀有度所对应的颜色。", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore": "地牢分数", - "skyblocker.wikiLookup.noArticleFound": "§r无法找到此物品的 wiki 相关条目...", + "skyblocker.wikiLookup.noArticleFound": "§r无法找到此物品的维基条目...", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableWitherWaypoints": "启用凋灵精粹路径点", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableBatWaypoints": "启用蝙蝠路径点", "text.autoconfig.skyblocker.option.general.betterPartyFinder": "更好的组队查找", - "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableRoomMatching.@Tooltip": "关闭此选项可节省约20MB左右的内存,但是秘密路径点和§l部分谜题助手功能§r需要启用该选项", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableRoomMatching.@Tooltip": "关闭此选项可节省约20MB左右的内存,但是秘密路径点和§l部分谜题助手功能§r需要启用该选项。", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableItemWaypoints": "启用物品路径点", "text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarningAfterDungeon": "在地牢结束后启用箭袋提示", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableLeverWaypoints": "启用拉杆路径点", @@ -195,7 +195,7 @@ "key.itemProtection": "保护物品", "text.autoconfig.skyblocker.option.general.chestValue.enableChestValue.@Tooltip": "在容器中添加一个计算其价值的按钮。", "text.autoconfig.skyblocker.option.general.chestValue.color": "箱子价值颜色", - "text.autoconfig.skyblocker.option.general.chestValue.enableChestValue": "启用箱子价值计算", + "text.autoconfig.skyblocker.option.general.chestValue.enableChestValue": "启用箱子价值计算器", "text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects": "稀有地牢掉落效果", "text.autoconfig.skyblocker.option.general.specialEffects": "特效", "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight.doorHighlightType.secretWaypointsNote": "\n\n\n注意:若使此功能生效,必须启用地牢秘密路径点。", @@ -211,14 +211,14 @@ "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundStyle.@Tooltip": "选择圆形或方形背景样式!", "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorGlow": "启用Livid高亮", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.pearlWaypoints": "末影珍珠路径点", - "text.autoconfig.skyblocker.option.locations.dungeons.creeperSolver": "解决苦力怕激光谜题", + "text.autoconfig.skyblocker.option.locations.dungeons.creeperSolver": "苦力怕光束谜题助手", "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight.enableDoorHighlight": "启用门的高亮", "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight": "高亮门", "text.autoconfig.skyblocker.option.general.titleContainer.@Tooltip": "用于同时显示多个标题,例如:吸血鬼杀手", "text.autoconfig.skyblocker.option.locations.end": "终末之地", "text.autoconfig.skyblocker.option.locations.end.hudEnabled": "启用HUD", "text.autoconfig.skyblocker.option.general.flameOverlay.flameOpacity": "火焰不透明度", - "text.autoconfig.skyblocker.option.general.flameOverlay": "火焰叠加", + "text.autoconfig.skyblocker.option.general.flameOverlay": "火焰效果动画修改", "text.autoconfig.skyblocker.option.general.flameOverlay.flameHeight": "火焰高度", "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage": "启用Mimic消息", "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage": "Mimic消息", @@ -270,13 +270,13 @@ "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints": "水晶洞窟路径点", "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.enabled": "启用路径点", "text.autoconfig.skyblocker.option.general.waypoints.waypointType.@Tooltip": "路径点:显示高亮和信标光束。\n\n边框路径点:显示路径点和边框。\n\n高亮:只显示高亮。\n\n边框高亮:显示高亮和边框。\n\n边框:只显示边框。", - "text.autoconfig.skyblocker.option.general.searchOverlay": "搜索覆盖", + "text.autoconfig.skyblocker.option.general.searchOverlay": "搜索界面", "text.autoconfig.skyblocker.option.general.searchOverlay.enableBazaar": "为Bazaar启用", - "text.autoconfig.skyblocker.option.general.searchOverlay.enableBazaar.@Tooltip": "在Bazaar中搜索时显示自定义搜索覆盖。", + "text.autoconfig.skyblocker.option.general.searchOverlay.enableBazaar.@Tooltip": "在Bazaar中搜索时显示自定义搜索界面。", "text.autoconfig.skyblocker.option.general.searchOverlay.enableAuctionHouse": "为Auction House启用", - "text.autoconfig.skyblocker.option.general.searchOverlay.enableAuctionHouse.@Tooltip": "在Auction House中搜索时显示自定义搜索覆盖。", + "text.autoconfig.skyblocker.option.general.searchOverlay.enableAuctionHouse.@Tooltip": "在Auction House中搜索时显示自定义搜索界面。", "text.autoconfig.skyblocker.option.general.searchOverlay.keepPreviousSearches": "保留上次搜索结果", - "text.autoconfig.skyblocker.option.general.searchOverlay.keepPreviousSearches.@Tooltip": "打开叠加层时保留现有搜索队列。", + "text.autoconfig.skyblocker.option.general.searchOverlay.keepPreviousSearches.@Tooltip": "打开界面时保留已有的搜索记录。", "text.autoconfig.skyblocker.option.general.searchOverlay.maxSuggestions": "最大建议数", "text.autoconfig.skyblocker.option.general.searchOverlay.maxSuggestions.@Tooltip": "要显示的建议项目的最大数量。", "text.autoconfig.skyblocker.option.general.searchOverlay.historyLength": "保存的搜索历史长度", @@ -320,11 +320,11 @@ "skyblocker.tips.protectItem": "使用 /skyblocker protectItem 防止意外掉落物品。", "skyblocker.customItemNames.removed": "§f移除该物品的自定义名称。", "skyblocker.partyFinder.loadingError": "如果你看到此内容超过5秒,可能是出现了什么错误……", - "skyblocker.partyFinder.partyCard.minClassLevel": "最低职业要求:%d", + "skyblocker.partyFinder.partyCard.minClassLevel": "最低职业等级要求:%d", "skyblocker.quiverWarning.10Left": "你的箭袋里只剩10支箭了!", "skyblocker.tips.enabled": "§a启用提示。", "skyblocker.tips.tip": "§a提示:%s\n", - "skyblocker.partyFinder.tabs.partyFinder": "组队查找器", + "skyblocker.partyFinder.tabs.partyFinder": "队伍查找器", "text.autoconfig.skyblocker.option.messages.chatRules": "定义聊天规则", "skyblocker.partyFinder.error.name": "队伍查找器错误!", "skyblocker.partyFinder.error.message": "出现错误,将自动转回原版的队伍查找器", @@ -336,11 +336,11 @@ "skyblocker.quiverWarning.empty": "你的箭袋空了!", "skyblocker.shortcuts.notLoaded": "§c§l精简命令尚未加载完毕", "skyblocker.shortcuts.command.target": "目标命令", - "text.autoconfig.skyblocker.option.slayer.endermanSlayer": "[测试功能] 末影人杀手", + "text.autoconfig.skyblocker.option.slayer.endermanSlayer": "末影人杀手", "text.autoconfig.skyblocker.option.slayer.vampireSlayer.effigyUpdateFrequency.@Tooltip": "值越低,更新越频繁,可能会导致卡顿。", "skyblocker.shortcuts.commandArg.target": "目标命令参数", "skyblocker.tips.clickNextTip": "§a[点击查看下一条提示]", - "skyblocker.tips.modMenuUpdate": "ModMenu 会通知你是否有适用于你的游戏版本的 Skyblocker 更新。", + "skyblocker.tips.modMenuUpdate": "ModMenu会通知你是否有适用于你的游戏版本的Skyblocker更新。", "skyblocker.partyFinder.partyCard.minDungeonLevel": "最低地牢等级要求:%d", "skyblocker.partyFinder.noParties": "未找到队伍。哭哭:(", "skyblocker.end.hud.location": "位置:%s", @@ -411,35 +411,35 @@ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.croesusProfit": "Croesus宝箱利润计算器", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.croesusProfit.@Tooltip": "高亮显示NPC Croesus处利润最高的宝箱。\n\n\n利润最高的宝箱将用绿色标识。\n如果有宝箱值得使用地牢钥匙,将使用黄色标识", "skyblocker.dungeons.secrets.markSecretFound": "§r将秘密 #%d 标记为已找到。", - "text.autoconfig.skyblocker.option.locations.dungeons.solveSilverfish": "解决蠹虫谜题", - "text.autoconfig.skyblocker.option.locations.dungeons.solveIceFill": "解决冰块填充谜题", + "text.autoconfig.skyblocker.option.locations.dungeons.solveSilverfish": "蠹虫谜题助手", + "text.autoconfig.skyblocker.option.locations.dungeons.solveIceFill": "冰块填充谜题助手", "text.autoconfig.skyblocker.option.locations.dungeons.floor3GuardianHealthDisplay": "守卫者生命值显示(F3/M3)", "text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard.@Tooltip": "单击带有绿色框的控制杆来解决谜题。", - "text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard": "解决水流引导谜题", + "text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard": "水流引导谜题助手", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.supplyWaypoints": "补给路径点", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.suppliesAndFuelWaypointType": "补给/燃料路径点类型", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.fuelWaypoints": "燃料路径点", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.safeSpotWaypoints": "安全点路径", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.noArrowPoisonWarning": "箭毒耗尽警告", - "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet.@Tooltip": "启用后,如果您使用 kismet,则将从利润中减去 kismet 的价格", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet.@Tooltip": "启用后,如果您使用kismet,则将从利润中减去kismet的价格", "text.autoconfig.skyblocker.option.general.titleContainer": "标题容器", "text.autoconfig.skyblocker.option.general.titleContainer.titleContainerScale": "标题容器缩放", "skyblocker.dungeons.secrets.markSecretMissing": "§r将秘密 #%d 标记为已忽略。", - "skyblocker.dungeons.secrets.physicalEntranceNotFound": "§c未找到地下城入口房间坐标。请返回绿色的入口房间。", + "skyblocker.dungeons.secrets.physicalEntranceNotFound": "§c未找到地牢入口房间坐标。请返回绿色的入口房间。", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.noArrowPoisonWarning.@Tooltip": "在使用弓箭但没有箭毒时发出警告。只在DPS阶段有效。", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.arrowPoisonThreshold": "箭毒警告阈值", "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.arrowPoisonThreshold.@Tooltip": "当背包中箭毒的数量低于设定的值时收到警告。", - "text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency.@Tooltip": "值越低,更新越频繁,可能会导致高延迟。", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency.@Tooltip": "值越低,更新越频繁,可能会导致卡顿。", "text.autoconfig.skyblocker.option.locations.dungeons.floor3GuardianHealthDisplay.@Tooltip": "在F3/M3的Boss战中,在守卫者下方显示它们的生命值。", "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSuperboomWaypoints": "启用爆破路径点", "skyblocker.crimson.kuudra.lowArrowPoison": "箭毒即将耗尽!", "skyblocker.crimson.kuudra.noArrowPoison": "箭毒已耗尽!", - "text.autoconfig.skyblocker.option.locations.dungeons.creeperSolver.@Tooltip": "高亮显示最合适的光线路径与需要点击的目标。", + "text.autoconfig.skyblocker.option.locations.dungeons.creeperSolver.@Tooltip": "高亮显示最合适的光束路径与需要点击的目标。", "text.autoconfig.skyblocker.option.general.titleContainer.config": "标题容器位置设置", "skyblocker.dungeons.secrets.markSecretMissingUnable": "§c无法将秘密 #%d 标记为已忽略。", "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.mapScaling": "地图缩放", "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts.@Tooltip": "精简命令是替换具有多个单词/参数的命令中一个或多个单词/参数的快捷方式。使用“/skyblocker shortcuts”编辑快捷方式。必须启用快捷方式才能生效。", - "text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder": "解决推箱子谜题", + "text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder": "推箱子谜题助手", "text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder.@Tooltip": "绘制路线并高亮按钮", "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.mimicMessage.@Tooltip": "杀死Mimic后将在聊天中发送的消息。建议保留默认值。", "skyblocker.dungeons.secrets.markSecretFoundUnable": "§c无法将秘密 #%d 标记为已找到。", @@ -448,5 +448,71 @@ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote": "\n\n\n注意:仅当skyblock设置中启用了死亡消息时,此功能才会生效。如果想隐藏死亡消息,请使用此模组的隐藏玩家死亡消息设置来允许进一步处理死亡消息。", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling": "分数缩放", "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator.@Tooltip": "在宝箱屏幕的标题中显示地牢宝箱的利润。\n如果获利则显示绿色。\n如果亏损则为红色。\n灰色表示没有获利也没有亏损。\n如果计算基于不完整的数据,则显示为蓝色。", - "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet": "包含 Kismet 价格" + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet": "包含Kismet价格", + "text.autoconfig.skyblocker.option.locations.rift.enigmaSoulWaypoints": "启用Enigma之魂路径点", + "text.autoconfig.skyblocker.option.locations.rift.enigmaSoulWaypoints.@Tooltip": "注意:许多Enigma之魂需要一些前置任务方能获得,所以建议参考网络视频攻略。", + "text.autoconfig.skyblocker.option.slayer.endermanSlayer.enableYangGlyphsNotification": "启用Yang Glyphs提醒", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.ballistaBuildWaypoints": "弩炮建造点路径点", + "text.autoconfig.skyblocker.option.locations.rift.highlightFoundEnigmaSouls": "高亮已找到的Enigma之魂", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer": "吸血鬼杀手", + "text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer": "开火冻结计时器(F3/M3)", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold": "中性的临界点", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold.@Tooltip": "低于这个数值的利润将被视为中性。", + "text.autoconfig.skyblocker.option.general.hideStatusEffectOverlay": "隐藏状态效果动画", + "skyblocker.tips.fancyTabExtraInfo": "你知道吗?在拿着N或M时可以在精致菜单中看到额外的信息。", + "text.autoconfig.skyblocker.option.locations.dungeons.mapScreen": "地牢地图和分数的位置设置...", + "text.autoconfig.skyblocker.option.quickNav.button": "按钮 %d", + "text.autoconfig.skyblocker.option.quickNav.button.uiTitle": "UI标题", + "text.autoconfig.skyblocker.option.general.flameOverlay.flameHeight.@Tooltip": "100% 默认高度\n0% 完全关闭", + "text.autoconfig.skyblocker.option.general.flameOverlay.flameOpacity.@Tooltip": "100% 默认透明度\n0% 完全关闭", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.screen": "水晶洞窟地图位置设置…", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.@Tooltip": "以方块显示水晶洞窟的重要区域,如丛林神庙和仙女石窟。", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enablePearlWaypoints.@Tooltip": "通过这些路径点,你可以通过同时使用末影珍珠和AOTV实现穿墙。", + "text.autoconfig.skyblocker.option.locations.dungeons.allowDroppingProtectedItems.@Tooltip": "允许在地牢中通过受/skyblocker protectItem命令保护的物品施放职业能力。", + "text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer.@Tooltip": "在F3/M3的Boss战中使用受到开火冻结影响的武器时显示计时器。", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.enabled.@Tooltip": "为丛林神庙和仙女石窟等水晶洞窟中的重要区域添加路径点(在常规/路径点中所选择的路径点)。 ", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.replace.@Tooltip": "输入要输出的新消息(可以使用Minecraft颜色代码进行格式化)。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.zombie": "僵尸", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.crit": "暴击", + "text.autoconfig.skyblocker.option.slayer.endermanSlayer.highlightNukekubiHeads": "头颅高亮", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence": "包括精粹价格", + "text.autoconfig.skyblocker.option.general.tabHud.enableHudBackground": "启用HUD背景", + "text.autoconfig.skyblocker.option.general.tabHud.enableHudBackground.@Tooltip": "为非TAB的HUD启用背景。", + "text.autoconfig.skyblocker.option.locations.garden.farmingHud.enableHud": "启用耕作HUD", + "text.autoconfig.skyblocker.option.locations.garden.farmingHud.config": "耕作HUD设置...", + "text.autoconfig.skyblocker.option.locations.garden.visitorHelper": "访客助手", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.locationSize": "方块尺寸", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.locationSize.@Tooltip": "在地图上使用的方块尺寸。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.amethyst": "紫水晶", + "skyblocker.customDyeColors.invalidHex": "§c无效的十六进制颜色代码!", + "skyblocker.customDyeColors.unableToSetColor": "§c无法设置自定义染料颜色:( (请检查自己是否在游玩空岛生存,以及是否手持物品。)", + "skyblocker.customDyeColors.added": "§f为你当前所持物品设置自定义染料颜色!", + "skyblocker.rift.enigmaSouls.markAllMissing": "§r将所有Enigma之魂标记为已忽略!", + "skyblocker.customArmorTrims.noItemUuid": "§c你必须手持一个有uuid的物品才能为其设置自定义护甲装饰!", + "skyblocker.tips.flameOverlay": "感觉火焰动画占据了太多屏幕空间?检查设置把它调小", + "skyblocker.customDyeColors.neverHad": "§f该物品还没有自定义染料颜色,为什么不设置一个呢?;)", + "text.autoconfig.skyblocker.option.general.waypoints.waypointType.generalNote": "\n\n\n该选项不会对所有路径点都生效。地牢秘密路径点等有它们自己的设置。", + "skyblocker.customDyeColors.removed": "§f删除了该物品的自定义染料颜色。", + "skyblocker.customDyeColors.notDyeable": "§c该物品不是可染色的护甲!", + "skyblocker.customArmorTrims.neverHad": "§f该物品还没有自定义护甲装饰,为什么不设置一个呢?;)", + "skyblocker.customArmorTrims.added": "§f为你当前所持物品设置自定义护甲装饰!", + "skyblocker.customArmorTrims.unableToSetTrim": "§c无法设置自定义护甲装饰:( (请检查自己是否在游玩空岛生存,以及是否手持物品。)", + "skyblocker.customArmorTrims.removed": "§f移除了该物品的自定义护甲装饰。", + "text.autoconfig.skyblocker.category.slayer": "杀手", + "text.autoconfig.skyblocker.option.messages.hideToggleSkyMall.@Tooltip": "隐藏那些在你想要保持开启Sky Mall技能时不断提醒你关闭的烦人信息!", + "skyblocker.rift.enigmaSouls.markAllFound": "§r将所有Enigma之魂标记为已找到!", + "skyblocker.customDyeColors.noItemUuid": "§c你必须手持一个有uuid的物品才能为其设置自定义染料颜色!", + "skyblocker.customArmorTrims.invalidMaterialOrPattern": "§c你提供的是无效的材料或无效的装饰图案!", + "text.autoconfig.skyblocker.option.general.searchOverlay.historyLength.@Tooltip": "界面显示的搜索历史的最大条数。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.cave": "洞窟", + "text.autoconfig.skyblocker.option.locations.garden.lockMouseTool": "当手持农具时锁定镜头", + "text.autoconfig.skyblocker.option.locations.garden.lockMouseGround": "仅当在地面上时锁定镜头", + "skyblocker.garden.hud.mouseLocked": "鼠标已锁定。", + "text.autoconfig.skyblocker.option.messages.chatRules.announcementLength": "告示显示时长", + "text.autoconfig.skyblocker.option.messages.chatRules.announcementLength.@Tooltip": "显示公告的时间长度,以游戏刻为单位。", + "text.autoconfig.skyblocker.option.messages.chatRules.announcementScale": "告示尺寸", + "text.autoconfig.skyblocker.option.messages.chatRules.announcementScale.@Tooltip": "告示的缩放级别。", + "text.autoconfig.skyblocker.option.general.enableNewYearCakesHelper": "启用新年蛋糕助手", + "text.autoconfig.skyblocker.option.general.enableNewYearCakesHelper.@Tooltip": "以绿色高亮显示缺少的新年蛋糕、红色高亮显示已有的蛋糕。\n\n需要至少打开一次蛋糕背包才能生效。", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer.maniaUpdateFrequency.@Tooltip": "值越低,更新越频繁,可能会导致卡顿。" } diff --git a/src/main/resources/assets/skyblocker/lang/zh_tw.json b/src/main/resources/assets/skyblocker/lang/zh_tw.json index 8607bdce..a0d06732 100644 --- a/src/main/resources/assets/skyblocker/lang/zh_tw.json +++ b/src/main/resources/assets/skyblocker/lang/zh_tw.json @@ -8,12 +8,12 @@ "key.wikiLookup": "查閱Wiki", "key.hotbarSlotLock": "鎖定快捷欄", "key.categories.skyblocker": "Skyblocker", - "text.autoconfig.skyblocker.option.messages.hideAOTE": "隱藏終焉之貌的提示訊息", + "text.autoconfig.skyblocker.option.messages.hideAOTE": "隱藏傳送類能力的提示訊息", "text.autoconfig.skyblocker.option.messages.hideAds": "从公頻訊息中隱藏廣告", "text.autoconfig.skyblocker.option.messages.hideTeleportPad": "隱藏傳送點訊息", "text.autoconfig.skyblocker.option.messages.hideCombo": "隱藏連殺訊息", "text.autoconfig.skyblocker.option.messages.hideAutopet": "隱藏自動寵物訊息", - "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "隱藏選單玻璃物品資訊\"", + "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "隱藏選單玻璃物品資訊", "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "快捷欄右側", "text.autoconfig.skyblocker.option.messages.hideAbility": "隱藏技能冷卻", "text.autoconfig.skyblocker.option.messages.hideHeal": "隱藏治療訊息", @@ -37,17 +37,17 @@ "text.autoconfig.skyblocker.category.quickNav": "快速導航", "text.autoconfig.skyblocker.option.quickNav.enableQuickNav": "啟用快速導航", "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "啟用 Dungeon 地圖", - "text.autoconfig.skyblocker.option.locations.barn.solveTreasureHunter": "Treasure Hunter 助手", - "text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel": "顯示電鑽燃料", + "text.autoconfig.skyblocker.option.locations.barn.solveTreasureHunter": "寶藏獵人助手", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel": "顯示鑽頭燃料", "text.autoconfig.skyblocker.option.locations.barn": "農業島嶼", - "text.autoconfig.skyblocker.option.locations.barn.solveHungryHiker": "Hungry Hikers 所需物品提示", - "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "隱藏 Molten Wave 技能的提示訊息", - "text.autoconfig.skyblocker.option.messages.hideImplosion": "隱藏內爆技能的提示訊息", + "text.autoconfig.skyblocker.option.locations.barn.solveHungryHiker": "飢餓的旅行者所需物品提示", + "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "隱藏Molten Wave技能的提示訊息", + "text.autoconfig.skyblocker.option.messages.hideImplosion": "隱藏Implosion技能的提示訊息", "text.autoconfig.skyblocker.option.richPresence.info.PURSE": "錢包", "text.autoconfig.skyblocker.option.richPresence.info.LOCATION": "位置", "text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "如果您正在循環模式,這個值將不會生效", "text.autoconfig.skyblocker.option.richPresence.customMessage": "自訂訊息", - "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "Fetchur 所需物品提示", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "解開Fetchur的謎題", "text.autoconfig.skyblocker.option.messages.chatFilterResult.ACTION_BAR": "移至動作欄", "text.autoconfig.skyblocker.option.messages.chatFilterResult.PASS": "關閉", "text.autoconfig.skyblocker.option.messages.chatFilterResult.FILTER": "啟用", @@ -57,5 +57,460 @@ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.THREE_DAY": "三日內均價", "text.autoconfig.skyblocker.option.general.itemTooltip.avg.BOTH": "同時顯示", "text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN": "顯示最低BIN(立即購買)價格", - "text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice": "顯示集市購買/賣出價格" + "text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice": "顯示集市購買/賣出價格", + "skyblocker.tips.clickNextTip": "§a[點擊看下一則提示]", + "text.autoconfig.skyblocker.option.general.acceptReparty": "自動接受重新組隊", + "text.autoconfig.skyblocker.option.general.fairySouls": "仙女之魂助手", + "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "相對於原版GUI的百分比大小", + "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts": "啟用快捷指令", + "text.autoconfig.skyblocker.option.general.fishing": "釣魚助手", + "key.skyblocker.toggleB": "將HUD分頁切換至顯示屏B", + "key.skyblocker.toggleA": "將HUD分頁切換至顯示屏A", + "text.autoconfig.skyblocker.option.quickNav.button.item.itemName": "物品ID", + "text.autoconfig.skyblocker.option.quickNav.button.item": "物品", + "text.autoconfig.skyblocker.option.quickNav.button.item.nbt": "NBT", + "text.autoconfig.skyblocker.option.quickNav.button.uiTitle": "UI標題", + "text.autoconfig.skyblocker.option.quickNav.button.item.count": "物品數量", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.pearlWaypoints": "末影珍珠路徑點", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style": "HUD風格", + "text.autoconfig.skyblocker.option.locations.dungeons.terminals": "終端助手", + "text.autoconfig.skyblocker.option.locations.end.resetName": "重置已儲存的終界統計數據", + "text.autoconfig.skyblocker.option.messages.hideToggleSkyMall.@Tooltip": "隱藏那些在你想要保持開啟Sky Mall技能時不斷提醒你關閉的煩人訊息!", + "text.autoconfig.skyblocker.option.messages.hideToggleSkyMall": "隱藏Sky Mall切換的訊息", + "text.autoconfig.skyblocker.option.messages.hideDicer.@Tooltip": "從聊天欄過濾Dicer訊息。", + "text.autoconfig.skyblocker.option.slayer.endermanSlayer.highlightBeacons": "信標高亮", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer": "吸血鬼殺手", + "skyblocker.end.hud.zealotsTotalKills": "總擊殺數:%d", + "skyblocker.end.hud.stage": "階段:%s", + "skyblocker.dungeons.secretsTracker.failFeedback": "§c無法計算此關卡中玩家找到的秘密數量!", + "skyblocker.dungeons.secretsTracker.feedback": "%s§f找到了%s§f個秘密。 %s", + "skyblocker.partyFinder.noParties": "未找到隊伍。 哭哭:(", + "skyblocker.partyFinder.error.name": "隊伍查找器錯誤!", + "text.autoconfig.skyblocker.option.locations.dungeons": "地牢", + "text.autoconfig.skyblocker.option.messages.chatRules": "自訂聊天規則", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.new": "新聊天規則", + "text.autoconfig.skyblocker.option.messages.chatRules.screen": "聊天欄規則配置…", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleName": "規則名稱", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.editRule": "編輯", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.editRule.@Tooltip": "開啟規則配置。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen": "聊天規則配置…", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.true": "是", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.false": "否", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.partialMatch.@Tooltip": "過濾器是否可以匹配部分聊天訊息。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.outputs": "輸出:", + "text.autoconfig.skyblocker.option.general.mythologicalRitual": "神話儀式助手", + "text.autoconfig.skyblocker.option.general.mythologicalRitual.enableMythologicalRitualHelper": "開啟神話儀式助手", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.hideMessage.@Tooltip": "從聊天中移除這則訊息。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.finish": "完成", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds": "播放音效:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.@Tooltip": "當訊息發送後播放音效。", + "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder": "排序終端助手", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enablePearlWaypoints": "啟用珍珠路徑點", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.crit": "暴擊", + "text.autoconfig.skyblocker.category.locations": "位置", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.amethyst": "紫水晶", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer.effigyUpdateFrequency.@Tooltip": "值越低,更新越頻繁,可能會導致卡頓。", + "skyblocker.shortcuts.command.replacement": "替代指令", + "skyblocker.end.hud.zealotsSinceLastEye": "自上個召喚之眼以來:%d", + "text.autoconfig.skyblocker.option.locations.dungeons.allowDroppingProtectedItems": "啟用物品掉落保護", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText.@Tooltip": "Livid Boss戰時傳送到聊天列的訊息, 字串 「[color]」 將會被替換為真Livid的顏色。", + "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts": "啟用指令別名", + "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts": "啟用含參指令別名", + "skyblocker.partyFinder.join": "點擊加入", + "text.autoconfig.skyblocker.option.messages.hideAOTE.@Tooltip": "隱藏煩人的「路徑被阻擋」訊息。", + "skyblocker.api.cache.MISS": "資料未快取!", + "skyblocker.customItemNames.removed": "§f移除該物品的自訂名稱。", + "text.autoconfig.skyblocker.option.locations.rift.enigmaSoulWaypoints": "啟用Enigma之魂路徑點", + "skyblocker.customItemNames.noItemUuid": "§c你必須手持一個有uuid的物品才能為其設定自訂名稱!", + "skyblocker.customDyeColors.removed": "§f刪除了該物品的自訂染料顏色。", + "text.autoconfig.skyblocker.option.general.wikiLookup.enableWikiLookup": "啟用wiki查閲功能", + "text.autoconfig.skyblocker.option.general.wikiLookup": "查閲wiki", + "text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker.@Tooltip": "追蹤玩家在地牢內找到的秘密數量。", + "text.autoconfig.skyblocker.option.locations.dungeons.playerSecretsTracker": "玩家解謎追蹤器", + "text.autoconfig.skyblocker.option.general.compactorDeletorPreview": "啟用個人壓縮器/刪除器預覽", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations": "顯示路徑點", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.locationSize": "方塊尺寸", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.locationSize.@Tooltip": "地圖上使用的方塊尺寸。", + "text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard": "水流引導謎題助手", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints": "水晶洞窟路徑點", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.enabled": "啟用路徑點", + "text.autoconfig.skyblocker.option.locations.rift.enigmaSoulWaypoints.@Tooltip": "注意:許多Enigma之魂需要一些前置任務方能獲得,所以建議參考網路影片攻略。", + "text.autoconfig.skyblocker.option.locations.end.resetText": "重置", + "text.autoconfig.skyblocker.option.locations.rift.highlightFoundEnigmaSouls": "高亮已找到的Enigma之魂", + "text.autoconfig.skyblocker.option.messages.hideShowOff.@Tooltip": "過濾來自 /show 指令的訊息", + "text.autoconfig.skyblocker.option.messages.hideShowOff": "隱藏炫耀訊息", + "skyblocker.customDyeColors.unableToSetColor": "§c無法設定自訂染料顏色:( (請檢查自己是否在遊玩空島生存,以及是否手持物品。)", + "skyblocker.customDyeColors.noItemUuid": "§c你必須手持一個有uuid的物品才能為其設定自訂染料顏色!", + "skyblocker.tips.customArmorTrims": "你可以使用/skyblocker custom armorTrim 為護甲設定自訂裝飾。", + "text.autoconfig.skyblocker.category.slayer": "殺手", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundStyle": "物品稀有度背景樣式", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundStyle.@Tooltip": "選擇圓形或方形背景樣式!", + "skyblocker.itemProtection.noItemUuid": "§c你必須手持一個有uuid的物品才能為其施加保護!", + "text.autoconfig.skyblocker.option.general.shortcuts.config": "設置快捷指令…", + "text.skyblocker.quit_discard": "不保存退出", + "text.skyblocker.quit_config": "更改未保存", + "text.autoconfig.skyblocker.option.slayer.endermanSlayer.enableYangGlyphsNotification": "啟用Yang Glyphs提醒", + "skyblocker.dungeons.puzzle.boulder.noSolution": "沒有找到解法!", + "skyblocker.end.hud.avgKillsPerEye": "平均每隻眼所需擊殺:%d", + "skyblocker.customDyeColors.added": "§f為你目前所持物品設定自訂染料顏色!", + "skyblocker.customArmorTrims.notAnArmorPiece": "§c該物品不是護甲!", + "skyblocker.tips.clickEnable": "§a[點擊啟用提示]", + "skyblocker.tips.clickDisable": "§a[點擊禁用提示]", + "skyblocker.tips.tip": "§a提示:%s\n", + "skyblocker.tips.shortcuts": "使用 /skyblocker shortcuts 建立和編輯指令和訊息快捷方式。", + "text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects.@Tooltip": "為地牢稀有掉落添加特殊的視覺效果!", + "text.autoconfig.skyblocker.option.general.wikiLookup.enableWikiLookup.@Tooltip": "滑鼠指針懸停在物品上,按F4鍵打開wiki中的條目。", + "text.autoconfig.skyblocker.option.general.wikiLookup.officialWiki": "使用Hypixel官方維基", + "text.autoconfig.skyblocker.option.general.wikiLookup.officialWiki.@Tooltip": "在查閲時使用Hypixel官方維基代替Fandom維基。", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorGlow.@Tooltip": "為F5/M5的真Livid啟用發光效果。", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorText": "啟用真Livid的顏色提示文本", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorGlow": "啟用Livid高亮", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableSinrecallTransmission": "啟用Sinrecall Transmission技能目標位置顯示", + "text.autoconfig.skyblocker.category.richPresence": "Discord活動狀態", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator.@Tooltip": "在寶箱畫面的標題中顯示地牢寶箱的利潤。\n如果獲利則顯示綠色。\n如果虧損則為紅色。\n灰色表示沒有獲利也沒有虧損。\n如果計算是基於不完整的數據,則顯示為藍色。", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet": "包含Kismet價格", + "text.autoconfig.skyblocker.option.slayer.endermanSlayer": "終界使者杀手", + "text.skyblocker.quit_config_sure": "確定退出嗎?你的修改將不會被保存!", + "skyblocker.tips.gallery": "瀏覽 https://hysky.de/skyblocker/gallery 查看該模組如何在遊戲中運作!", + "skyblocker.partyFinder.tabs.searchSettings": "搜尋過濾器", + "skyblocker.partyFinder.tabs.partyFinder": "隊伍查找器", + "text.autoconfig.skyblocker.option.general.fairySouls.enableFairySoulsHelper": "啟用仙女之魂助手", + "skyblocker.tips.discord": "請加入我們的Discord(https://discord.gg/aNNJHQykck)以了解 Skyblocker 的最新消息!", + "skyblocker.partyFinder.tabs.createParty": "創建隊伍", + "text.autoconfig.skyblocker.option.general.itemList": "物品列表", + "text.autoconfig.skyblocker.option.general.itemList.enableItemList": "啟用物品列表", + "text.autoconfig.skyblocker.option.general.shortcuts": "快捷指令", + "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip": "始終生效,即使在原版游戲中也是!使用「/skyblocker shortcuts」編輯快捷指令。以下選項必須至少啟用一個方可生效。", + "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale": "更好的Tab HUD縮放大小", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumInfo": "顯示博物館資訊", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableObtainedDate": "顯示獲取時間", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold.@Tooltip": "低於這個數值的利潤將被視為中性。", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumInfo.@Tooltip": "如果該物品可捐贈給博物館,則會顯示該物品在博物館中的類別。 它還會顯示一個標記,指示您是否已將該物品捐贈給您的博物館(尚不支持免費贈品)。\n\n為了獲得準確資訊,請啟用博物館API!", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold": "中性的臨界值", + "text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper": "啟用釣魚助手", + "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts.@Tooltip": "此功能可用較短的別名替換較長的原始指令,使用「/skyblocker shortcuts」以編輯。(需要啟用快捷指令功能才可生效)", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableAotvWaypoints": "啟用AOTV路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard.@Tooltip": "點擊帶有綠色框的控制桿來解決謎題。", + "text.autoconfig.skyblocker.option.locations.dungeons.solveSilverfish": "蠹蟲謎題助手", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.partialMatch": "允許部分匹配:", + "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight.enableDoorHighlight.@Tooltip": "分別用紅色和綠色高亮上鎖和解鎖的地牢大門。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.locations": "生效地點:", + "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight": "高亮門", + "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight.enableDoorHighlight": "啟用門的高亮", + "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight.doorHighlightType": "高亮類型", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.locations.@Tooltip": "過濾器生效的地點清單。 用「,」分隔各地點名稱。 如果希望它在某個位置之外的任何地方都可以工作,請使用「!」。 地點名稱:", + "text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls": "僅突顯附近的仙女之魂", + "text.autoconfig.skyblocker.option.general.fairySouls.highlightFoundSouls": "突顯已找到的仙女之魂", + "text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls.@Tooltip": "當此功能啟用時,玩家半徑50格以内的仙女之魂將被突顯", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableWitherImpact": "啟用Wither Impact技能目標位置顯示", + "key.skyblocker.defaultTgl": "將tab鍵所顯示的列表改為默認列表", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableEtherTransmission": "啟用Ether Transmission技能目標位置顯示", + "text.autoconfig.skyblocker.option.locations.end": "終界", + "text.autoconfig.skyblocker.option.slayer.endermanSlayer.highlightNukekubiHeads": "頭顱高亮", + "text.autoconfig.skyblocker.option.messages.hideMimicKill": "隱藏擊殺Mimic的消息", + "text.autoconfig.skyblocker.option.messages.hideMimicKill.@Tooltip": "過濾聊天中「Mimic dead!」和「Mimic killed!」的訊息。", + "skyblocker.dungeons.secrets.markSecretMissing": "§r將秘密 #%d 標記為已忽略。", + "skyblocker.api.cache.HIT": "該資料已被快取!\n已快取%d秒。", + "skyblocker.end.hud.location": "位置:%s", + "skyblocker.end.hud.protectorLocations.left": "左", + "skyblocker.end.hud.protectorLocations.front": "前", + "skyblocker.end.hud.protectorLocations.center": "中央", + "skyblocker.end.hud.protectorLocations.rightFront": "右前", + "skyblocker.end.hud.protectorLocations.rightBack": "右後", + "skyblocker.end.hud.protectorLocations.back": "后", + "text.autoconfig.skyblocker.option.general.flameOverlay.flameOpacity": "火焰不透明度", + "skyblocker.shortcuts.commandArg.target": "目標指令參數", + "skyblocker.shortcuts.new": "新的快捷指令", + "skyblocker.shortcuts.command.target": "目標指令", + "skyblocker.customDyeColors.neverHad": "§f該物品還沒有自訂染料顏色,為什麼不設定一個呢? ;)", + "text.autoconfig.skyblocker.option.general.flameOverlay": "火焰效果動畫修改", + "text.autoconfig.skyblocker.option.general.flameOverlay.flameHeight": "火焰高度", + "skyblocker.customArmorTrims.unableToSetTrim": "§c無法設定自訂護甲裝飾:( (請檢查自己是否在遊玩空島生存,以及是否手持物品。)", + "skyblocker.tips.itemRarityBackground": "使用配置的物品資訊顯示部分中的物品稀有度背景功能輕鬆查看物品的稀有度。", + "text.autoconfig.skyblocker.option.general.searchOverlay.enableCommands.@Tooltip": "使用「/bzs」開啟集市搜索,使用「/ahs」開啟拍賣行。 需要重新登入才能更新此設定。", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableLeverWaypoints": "啟用拉桿路徑點", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.ballistaBuildWaypoints": "弩砲建造點路徑點", + "text.autoconfig.skyblocker.option.general.searchOverlay.historyLabel": "歷史記錄:", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.fuelWaypoints": "燃料路徑點", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.suppliesAndFuelWaypointType": "補給/燃料路徑點類型", + "text.autoconfig.skyblocker.option.general.experiments": "實驗臺助手", + "text.autoconfig.skyblocker.option.general.experiments.enableChronomatronSolver": "啟用序列記憶實驗助手", + "text.autoconfig.skyblocker.option.general.experiments.enableSuperpairsSolver": "啟用超級配對實驗助手", + "text.autoconfig.skyblocker.option.general.experiments.enableUltrasequencerSolver": "啟用超級序列實驗助手", + "skyblocker.shortcuts.commandArg.replacement": "替代指令參數", + "text.autoconfig.skyblocker.option.general.chestValue": "箱子價值", + "text.autoconfig.skyblocker.option.general.chestValue.@Tooltip": "計算該容器中的物品價值。", + "text.autoconfig.skyblocker.option.general.chestValue.enableChestValue.@Tooltip": "在容器中添加一個計算其價值的按鈕。", + "text.autoconfig.skyblocker.option.general.chestValue.enableChestValue": "啟用箱子價值計算器", + "text.autoconfig.skyblocker.option.general.chestValue.incompleteColor.@Tooltip": "價格數據不完整時顯示的顔色。", + "text.autoconfig.skyblocker.option.general.chestValue.color": "箱子價值顔色", + "text.autoconfig.skyblocker.option.general.chestValue.incompleteColor": "不完整的顔色", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSuperboomWaypoints": "啟用爆破路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableWitherWaypoints": "啟用凋零精粹路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.blazeSolver.@Tooltip": "以綠色邊框標記正確的烈焰人,並將下一個烈焰人以白色線條與邊框一同標記。", + "text.autoconfig.skyblocker.option.locations.dungeons.creeperSolver.@Tooltip": "高亮顯示最適合的光束路徑與需要點擊的目標。", + "skyblocker.fishing.reelNow": "收竿!", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.arrowPoisonThreshold": "箭毒警告閾值", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableEntranceWaypoints": "啟用入口路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints": "地牢秘密路徑點", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice": "顯示Motes價格", + "skyblocker.customArmorTrims.neverHad": "§f該物品還沒有自訂護甲裝飾,為什麼不設置一個呢? ;)", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText": "真Livid顏色提示訊息文本", + "text.autoconfig.skyblocker.option.locations.dungeons.mapScreen": "地牢地圖和分數的位置設定...", + "skyblocker.partyFinder.partyCard.minClassLevel": "最低職業等級要求:%d", + "skyblocker.tips.modMenuUpdate": "ModMenu會通知你是否有適用於你的遊戲版本的Skyblocker更新。", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreMessage.@Tooltip": "在地牢中達到 %d 分時發送提示訊息。", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreTitle": "啟用地牢分數%d的標題", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreMessage": "啟用地牢 %d 分數提示訊息", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreTitle.@Tooltip": "當在地牢達到%d分數時顯示標題。", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorText.@Tooltip": "將真Livid的顏色傳送到聊天欄。", + "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor": "選色終端助手", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[1]": "\n精緻:顯示委託名,進度百分比與進度條以及圖示。", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]": "\n極簡:僅在簡單的方框內顯示委託及其進度。", + "skyblocker.tips.customArmorDyeColors": "使用 /skyblocker custom dyeColor 將自訂染料顏色應用到你的皮革護甲上", + "text.autoconfig.skyblocker.option.general.etherwarpOverlay": "Etherwarp技能目標位置顯示", + "text.autoconfig.skyblocker.option.general.tabHud.plainPlayerNames": "簡潔的玩家名稱", + "text.autoconfig.skyblocker.option.general.tabHud.nameSorting": "玩家名排列順序", + "text.autoconfig.skyblocker.option.general.tabHud.plainPlayerNames.@Tooltip": "開啟後在公共島嶼顯示玩家名稱時不顯示任何特殊效果。", + "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts.@Tooltip": "含參指令將替換具有多個詞匯/參數的指令中的一個或多個詞匯/參數。使用「/skyblocker shortcuts」編輯快捷指令。必須啟用快捷指令才能生效。", + "skyblocker.customItemNames.neverHad": "§f該物品還沒有自訂名稱,為什麼不設定一個呢? ;)", + "skyblocker.customItemNames.added": "§f為你目前所持物品設定自訂名稱!", + "text.autoconfig.skyblocker.option.general.waypoints": "路徑點", + "text.autoconfig.skyblocker.option.general.waypoints.enableWaypoints": "啟用路徑點", + "text.autoconfig.skyblocker.option.general.waypoints.waypointType.@Tooltip": "路徑點:顯示高亮和信標光束。\n\n邊框路徑點:顯示路徑點和邊框。\n\n高亮:只顯示高亮。\n\n邊框高亮:顯示高亮和邊框。\n\n邊框:只顯示邊框。", + "text.autoconfig.skyblocker.option.general.waypoints.waypointType.generalNote": "\n\n\n此選項不會對所有路徑點都生效。 地牢秘密路徑點等有它們自己的設定。", + "text.autoconfig.skyblocker.option.general.waypoints.waypointType": "路徑點類型", + "text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer": "開火凍結計時器(F3/M3)", + "text.autoconfig.skyblocker.option.locations.spidersDen": "蜘蛛巢穴", + "skyblocker.relics.markAllMissing": "§r將全部遺物標記為已忽略", + "text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects": "稀有地牢掉落效果", + "text.autoconfig.skyblocker.option.general.tabHud.nameSorting.@Tooltip": "「Alphabetical」以字典序排列玩家,而「Default」以Hypixel預設順序排列。", + "text.autoconfig.skyblocker.option.locations.spidersDen.relics.highlightFoundRelics": "突顯已發現的遺物", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSecretWaypoints": "啟用地牢秘密路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.profitColor": "獲利的顔色", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralColor": "中性的顏色", + "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "將打開過的箱子標記為灰色。", + "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow": "使星標怪物發光", + "text.autoconfig.skyblocker.option.locations.dungeons.allowDroppingProtectedItems.@Tooltip": "允許在地牢中透過受/skyblocker protectItem指令保護的物品施放職業能力。", + "text.autoconfig.skyblocker.option.general.specialEffects": "特效", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay": "物品資訊顯示", + "skyblocker.itemTooltip.noData": "§c沒有數據", + "text.autoconfig.skyblocker.option.locations.dungeons.floor3GuardianHealthDisplay": "守衛者生命值顯示(F3/M3)", + "text.autoconfig.skyblocker.option.locations.dungeons.floor3GuardianHealthDisplay.@Tooltip": "在F3/M3的Boss戰中,在守衛者下方顯示它們的生命值。", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableRoomMatching": "啟用房間匹配", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableRoomMatching.@Tooltip": "關閉此選項可節省約20MB左右的記憶體,但是秘密路徑點和§l部分謎題助手功能§r需要啟用該選項。", + "text.autoconfig.skyblocker.option.general.quiverWarning": "箭袋提示", + "text.autoconfig.skyblocker.option.general.itemCooldown": "物品冷卻提示", + "text.autoconfig.skyblocker.option.general.itemCooldown.enableItemCooldowns": "啟用物品冷卻提示", + "text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarning": "啟用箭袋提示", + "text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarningInDungeons": "在地牢内啟用箭袋提示", + "text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarningAfterDungeon": "在地牢結束後啟用箭袋提示", + "text.autoconfig.skyblocker.option.locations.spidersDen.relics": "遺物助手", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableChestWaypoints": "啟用箱子路徑點", + "skyblocker.customArmorTrims.removed": "§f移除了該物品的自訂護甲裝飾。", + "text.autoconfig.skyblocker.option.locations.spidersDen.relics.enableRelicsHelper": "啟用遺物助手", + "text.autoconfig.skyblocker.option.locations.garden": "花園", + "text.autoconfig.skyblocker.option.locations.garden.dicerTitlePrevent": "阻止Dicer顯示標題", + "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight.doorHighlightType.@Tooltip": "高亮:只顯示高亮。\n\n邊框+高亮:顯示高亮與邊框。\n\n邊框:只顯示邊框。", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreSound": "啟用地牢分數%d的音效", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableDungeonScoreSound.@Tooltip": "當在地牢達到%d分數時播放音效。", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.lossColor": "虧損的顔色", + "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith": "首字母謎題終端助手", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit": "地牢寶箱利潤計算器", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence.@Tooltip": "如果你健忘,**不建議**禁用。", + "text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow.@Tooltip": "為玩家可見的星標怪物添加發光效果。", + "text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos": "解決三個怪人的謎題", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency.@Tooltip": "值越低,更新越頻繁,可能會導致卡頓。", + "skyblocker.shortcuts.deleteWarning": "快捷指令「%s」將永遠消失! (很久!)", + "skyblocker.tips.disabled": "§a禁用提示。", + "skyblocker.customArmorTrims.added": "§f為你目前所持物品設定自訂護甲裝飾!", + "skyblocker.itemProtection.unableToProtect": "§c無法保護該物品:( (請檢查自己是否在遊玩空島生存,以及是否手持物品。)", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.showSecretText": "顯示秘密文本", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableItemWaypoints": "啟用物品路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableBatWaypoints": "啟用蝙蝠路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence": "包括精粹價格", + "skyblocker.tips.fancyTabExtraInfo": "你知道嗎? 在拿著N或M時可以在精緻菜單中看到額外的信息。", + "skyblocker.tips.beta": "我們經常在 GitHub Actions 提供測試版,其中包含新的和實驗性的功能。", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableDefaultWaypoints.@Tooltip": "包括所有未分類的路徑點。", + "skyblocker.customItemNames.unableToSetName": "§c無法設定自訂名稱:( (請檢查自己是否在遊玩空島生存,以及是否手持物品。)", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds": "物品稀有度背景", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip": "將物品背景顏色顯示為其稀有度所對應的顔色。", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundsOpacity": "物品稀有度背景不透明度", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "解開Puzzler的謎題", + "text.skyblocker.open": "開啟", + "skyblocker.dungeons.secrets.markSecretFound": "§r將秘密 #%d 標記為已找到。", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore": "地牢分數", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.dungeonScoreMessage": "地牢分數%d訊息", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.dungeonScoreMessage.@Tooltip": "當在地牢達到%d分數時,在聊天欄內發送訊息。 字串「[score]」將替換為地牢分數(%d)。", + "skyblocker.quiverWarning.50Left": "你的箭袋裡只剩50支箭了!", + "skyblocker.wikiLookup.noArticleFound": "§r無法找到此物品的維基條目...", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableFairySoulWaypoints": "啟用仙女之魂路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableStonkWaypoints": "啟用Stonk路徑點", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet.@Tooltip": "啟用後,如果您使用kismet,則將從利潤中減去kismet的價格", + "text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe": "井字棋謎題助手", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.screen": "矮人礦井HUD配置…", + "skyblocker.tips.enabled": "§a啟用提示。", + "text.autoconfig.skyblocker.option.quickNav.button": "按鈕 %d", + "text.autoconfig.skyblocker.option.general.betterPartyFinder": "更好的組隊查找", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableDefaultWaypoints": "啟用預設路徑點", + "skyblocker.fairySouls.markAllFound": "§r將目前島嶼上的全部仙女之魂標記為已發現", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD": "啟用分數狀態欄", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.@Tooltip": "在狀態欄中顯示地牢的分數。", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote": "\n\n\n注意:僅當skyblock設定中啟用了死亡訊息時,此功能才會生效。 如果想要隱藏死亡訊息,請使用此模組的隱藏玩家死亡訊息設定來允許進一步處理死亡訊息。", + "text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe.@Tooltip": "以紅色方塊標記井字棋的下一步!", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage": "Mimic訊息", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage": "啟用Mimic訊息", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage.@Tooltip": "殺死Mimic後在聊天中發送一則訊息,以供其他玩家的分數計算模組使用。", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.mimicMessage": "Mimic訊息", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "提示真Livid的顏色", + "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.mimicMessage.@Tooltip": "殺死Mimic後將在聊天中發送的訊息。 建議保留預設值。", + "text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer.@Tooltip": "在F3/M3的Boss戰中使用受到開火凍結影響的武器時顯示計時器。", + "text.autoconfig.skyblocker.option.locations.dwarvenMines": "矮人礦井", + "text.autoconfig.skyblocker.option.messages.hideDeath.@Tooltip": "過濾聊天欄中玩家死亡的訊息。", + "text.autoconfig.skyblocker.option.messages.hideDeath": "隱藏玩家死亡訊息", + "skyblocker.quiverWarning.10Left": "你的箭袋裡只剩10支箭了!", + "skyblocker.customArmorTrims.noItemUuid": "§c你必須手持有uuid的物品才能為其設定自訂護甲裝飾!", + "skyblocker.itemProtection.added": "§f你的%s已被保護! §o*你的物品感覺安全了一些:')*", + "skyblocker.itemProtection.removed": "§f你的%s將§n不再受到保護! §f :( - §l小心一點!", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.enabled": "啟用", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.screen": "水晶洞窟地圖位置設定…", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.mapScaling": "地圖縮放", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabledCommissions": "啟用任務欄", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabledPowder": "啟用粉末欄", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud": "水晶洞窟地圖", + "text.autoconfig.skyblocker.option.general.enableTips": "啟用貼士", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableTeleportOverlays": "啟用傳送類型技能目標位置顯示", + "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "已啟用", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.enabled.@Tooltip": "為叢林遺跡和仙女石窟等水晶洞窟中的重要區域添加路徑點(在一般設定/路徑點中所選擇的路徑點)。 ", + "text.autoconfig.skyblocker.option.general.searchOverlay.enableBazaar.@Tooltip": "在Bazaar中搜尋時顯示自訂搜尋介面。", + "text.autoconfig.skyblocker.option.general.searchOverlay": "搜尋介面", + "text.autoconfig.skyblocker.option.general.searchOverlay.enableBazaar": "為Bazaar啟用", + "text.autoconfig.skyblocker.option.general.searchOverlay.enableAuctionHouse": "為Auction House啟用", + "skyblocker.tips.helpCommand": "使用指令/skyblocker help,你可能會發現一些更酷炫的功能!", + "skyblocker.tips.wikiLookup": "將滑鼠懸停在某個物品上時按F4可在網頁瀏覽器中開啟其維基頁面。", + "skyblocker.tips.fairySoulsEnigmaSoulsRelics": "不知道哪裡可以找到仙女之魂、Enigma之魂或遺物? 請助手幫助你進行探索,他們會記得你已經找到了哪些靈魂。", + "skyblocker.tips.quickNav": "你可以在配置中自訂 QuickNav 按鈕。", + "skyblocker.tips.flameOverlay": "感覺火焰動畫佔據太多螢幕空間了? 檢查設定把它調小", + "text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder": "推箱子謎題助手", + "text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder.@Tooltip": "繪製路線並高亮按鈕", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorTitle": "啟用Livid顏色標題", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.findInChat": "在聊天欄中搜尋路徑點", + "text.autoconfig.skyblocker.option.general.dungeonQuality": "地牢物品品質", + "text.autoconfig.skyblocker.option.general.searchOverlay.historyLength.@Tooltip": "介面顯示的搜尋歷史的最大數量。", + "text.autoconfig.skyblocker.option.general.searchOverlay.enableCommands": "從指令打開", + "skyblocker.tips.issues": "在 https://github.com/SkyblockerMod/Skyblocker 提交錯誤報告和功能請求。", + "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColorTitle.@Tooltip": "在Livid的Boss戰中顯示Livid顏色的標題。", + "text.autoconfig.skyblocker.option.general.itemProtection": "物品保護", + "text.autoconfig.skyblocker.option.general.itemProtection.slotLockStyle": "物品欄鎖定樣式", + "text.autoconfig.skyblocker.option.general.itemProtection.slotLockStyle.@Tooltip": "選擇精美或經典的鎖定圖標。", + "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "地圖介面大小", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.supplyWaypoints": "補給路徑點", + "skyblocker.partyFinder.loadingError": "如果你看到此內容超過5秒,可能是出現了什麼錯誤…", + "skyblocker.partyFinder.yourParty": "你的隊伍", + "skyblocker.crimson.kuudra.noArrowPoison": "箭毒已耗盡!", + "skyblocker.crimson.kuudra.lowArrowPoison": "箭毒即將耗盡!", + "text.autoconfig.skyblocker.option.general.tabHud": "更好的Tab HUD(在地牢外臨時禁用)", + "text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice.@Tooltip": "顯示Rift中的物品價格,以Mote為計量單位。", + "text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled": "啟用更好的Tab HUD", + "text.autoconfig.skyblocker.option.general.teleportOverlay": "傳送類型技能目標位置顯示", + "text.autoconfig.skyblocker.option.richPresence.info": "Skyblock訊息", + "text.autoconfig.skyblocker.option.richPresence.cycleMode": "循環Skyblock訊息", + "skyblocker.updaterepository.failed": "§c更新本機資料儲存庫失敗,請手動刪除檔案並重新啟動遊戲。", + "skyblocker.dungeons.secrets.markSecretFoundUnable": "§c無法將秘密 #%d 標記為已找到。", + "skyblocker.dungeons.secrets.markSecretMissingUnable": "§c無法將秘密 #%d 標記為已忽略。", + "skyblocker.dungeons.dungeonScore.scoreText": "分數:%s", + "skyblocker.rift.enigmaSouls.markAllFound": "§r將所有Enigma之魂標記為已找到!", + "skyblocker.rift.enigmaSouls.markAllMissing": "§r將所有Enigma之魂標記為已忽略!", + "skyblocker.shortcuts.deleteQuestion": "你確定要移除這個快捷指令嗎?", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer.maniaUpdateFrequency.@Tooltip": "值越低,更新越頻繁,可能會導致卡頓。", + "text.autoconfig.skyblocker.option.general.hideStatusEffectOverlay": "隱藏狀態效果動畫", + "key.itemProtection": "保護物品", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableWeirdTransmission": "啟用Weird Transmission目標位置顯示", + "text.autoconfig.skyblocker.option.general.teleportOverlay.enableInstantTransmission": "啟用Instant Transmission技能目標位置顯示", + "text.autoconfig.skyblocker.option.general.flameOverlay.flameHeight.@Tooltip": "100% 預設高度\n0% 完全关闭", + "text.autoconfig.skyblocker.option.general.flameOverlay.flameOpacity.@Tooltip": "100% 預設透明度\n0% 完全关闭", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.noArrowPoisonWarning.@Tooltip": "使用弓箭但沒有箭毒時發出警告。 只在DPS階段有效。", + "text.autoconfig.skyblocker.option.locations.dungeons.blazeSolver": "烈焰人謎題助手", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.croesusProfit": "Croesus寶箱利潤計算器", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.croesusProfit.@Tooltip": "高亮顯示NPC Croesus處利潤最高的寶箱。\n\n\n利潤最高的寶箱將以綠色標示。\n如果有寶箱值得使用地牢鑰匙,將使用黃色標識", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.@Tooltip": "以方塊顯示水晶洞窟的重要區域,如叢林遺跡和仙女石窟。", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.findInChat.@Tooltip": "在水晶洞窟時,讀取聊天內容以查看是否有座標,並將其提取出顯示為路徑點或在地圖中標出", + "text.autoconfig.skyblocker.option.locations.end.waypoint": "終界守護者路徑點", + "text.autoconfig.skyblocker.option.messages.hideDicer": "隱藏Dicer訊息", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.hideMessage": "隱藏訊息:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.actionBar": "在操作欄中顯示:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.name": "名稱:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.name.@Tooltip": "規則的名稱。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.filter": "過濾:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.filter.@Tooltip": "與聊天訊息相符的字串/正則表示式。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.regex": "是正則表示式:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.regex.@Tooltip": "過濾器使用正則表示式還是僅字串。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.ignoreCase": "忽略大小寫:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.ignoreCase.@Tooltip": "過濾器是否大小寫敏感。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.announcement.@Tooltip": "把這訊息顯示在螢幕中間。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.replace": "替代訊息:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.replace.@Tooltip": "輸入要輸出的新訊息(可以使用Minecraft顏色代碼進行格式化)。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.none": "無", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.cave": "洞窟", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.sounds.zombie": "僵尸", + "skyblocker.dungeons.secrets.physicalEntranceNotFound": "§c未找到地牢入口房間座標。 請返回綠色的入口房間。", + "skyblocker.tips.protectItem": "使用 /skyblocker protectItem 防止意外掉落物品。", + "skyblocker.partyFinder.error.message": "出現錯誤,將自動轉回原版的隊伍查找器", + "skyblocker.fairySouls.markAllMissing": "§r將目前島嶼上的全部仙女之魂標記為已忽略", + "skyblocker.relics.markAllFound": "§r將全部遺物標記為已發現", + "skyblocker.shortcuts.config": "快捷指令配置", + "skyblocker.shortcuts.notLoaded": "§c§l快捷指令尚未載入完畢", + "skyblocker.customDyeColors.invalidHex": "§c無效的十六進位顏色代碼!", + "skyblocker.customDyeColors.notDyeable": "§c該物品不是可染色的護甲!", + "skyblocker.customArmorTrims.invalidMaterialOrPattern": "§c你提供的是無效的材料或無效的裝飾圖案!", + "text.autoconfig.skyblocker.option.general.searchOverlay.maxSuggestions": "最大建議數", + "text.autoconfig.skyblocker.option.general.searchOverlay.maxSuggestions.@Tooltip": "要顯示的搜尋建議的最大數量。", + "text.autoconfig.skyblocker.option.general.searchOverlay.enableAuctionHouse.@Tooltip": "在Auction House中搜尋時顯示自訂搜尋介面。", + "text.autoconfig.skyblocker.option.general.searchOverlay.keepPreviousSearches": "保留前次搜尋結果", + "text.autoconfig.skyblocker.option.general.searchOverlay.keepPreviousSearches.@Tooltip": "開啟介面時保留已有的搜尋記錄。", + "text.autoconfig.skyblocker.option.general.searchOverlay.historyLength": "保存的搜尋歷史長度", + "text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enablePearlWaypoints.@Tooltip": "透過這些路徑點,你可以透過同時使用末影珍珠和AOTV實現穿牆。", + "text.autoconfig.skyblocker.option.locations.dungeons.doorHighlight.doorHighlightType.secretWaypointsNote": "\n\n\n注意:若使此功能生效,必須啟用地牢秘密路徑點。", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonScore.scoreScaling": "分數縮放", + "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator": "啟用利潤計算器", + "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Croesus助手", + "text.autoconfig.skyblocker.option.locations.dungeons.creeperSolver": "苦力怕光束謎題助手", + "text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "常識問答謎題助手", + "skyblocker.partyFinder.partyCard.minDungeonLevel": "最低地牢等級要求:%d", + "skyblocker.partyFinder.deList": "點擊取消列出", + "text.autoconfig.skyblocker.option.general.tabHud.enableHudBackground": "啟用HUD背景", + "text.autoconfig.skyblocker.option.general.tabHud.enableHudBackground.@Tooltip": "為非TAB的HUD啟用背景。", + "text.autoconfig.skyblocker.option.richPresence.info.BITS": "點數", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.safeSpotWaypoints": "安全點路徑", + "text.autoconfig.skyblocker.option.locations.crimsonIsle.kuudra.noArrowPoisonWarning": "箭毒耗盡警告", + "text.autoconfig.skyblocker.option.locations.garden.farmingHud.enableHud": "啟用耕作HUD", + "text.autoconfig.skyblocker.option.locations.garden.farmingHud.config": "耕作HUD設定…", + "text.autoconfig.skyblocker.option.locations.garden.visitorHelper": "訪客助手", + "text.autoconfig.skyblocker.option.locations.dungeons.solveIceFill": "冰塊填充謎題助手", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud": "矮人礦井HUD", + "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]": "簡約:僅顯示委託及其進度百分比。", + "text.autoconfig.skyblocker.option.locations.end.hudEnabled": "啟用HUD", + "text.autoconfig.skyblocker.option.locations.end.screen": "終界HUD設定…", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleEnabled": "規則已啟用", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.modify": "修改", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.deleteQuestion": "你確定要移除這條規則嗎?", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.deleteWarning": "規則「%s」將永遠消失! (很久!)", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.inputs": "輸入:", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.actionBar.@Tooltip": "在操作欄中顯示此訊息。", + "text.autoconfig.skyblocker.option.messages.chatRules.screen.ruleScreen.announcement": "顯示公告:", + "skyblocker.quiverWarning.empty": "你的箭袋空了!", + "skyblocker.tips.customItemNames": "使用/skyblocker custom renameItem 自訂物品名稱", + "text.autoconfig.skyblocker.option.locations.garden.lockMouseTool": "手持農具時鎖定鏡頭", + "text.autoconfig.skyblocker.option.locations.garden.lockMouseGround": "僅當在地面上時鎖定鏡頭", + "text.autoconfig.skyblocker.option.messages.chatRules.announcementLength": "告示顯示時長", + "text.autoconfig.skyblocker.option.messages.chatRules.announcementScale.@Tooltip": "告示的縮放等級。", + "skyblocker.garden.hud.mouseLocked": "滑鼠已鎖定。", + "text.autoconfig.skyblocker.option.messages.chatRules.announcementLength.@Tooltip": "顯示公告的時間長度,以遊戲刻為單位。", + "text.autoconfig.skyblocker.option.messages.chatRules.announcementScale": "告示尺寸", + "text.autoconfig.skyblocker.option.general.enableNewYearCakesHelper.@Tooltip": "以綠色高亮顯示缺少的新年蛋糕、紅色高亮顯示已有的蛋糕。\n\n需要至少打開一次蛋糕背包才能生效。", + "text.autoconfig.skyblocker.option.general.enableNewYearCakesHelper": "啟用新年蛋糕助手", + "text.autoconfig.skyblocker.option.general.titleContainer.@Tooltip": "用於同時顯示多個標題,例如:吸血鬼殺手", + "text.autoconfig.skyblocker.option.general.titleContainer.titleContainerScale": "標題容器縮放", + "text.autoconfig.skyblocker.option.general.titleContainer.config": "標題容器位置設定", + "text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency.@Tooltip": "值越小,更新越頻繁,可能會導致卡頓。" } diff --git a/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button.png b/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button.png Binary files differnew file mode 100644 index 00000000..7a6e3162 --- /dev/null +++ b/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button.png diff --git a/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button_disabled.png b/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button_disabled.png Binary files differnew file mode 100644 index 00000000..02a22b4f --- /dev/null +++ b/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button_disabled.png diff --git a/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button_highlighted.png b/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button_highlighted.png Binary files differnew file mode 100644 index 00000000..be41b321 --- /dev/null +++ b/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/more_button_highlighted.png diff --git a/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/quick_craft_overlay.png b/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/quick_craft_overlay.png Binary files differnew file mode 100644 index 00000000..d8cf0c2b --- /dev/null +++ b/src/main/resources/assets/skyblocker/textures/gui/sprites/quick_craft/quick_craft_overlay.png diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index 11002372..9f5f8240 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -9,6 +9,7 @@ "BatEntityMixin", "ClientPlayerEntityMixin", "ClientPlayNetworkHandlerMixin", + "CommandTreeS2CPacketMixin", "DataTrackerMixin", "DrawContextMixin", "DyeableItemMixin", diff --git a/src/test/java/de/hysky/skyblocker/utils/InstancedUtilsTest.java b/src/test/java/de/hysky/skyblocker/utils/InstancedUtilsTest.java new file mode 100644 index 00000000..e5dbbff6 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/utils/InstancedUtilsTest.java @@ -0,0 +1,93 @@ +package de.hysky.skyblocker.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class InstancedUtilsTest { + + @Test + void testSameInstanceEqual() { + Vector3i vec1 = new Vector3i(8, 8, 8); + + Assertions.assertEquals(vec1, vec1); + } + + @Test + void testSameFieldValuesEqual() { + Vector3i vec1 = new Vector3i(8, 8, 8); + Vector3i vec2 = new Vector3i(8, 8, 8); + + Assertions.assertEquals(vec1, vec2); + } + + @Test + void testDifferentFieldValuesEqual() { + Vector3i vec1 = new Vector3i(8, 8, 8); + Vector3i vec2 = new Vector3i(-8, -8, -8); + + Assertions.assertNotEquals(vec1, vec2); + } + + @Test + void testHashCodeOfEqualFieldValues() { + Vector3i vec1 = new Vector3i(8, 8, 8); + Vector3i vec2 = new Vector3i(8, 8, 8); + + Assertions.assertEquals(vec1.hashCode(), vec2.hashCode()); + } + + @Test + void testHashCodeOfDifferentFieldValues() { + Vector3i vec1 = new Vector3i(8, 8, 8); + Vector3i vec2 = new Vector3i(-8, -8, -8); + + Assertions.assertNotEquals(vec1.hashCode(), vec2.hashCode()); + } + + @Test + void testToString() { + Vector3i vec1 = new Vector3i(8, 8, 8); + + Assertions.assertEquals(vec1.toString(), "Vector3i[x=8, y=8, z=8]"); + } + + @SuppressWarnings("unused") + private static class Vector3i { + final int x; + final int y; + final int z; + + Vector3i(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean equals(Object o) { + try { + return (boolean) InstancedUtils.equals(getClass()).invokeExact(this, o); + } catch (Throwable ignored) { + return super.equals(o); + } + } + + @Override + public int hashCode() { + try { + return (int) InstancedUtils.hashCode(getClass()).invokeExact(this); + } catch (Throwable ignored) { + return System.identityHashCode(this); + } + } + + @Override + public String toString() { + try { + return (String) InstancedUtils.toString(getClass()).invokeExact(this); + } catch (Throwable ignored) { + return super.toString(); + } + } + } +} diff --git a/src/test/resources/assets/skyblocker/dungeons/dungeonrooms.json b/src/test/resources/assets/skyblocker/dungeons/dungeonrooms.json index f7d50d36..f626623d 100644 --- a/src/test/resources/assets/skyblocker/dungeons/dungeonrooms.json +++ b/src/test/resources/assets/skyblocker/dungeons/dungeonrooms.json @@ -886,5 +886,66 @@ "fairysoul": false, "dsg": "/channels/727426780381577291/732154637506969650/733875583536201820", "sbp": "spider" + }, + "Redstone-Skull-3": { + "category": "1x1", + "secrets": 3, + "fairysoul": false, + "sbp": "redstone-skull" + }, + "Sewer-7": { + "category": "1x4", + "secrets": 7, + "fairysoul": false, + "sbp": "sewer" + }, + "Altar-6": { + "category": "L-shape", + "secrets": 6, + "fairysoul": false, + "sbp": "altar" + }, + "Slime-5": { + "category": "1x3", + "secrets": 5, + "fairysoul": false, + "sbp": "slime" + }, + "Crypts-1": { + "category": "1x1", + "secrets": 1, + "fairysoul": false, + "sbp": "crypts" + }, + "Draw-Bridge-6": { + "category": "1x1", + "secrets": 6, + "fairysoul": false, + "sbp": "draw-bridge" + }, + "Double-Stair-3": { + "category": "1x1", + "secrets": 3, + "fairysoul": false, + "sbp": "double-stair" + }, + "Lava-Skulls-3": { + "category": "1x1", + "secrets": 3, + "fairysoul": false, + "sbp": "lava-skulls" + }, + "Four-Banner-1": { + "category": "1x1", + "secrets": 1, + "fairysoul": false, + "sbp": "four-banner" + }, + "Zodd-1": { + "category": "1x1", + "secrets": 1, + "fairysoul": false, + "dsg": "null", + "sbp": null } -}
\ No newline at end of file +} diff --git a/src/test/resources/assets/skyblocker/dungeons/dungeonrooms/catacombs/1x1/Temple-3.skeleton b/src/test/resources/assets/skyblocker/dungeons/dungeonrooms/catacombs/1x1/Temple-3.skeleton Binary files differindex a0be54e0..b2193e71 100644 --- a/src/test/resources/assets/skyblocker/dungeons/dungeonrooms/catacombs/1x1/Temple-3.skeleton +++ b/src/test/resources/assets/skyblocker/dungeons/dungeonrooms/catacombs/1x1/Temple-3.skeleton diff --git a/src/test/resources/assets/skyblocker/dungeons/dungeonrooms/catacombs/1x1/Zodd-1.skeleton b/src/test/resources/assets/skyblocker/dungeons/dungeonrooms/catacombs/1x1/Zodd-1.skeleton Binary files differnew file mode 100644 index 00000000..099d5547 --- /dev/null +++ b/src/test/resources/assets/skyblocker/dungeons/dungeonrooms/catacombs/1x1/Zodd-1.skeleton diff --git a/src/test/resources/assets/skyblocker/dungeons/secretlocations.json b/src/test/resources/assets/skyblocker/dungeons/secretlocations.json index 031e1d03..3815e759 100644 --- a/src/test/resources/assets/skyblocker/dungeons/secretlocations.json +++ b/src/test/resources/assets/skyblocker/dungeons/secretlocations.json @@ -263,6 +263,13 @@ "z":21 }, { + "secretName":"1 - Pearl", + "category":"pearl", + "x":25, + "y":75, + "z":27 + }, + { "secretName":"1 - Item", "category":"item", "x":27, @@ -835,6 +842,13 @@ "z":21 }, { + "secretName":"2 - AOTV", + "category":"aotv", + "x":25, + "y":76, + "z":22 + }, + { "secretName":"2 - Stonk", "category":"stonk", "x":23, @@ -1419,6 +1433,13 @@ "z":9 }, { + "secretName":"3 - Stonk", + "category":"stonk", + "x":2, + "y":77, + "z":15 + }, + { "secretName":"3 - Superboom (not the chest in front)", "category":"superboom", "x":11, @@ -1706,11 +1727,11 @@ ], "Redstone-Key-3":[ { - "secretName":"1 - Redstone Skull (right click)", - "category":"lever", - "x":10, - "y":70, - "z":26 + "secretName":"1 - Superboom", + "category":"superboom", + "x":20, + "y":68, + "z":7 }, { "secretName":"1 - Lever behind Crypt", @@ -1727,6 +1748,20 @@ "z":6 }, { + "secretName":"2 - Redstone Skull (right click)", + "category":"lever", + "x":10, + "y":70, + "z":26 + }, + { + "secretName":"2 - Redstone Skull (right click)", + "category":"lever", + "x":19, + "y":66, + "z":7 + }, + { "secretName":"2 - Place Skull", "category":"lever", "x":27, @@ -2138,7 +2173,7 @@ }, { "secretName":"3 - Lever 2", - "category":"Lever", + "category":"lever", "x":31, "y":53, "z":24 @@ -2340,7 +2375,7 @@ }, { "secretName":"2 - Item", - "category":"Item", + "category":"item", "x":27, "y":56, "z":19 @@ -2924,9 +2959,9 @@ { "secretName":"6 - Stonk", "category":"stonk", - "x":17, + "x":18, "y":70, - "z":15 + "z":14 }, { "secretName":"6 - Pressure Plate 2", @@ -3038,6 +3073,13 @@ "z":5 }, { + "secretName":"2 - AOTV", + "category":"aotv", + "x":77, + "y":82, + "z":16 + }, + { "secretName":"2 - Wither Essence", "category":"wither", "x":79, @@ -3089,6 +3131,13 @@ "z":9 }, { + "secretName":"3 - AOTV", + "category":"aotv", + "x":7, + "y":80, + "z":15 + }, + { "secretName":"3 - Superboom", "category":"superboom", "x":6, @@ -3205,6 +3254,13 @@ "z":24 }, { + "secretName":"1/2 - AOTV", + "category":"aotv", + "x":84, + "y":91, + "z":26 + }, + { "secretName":"1 - Bat", "category":"bat", "x":81, @@ -3779,6 +3835,13 @@ "z":51 }, { + "secretName":"5 - AOTV", + "category":"aotv", + "x":16, + "y":81, + "z":53 + }, + { "secretName":"5 - Chest", "category":"chest", "x":31, @@ -3873,7 +3936,7 @@ }, { "secretName":"4/5/6 - Entrance 3", - "category":"", + "category":"entrance", "x":31, "y":142, "z":39 @@ -4051,6 +4114,13 @@ "z":30 }, { + "secretName":"5 - AOTV", + "category":"aotv", + "x":52, + "y":78, + "z":9 + }, + { "secretName":"5 - Crypt", "category":"superboom", "x":52, @@ -4660,7 +4730,7 @@ "z":15 }, { - "secretName":"2 - Stonk", + "secretName":"1 - Stonk", "category":"stonk", "x":26, "y":112, @@ -4825,6 +4895,13 @@ "z":52 }, { + "secretName":"2 - AOTV", + "category":"aotv", + "x":45, + "y":78, + "z":47 + }, + { "secretName":"2 - Chest", "category":"chest", "x":46, @@ -4860,6 +4937,13 @@ "z":41 }, { + "secretName":"5 - Pearl", + "category":"pearl", + "x":15, + "y":84, + "z":56 + }, + { "secretName":"5 - Stonk", "category":"stonk", "x":18, @@ -5060,6 +5144,13 @@ "z":61 }, { + "secretName": "7 - AOTV", + "category":"aotv", + "x":39, + "y":84, + "z":58 + }, + { "secretName":"7 - Chest", "category":"chest", "x":41, @@ -5311,6 +5402,13 @@ "z":43 }, { + "secretName":"3/4 - AOTV", + "category":"aotv", + "x":49, + "y":83, + "z":36 + }, + { "secretName":"3 - Chest", "category":"chest", "x":49, @@ -5387,5 +5485,601 @@ "y":91, "z":39 } + ], + "Redstone-Skull-3":[ + { + "secretName":"1 - Superboom", + "category":"superboom", + "x":22, + "y":70, + "z":24 + }, + { + "secretName":"1 - Chest", + "category":"chest", + "x":21, + "y":69, + "z":27 + }, + { + "secretName":"2/3 - Superboom", + "category":"superboom", + "x":24, + "y":70, + "z":26 + }, + { + "secretName":"2/3 - Redstone Skull (right click)", + "category":"lever", + "x":26, + "y":71, + "z":26 + }, + { + "secretName":"2/3 - Place Skull", + "category":"lever", + "x":4, + "y":73, + "z":24 + }, + { + "secretName":"3 - Entrance", + "category":"entrance", + "x":18, + "y":70, + "z":15 + }, + { + "secretName":"3 - Lever", + "category":"lever", + "x":15, + "y":68, + "z":15 + }, + { + "secretName":"3 - Entrance", + "category":"entrance", + "x":5, + "y":70, + "z":15 + }, + { + "secretName":"3 - Chest", + "category":"chest", + "x":1, + "y":70, + "z":15 + } + ], + "Sewer-7":[ + { + "secretName":"1 - Superboom", + "category":"superboom", + "x":120, + "y":48, + "z":14 + }, + { + "secretName":"1 - Chest", + "category":"chest", + "x":119, + "y":42, + "z":27 + }, + { + "secretName":"2 - Under Crypt", + "category":"superboom", + "x":93, + "y":43, + "z":24 + }, + { + "secretName":"2 - Item", + "category":"item", + "x":77, + "y":32, + "z":24 + }, + { + "secretName":"3 - Stonk", + "category":"stonk", + "x":63, + "y":55, + "z":7 + }, + { + "secretName":"3 - Lever", + "category":"lever", + "x":74, + "y":94, + "z":15 + }, + { + "secretName":"3 - Entrance", + "category":"entrance", + "x":63, + "y":54, + "z":7 + }, + { + "secretName":"3 - Chest", + "category":"chest", + "x":63, + "y":55, + "z":4 + }, + { + "secretName":"4 - Stonk", + "category":"stonk", + "x":20, + "y":46, + "z":15 + }, + { + "secretName":"4 - Entrance", + "category":"entrance", + "x":8, + "y":45, + "z":24 + }, + { + "secretName":"4 - Chest", + "category":"chest", + "x":19, + "y":45, + "z":15 + }, + { + "secretName":"5 - Pearl", + "category":"pearl", + "x":8, + "y":59, + "z":14 + }, + { + "secretName":"5 - Entrance", + "category":"entrance", + "x":25, + "y":62, + "z":25 + }, + { + "secretName":"5 - Chest", + "category":"chest", + "x":9, + "y":62, + "z":4 + }, + { + "secretName":"6 - Superboom", + "category":"superboom", + "x":16, + "y":55, + "z":25 + }, + { + "secretName":"6 - Chest", + "category":"chest", + "x":7, + "y":54, + "z":25 + }, + { + "secretName":"7 - Superboom", + "category":"superboom", + "x":7, + "y":56, + "z":22 + }, + { + "secretName":"7 - Bat", + "category":"bat", + "x":4, + "y":57, + "z":11 + } + ], + "Altar-6":[ + { + "secretName":"1 - Chest", + "category":"chest", + "x":19, + "y":83, + "z":43 + }, + { + "secretName":"2 - Item", + "category":"item", + "x":14, + "y":71, + "z":48 + }, + { + "secretName":"3 - Entrance", + "category":"entrance", + "x":25, + "y":87, + "z":22 + }, + { + "secretName":"3 - Entrance", + "category":"entrance", + "x":51, + "y":80, + "z":45 + }, + { + "secretName":"3 - Chest", + "category":"chest", + "x":51, + "y":88, + "z":45 + }, + { + "secretName":"4 - Entrance", + "category":"entrance", + "x":25, + "y":94, + "z":19 + }, + { + "secretName":"4 - Wither Essence", + "category":"wither", + "x":17, + "y":93, + "z":27 + }, + { + "secretName":"5 - Entrance", + "category":"entrance", + "x":30, + "y":58, + "z":38 + }, + { + "secretName":"5 - Bat", + "category":"bat", + "x":36, + "y":55, + "z":35 + }, + { + "secretName":"6 - Chest", + "category":"chest", + "x":11, + "y":44, + "z":39 + } + ], + "Slime-5":[ + { + "secretName":"1/2 - Superboom", + "category":"superboom", + "x":88, + "y":71, + "z":22 + }, + { + "secretName":"1 - Wither Essence", + "category":"wither", + "x":74, + "y":86, + "z":15 + }, + { + "secretName":"2 - Item", + "category":"item", + "x":91, + "y":86, + "z":20 + }, + { + "secretName":"3 - Entrance", + "category":"entrance", + "x":71, + "y":53, + "z":18 + }, + { + "secretName":"3 - Bat", + "category":"bat", + "x":80, + "y":52, + "z":16 + }, + { + "secretName":"4 - Chest", + "category":"chest", + "x":65, + "y":87, + "z":16 + }, + { + "secretName":"5 - Entrance", + "category":"entrance", + "x":12, + "y":70, + "z":15 + }, + { + "secretName":"5 - Entrance", + "category":"entrance", + "x":20, + "y":59, + "z":12 + }, + { + "secretName":"5 - Item", + "category":"item", + "x":4, + "y":57, + "z":15 + } + ], + "Crypts-1":[ + { + "secretName":"1 - Lever behind Crypt", + "category":"lever", + "x":29, + "y":75, + "z":15 + }, + { + "secretName":"1 - Entrance", + "category":"entrance", + "x":17, + "y":67, + "z":15 + }, + { + "secretName":"1 - Chest", + "category":"chest", + "x":6, + "y":66, + "z":15 + } + ], + "Draw-Bridge-6":[ + { + "secretName":"1 - Item", + "category":"item", + "x":4, + "y":60, + "z":58 + }, + { + "secretName":"2/3 - Superboom", + "category":"superboom", + "x":16, + "y":61, + "z":41 + }, + { + "secretName":"2 - Chest", + "category":"chest", + "x":15, + "y":60, + "z":46 + }, + { + "secretName":"3 - Bat", + "category":"bat", + "x":15, + "y":62, + "z":45 + }, + { + "secretName":"4 - Entrance", + "category":"entrance", + "x":5, + "y":61, + "z":20 + }, + { + "secretName":"4 - Wither Essence", + "category":"wither", + "x":19, + "y":60, + "z":4 + }, + { + "secretName":"5 - Lever", + "category":"lever", + "x":15, + "y":82, + "z":19 + }, + { + "secretName":"5 - Entrance", + "category":"entrance", + "x":16, + "y":83, + "z":56 + }, + { + "secretName":"5 - Chest", + "category":"chest", + "x":17, + "y":83, + "z":60 + }, + { + "secretName":"6 - Entrance", + "category":"entrance", + "x":41, + "y":60, + "z":55 + }, + { + "secretName":"6 - Chest", + "category":"chest", + "x":46, + "y":84, + "z":57 + } + ], + "Double-Stair-3":[ + { + "secretName":"1 - Pearl", + "category":"pearl", + "x":24, + "y":72, + "z":11 + }, + { + "secretName":"1 - Wither Essence", + "category":"wither", + "x":22, + "y":75, + "z":2 + }, + { + "secretName":"2 - Item", + "category":"item", + "x":11, + "y":78, + "z":27 + }, + { + "secretName":"3 - Superboom", + "category":"superboom", + "x":24, + "y":66, + "z":6 + }, + { + "secretName":"3 - Chest", + "category":"chest", + "x":28, + "y":66, + "z":3 + } + ], + "Lava-Skulls-3":[ + { + "secretName":"1 - Entrance", + "category":"entrance", + "x":5, + "y":71, + "z":26 + }, + { + "secretName":"1 - Chest", + "category":"chest", + "x":3, + "y":69, + "z":28 + }, + { + "secretName":"2/3 - Entrance", + "category":"entrance", + "x":23, + "y":73, + "z":15 + }, + { + "secretName":"2/3 - Entrance", + "category":"entrance", + "x":7, + "y":73, + "z":15 + }, + { + "secretName":"2 - Chest", + "category":"chest", + "x":15, + "y":80, + "z":27 + }, + { + "secretName":"3 - Bat", + "category":"bat", + "x":13, + "y":89, + "z":4 + } + ], + "Four-Banner-1":[ + { + "secretName":"1 - Chest", + "category":"chest", + "x":15, + "y":85, + "z":15 + } + ], + "Trinity-4":[ + { + "secretName":"1 - Entrance", + "category":"entrance", + "x":19, + "y":76, + "z":29 + }, + { + "secretName":"1 - Chest", + "category":"chest", + "x":14, + "y":75, + "z":27 + }, + { + "secretName":"2/3/4 - Entrance", + "category":"entrance", + "x":10, + "y":77, + "z":22 + }, + { + "secretName":"2 - Item", + "category":"item", + "x":7, + "y":74, + "z":27 + }, + { + "secretName":"3 - Entrance", + "category":"entrance", + "x":11, + "y":86, + "z":22 + }, + { + "secretName":"3 - Bat", + "category":"bat", + "x":8, + "y":87, + "z":18 + }, + { + "secretName":"4 - Entrance", + "category":"entrance", + "x":7, + "y":92, + "z":16 + }, + { + "secretName":"4 - Superboom", + "category":"superboom", + "x":17, + "y":91, + "z":24 + }, + { + "secretName":"4 - Bat", + "category":"bat", + "x":2, + "y":94, + "z":26 + } + ], + "Zodd-1": [ + { + "secretName":"1 - Chest", + "category":"chest", + "x":7, + "y":70, + "z":3 + } ] } |