From 1738f11d88bc90736d08f8a5f994a3afef5143b7 Mon Sep 17 00:00:00 2001 From: Fluboxer Date: Sat, 17 Feb 2024 13:27:28 +0300 Subject: added floor tier to item tooltip as item quality without tier was half useless --- .../hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/main/java') 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 19f2e6fd..6ec57359 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 @@ -144,11 +144,20 @@ public class ItemTooltip { NbtCompound ea = ItemUtils.getExtraAttributes(stack); if (ea != null && ea.contains("baseStatBoostPercentage")) { int baseStatBoostPercentage = ea.getInt("baseStatBoostPercentage"); - if (baseStatBoostPercentage == 50) { + boolean max_quality = baseStatBoostPercentage == 50; + if (max_quality) { lines.add(Text.literal(String.format("%-17s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.RED).formatted(Formatting.BOLD)); } else { lines.add(Text.literal(String.format("%-21s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.BLUE)); } + if (ea.contains("item_tier")) { // sometimes it just isn't here? + int item_tier = ea.getInt("item_tier"); + if (max_quality) { + lines.add(Text.literal(String.format("%-17s", "Floor Tier:") + item_tier).formatted(Formatting.RED).formatted(Formatting.BOLD)); + } else { + lines.add(Text.literal(String.format("%-21s", "Floor Tier:") + item_tier).formatted(Formatting.BLUE)); + } + } } } -- cgit From 15cb9c5784a9fbe41e3e5235213c2aff5967e9ca Mon Sep 17 00:00:00 2001 From: Fluboxer Date: Sat, 17 Feb 2024 17:48:40 +0300 Subject: Made tooltip also show floor instead of only showing tier number --- .../skyblock/item/tooltip/ItemTooltip.java | 25 ++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'src/main/java') 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 6ec57359..d5be7eee 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 @@ -140,22 +140,35 @@ public class ItemTooltip { } } + final Map itemTierFloors = new HashMap<>() {{ + put(1, "F1"); + put(2, "F2"); + put(3, "F3"); + put(4, "F4/M1"); + put(5, "F5/M2"); + put(6, "F6/M3"); + put(7, "F7/M4"); + put(8, "M5"); + put(9, "M6"); + put(10, "M7"); + }}; + if (SkyblockerConfigManager.get().general.dungeonQuality) { NbtCompound ea = ItemUtils.getExtraAttributes(stack); if (ea != null && ea.contains("baseStatBoostPercentage")) { int baseStatBoostPercentage = ea.getInt("baseStatBoostPercentage"); - boolean max_quality = baseStatBoostPercentage == 50; - if (max_quality) { + boolean maxQuality = baseStatBoostPercentage == 50; + if (maxQuality) { lines.add(Text.literal(String.format("%-17s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.RED).formatted(Formatting.BOLD)); } else { lines.add(Text.literal(String.format("%-21s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.BLUE)); } if (ea.contains("item_tier")) { // sometimes it just isn't here? - int item_tier = ea.getInt("item_tier"); - if (max_quality) { - lines.add(Text.literal(String.format("%-17s", "Floor Tier:") + item_tier).formatted(Formatting.RED).formatted(Formatting.BOLD)); + int itemTier = ea.getInt("item_tier"); + if (maxQuality) { + lines.add(Text.literal(String.format("%-17s", "Floor Tier:") + itemTier + " (" + itemTierFloors.get(itemTier) + ")").formatted(Formatting.RED).formatted(Formatting.BOLD)); } else { - lines.add(Text.literal(String.format("%-21s", "Floor Tier:") + item_tier).formatted(Formatting.BLUE)); + lines.add(Text.literal(String.format("%-21s", "Floor Tier:") + itemTier + " (" + itemTierFloors.get(itemTier) + ")").formatted(Formatting.BLUE)); } } } -- cgit From 6d9aa9f33e91294c5e5733982ffd6c77ebb1a674 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sat, 17 Feb 2024 21:17:49 -0500 Subject: Refactor search overlay bazaar data source --- .../skyblocker/skyblock/searchOverlay/SearchOverManager.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java b/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java index b2a453a9..68f279c7 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java @@ -96,13 +96,15 @@ public class SearchOverManager { HashMap namesToId = new HashMap<>(); //get bazaar items - try (Http.ApiResponse response = Http.sendHypixelRequest("skyblock/bazaar", "")) { - JsonObject products = JsonParser.parseString(response.content()).getAsJsonObject().get("products").getAsJsonObject(); + try { + if (TooltipInfoType.BAZAAR.getData() == null) TooltipInfoType.BAZAAR.run(); + + JsonObject products = TooltipInfoType.BAZAAR.getData(); for (Map.Entry entry : products.entrySet()) { if (entry.getValue().isJsonObject()) { JsonObject product = entry.getValue().getAsJsonObject(); - String id = product.get("product_id").getAsString(); - int sellVolume = product.get("quick_status").getAsJsonObject().get("sellVolume").getAsInt(); + String id = product.get("id").getAsString(); + int sellVolume = product.get("sellVolume").getAsInt(); if (sellVolume == 0) continue; //do not add items that do not sell e.g. they are not actual in the bazaar Matcher matcher = BAZAAR_ENCHANTMENT_PATTERN.matcher(id); -- cgit From 39cf82e4976f70bf8b8d27216a0284e30d9ae5e4 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sat, 17 Feb 2024 21:32:15 -0500 Subject: Rename search overlay package to match Java conventions --- .../java/de/hysky/skyblocker/SkyblockerMod.java | 2 +- .../skyblocker/mixin/ClientPlayerEntityMixin.java | 4 +- .../skyblock/searchOverlay/OverlayScreen.java | 185 ----------- .../skyblock/searchOverlay/SearchOverManager.java | 349 --------------------- .../skyblock/searchoverlay/OverlayScreen.java | 185 +++++++++++ .../skyblock/searchoverlay/SearchOverManager.java | 349 +++++++++++++++++++++ .../skyblock/searchOverlay/SplitStringTest.java | 31 -- .../skyblock/searchoverlay/SplitStringTest.java | 31 ++ 8 files changed, 568 insertions(+), 568 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java delete mode 100644 src/test/java/de/hysky/skyblocker/skyblock/searchOverlay/SplitStringTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/searchoverlay/SplitStringTest.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 1aa97526..b8722fc6 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -26,7 +26,7 @@ import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; import de.hysky.skyblocker.skyblock.quicknav.QuickNav; import de.hysky.skyblocker.skyblock.rift.TheRift; -import de.hysky.skyblocker.skyblock.searchOverlay.SearchOverManager; +import de.hysky.skyblocker.skyblock.searchoverlay.SearchOverManager; import de.hysky.skyblocker.skyblock.shortcut.Shortcuts; import de.hysky.skyblocker.skyblock.special.SpecialEffects; import de.hysky.skyblocker.skyblock.tabhud.TabHud; diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java index 2a4c38a7..ceda9ed4 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java @@ -6,8 +6,8 @@ import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen; import de.hysky.skyblocker.skyblock.item.HotbarSlotLock; import de.hysky.skyblocker.skyblock.item.ItemProtection; import de.hysky.skyblocker.skyblock.rift.HealingMelonIndicator; -import de.hysky.skyblocker.skyblock.searchOverlay.OverlayScreen; -import de.hysky.skyblocker.skyblock.searchOverlay.SearchOverManager; +import de.hysky.skyblocker.skyblock.searchoverlay.OverlayScreen; +import de.hysky.skyblocker.skyblock.searchoverlay.SearchOverManager; import de.hysky.skyblocker.utils.Utils; import net.minecraft.block.entity.SignBlockEntity; import net.minecraft.client.MinecraftClient; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java deleted file mode 100644 index e1545c6c..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java +++ /dev/null @@ -1,185 +0,0 @@ -package de.hysky.skyblocker.skyblock.searchOverlay; - -import de.hysky.skyblocker.config.SkyblockerConfigManager; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.item.ItemStack; -import net.minecraft.text.Style; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.Identifier; -import org.lwjgl.glfw.GLFW; - -import static de.hysky.skyblocker.skyblock.itemlist.ItemRepository.getItemStack; - -public class OverlayScreen extends Screen { - - protected static final Identifier SEARCH_ICON_TEXTURE = new Identifier("icon/search"); - private static final int rowHeight = 20; - private TextFieldWidget searchField; - private ButtonWidget finishedButton; - private ButtonWidget[] suggestionButtons; - private ButtonWidget[] historyButtons; - - public OverlayScreen(Text title) { - super(title); - } - - /** - * Creates the layout for the overlay screen. - */ - @Override - protected void init() { - super.init(); - int rowWidth = (int) (this.width * 0.4); - int startX = (int) (this.width * 0.5) - rowWidth / 2; - int startY = (int) ((int) (this.height * 0.5) - (rowHeight * (1 + SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions + 0.75 + SkyblockerConfigManager.get().general.searchOverlay.historyLength)) / 2); - - // Search field - this.searchField = new TextFieldWidget(textRenderer, startX, startY, rowWidth - rowHeight, rowHeight, Text.translatable("gui.recipebook.search_hint")); - searchField.setText(SearchOverManager.search); - searchField.setChangedListener(SearchOverManager::updateSearch); - searchField.setMaxLength(30); - - // finish buttons - finishedButton = ButtonWidget.builder(Text.literal("").setStyle(Style.EMPTY.withColor(Formatting.GREEN)), a -> close()) - .position(startX + rowWidth - rowHeight, startY) - .size(rowHeight, rowHeight).build(); - - // suggested item buttons - int rowOffset = rowHeight; - int totalSuggestions = SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions; - this.suggestionButtons = new ButtonWidget[totalSuggestions]; - for (int i = 0; i < totalSuggestions; i++) { - suggestionButtons[i] = ButtonWidget.builder(Text.literal(SearchOverManager.getSuggestion(i)).setStyle(Style.EMPTY), a -> { - SearchOverManager.updateSearch(a.getMessage().getString()); - close(); - }) - .position(startX, startY + rowOffset) - .size(rowWidth, rowHeight).build(); - suggestionButtons[i].visible = false; - rowOffset += rowHeight; - } - // history item buttons - rowOffset += (int) (rowHeight * 0.75); - int historyLength = SkyblockerConfigManager.get().general.searchOverlay.historyLength; - this.historyButtons = new ButtonWidget[historyLength]; - for (int i = 0; i < historyLength; i++) { - String text = SearchOverManager.getHistory(i); - if (text != null) { - historyButtons[i] = ButtonWidget.builder(Text.literal(text).setStyle(Style.EMPTY), (a) -> { - SearchOverManager.updateSearch(a.getMessage().getString()); - close(); - }) - .position(startX, startY + rowOffset) - .size(rowWidth, rowHeight).build(); - rowOffset += rowHeight; - } else { - break; - } - } - - //add drawables in order to make tab navigation sensible - addDrawableChild(searchField); - for (ButtonWidget suggestion : suggestionButtons) { - addDrawableChild(suggestion); - } - for (ButtonWidget historyOption : historyButtons) { - if (historyOption != null) { - addDrawableChild(historyOption); - } - } - addDrawableChild(finishedButton); - - //focus the search box - this.setInitialFocus(searchField); - } - - /** - * Renders the search icon, label for the history and item Stacks for item names - */ - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - int renderOffset = (rowHeight - 16) / 2; - context.drawGuiTexture(SEARCH_ICON_TEXTURE, finishedButton.getX() + renderOffset, finishedButton.getY() + renderOffset, 16, 16); - if (historyButtons.length > 0 && historyButtons[0] != null) { - context.drawText(textRenderer, Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.historyLabel"), historyButtons[0].getX() + renderOffset, historyButtons[0].getY() - rowHeight / 2, 0xFFFFFFFF, true); - } - - //draw item stacks and tooltip to buttons - for (int i = 0; i < suggestionButtons.length; i++) { - drawItemAndTooltip(context, mouseX, mouseY, SearchOverManager.getSuggestionId(i), suggestionButtons[i], renderOffset); - } - for (int i = 0; i < historyButtons.length; i++) { - drawItemAndTooltip(context, mouseX, mouseY, SearchOverManager.getHistoryId(i), historyButtons[i], renderOffset); - } - } - - /** - * Draws the item and tooltip for the given button - */ - private void drawItemAndTooltip(DrawContext context, int mouseX, int mouseY, String id, ButtonWidget button, int renderOffset) { - if (id == null || id.isEmpty()) return; - ItemStack item = getItemStack(id); - if (item == null) return; - context.drawItem(item, button.getX() + renderOffset, button.getY() + renderOffset); - - // Draw tooltip - if (button.isMouseOver(mouseX, mouseY)) { - context.drawItemTooltip(textRenderer, item, mouseX, mouseY); - } - } - - /** - * updates if the suggestions buttons should be visible based on if they have a value - */ - @Override - public final void tick() { - super.tick(); - //update suggestion buttons text - for (int i = 0; i < SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions; i++) { - String text = SearchOverManager.getSuggestion(i); - if (!text.isEmpty()) { - suggestionButtons[i].visible = true; - - boolean isNewText = !text.equals(suggestionButtons[i].getMessage().getString()); - if (!isNewText) continue; - - suggestionButtons[i].setMessage(Text.literal(text).setStyle(Style.EMPTY)); - } else { - suggestionButtons[i].visible = false; - } - } - } - - /** - * When a key is pressed. If enter key pressed and search box selected close - */ - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ENTER && searchField.isActive()) { - close(); - return true; - } - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public boolean shouldPause() { - return false; - } - - /** - * Closes the overlay screen and gets the manager to send a packet update about the sign - */ - @Override - public void close() { - assert this.client != null; - assert this.client.player != null; - SearchOverManager.pushSearch(); - super.close(); - } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java b/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java deleted file mode 100644 index 68f279c7..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java +++ /dev/null @@ -1,349 +0,0 @@ -package de.hysky.skyblocker.skyblock.searchOverlay; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import de.hysky.skyblocker.config.SkyblockerConfig; -import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType; -import de.hysky.skyblocker.utils.Http; -import de.hysky.skyblocker.utils.NEURepoManager; -import de.hysky.skyblocker.utils.scheduler.MessageScheduler; -import io.github.moulberry.repo.data.NEUItem; -import it.unimi.dsi.fastutil.Pair; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.block.entity.SignBlockEntity; -import net.minecraft.client.MinecraftClient; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; - -public class SearchOverManager { - - private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Search Overlay"); - - private static final Pattern BAZAAR_ENCHANTMENT_PATTERN = Pattern.compile("ENCHANTMENT_(\\D*)_(\\d+)"); - private static final Pattern AUCTION_PET_AND_RUNE_PATTERN = Pattern.compile("([A-Z0-9_]+);(\\d+)"); - - /** - * converts index (in array) +1 to a roman numeral - */ - private static final String[] ROMAN_NUMERALS = new String[]{ - "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", - "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX" - }; - - private static @Nullable SignBlockEntity sign = null; - private static boolean signFront = true; - private static boolean isAuction; - private static boolean isCommand; - - protected static String search = ""; - - // Use non-final variables and swap them to prevent concurrent modification - private static HashSet bazaarItems; - private static HashSet auctionItems; - private static HashMap namesToId; - - public static String[] suggestionsArray = {}; - - /** - * uses the skyblock api and Moulberry auction to load a list of items in bazaar and auction house - */ - public static void init() { - ClientCommandRegistrationCallback.EVENT.register(SearchOverManager::registerSearchCommands); - NEURepoManager.runAsyncAfterLoad(SearchOverManager::loadItems); - } - - private static void registerSearchCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { - if (SkyblockerConfigManager.get().general.searchOverlay.enableCommands) { - dispatcher.register(literal("ahs").executes(context -> startCommand(true))); - dispatcher.register(literal("bzs").executes(context -> startCommand(false))); - } - } - - private static int startCommand(boolean isAuction) { - isCommand = true; - SearchOverManager.isAuction = isAuction; - search = ""; - suggestionsArray = new String[]{}; - CLIENT.send(() -> CLIENT.setScreen(new OverlayScreen(Text.of("")))); - return Command.SINGLE_SUCCESS; - } - - private static void loadItems() { - HashSet bazaarItems = new HashSet<>(); - HashSet auctionItems = new HashSet<>(); - HashMap namesToId = new HashMap<>(); - - //get bazaar items - try { - if (TooltipInfoType.BAZAAR.getData() == null) TooltipInfoType.BAZAAR.run(); - - JsonObject products = TooltipInfoType.BAZAAR.getData(); - for (Map.Entry entry : products.entrySet()) { - if (entry.getValue().isJsonObject()) { - JsonObject product = entry.getValue().getAsJsonObject(); - String id = product.get("id").getAsString(); - int sellVolume = product.get("sellVolume").getAsInt(); - if (sellVolume == 0) - continue; //do not add items that do not sell e.g. they are not actual in the bazaar - Matcher matcher = BAZAAR_ENCHANTMENT_PATTERN.matcher(id); - if (matcher.matches()) {//format enchantments - //remove ultimate if in name - String name = matcher.group(1); - if (!name.contains("WISE")) { //only way found to remove ultimate from everything but ultimate wise - name = name.replace("ULTIMATE_", ""); - } - name = name.replace("_", " "); - name = capitalizeFully(name); - int enchantLevel = Integer.parseInt(matcher.group(2)); - String level = ""; - if (enchantLevel > 0) { - level = ROMAN_NUMERALS[enchantLevel - 1]; - } - bazaarItems.add(name + " " + level); - namesToId.put(name + " " + level, matcher.group(1) + ";" + matcher.group(2)); - continue; - } - //look up id for name - NEUItem neuItem = NEURepoManager.NEU_REPO.getItems().getItemBySkyblockId(id); - if (neuItem != null) { - String name = Formatting.strip(neuItem.getDisplayName()); - bazaarItems.add(name); - namesToId.put(name, id); - continue; - } - } - } - } catch (Exception e) { - LOGGER.error("[Skyblocker] Failed to load bazaar item list! ", e); - } - - //get auction items - try { - if (TooltipInfoType.THREE_DAY_AVERAGE.getData() == null) { - TooltipInfoType.THREE_DAY_AVERAGE.run(); - } - for (Map.Entry entry : TooltipInfoType.THREE_DAY_AVERAGE.getData().entrySet()) { - String id = entry.getKey(); - - Matcher matcher = AUCTION_PET_AND_RUNE_PATTERN.matcher(id); - if (matcher.find()) {//is a pet or rune convert id to name - String name = matcher.group(1).replace("_", " "); - name = capitalizeFully(name); - auctionItems.add(name); - namesToId.put(name, id); - continue; - } - //something else look up in NEU repo. - id = id.split("[+-]")[0]; - NEUItem neuItem = NEURepoManager.NEU_REPO.getItems().getItemBySkyblockId(id); - if (neuItem != null) { - String name = Formatting.strip(neuItem.getDisplayName()); - auctionItems.add(name); - namesToId.put(name, id); - continue; - } - } - } catch (Exception e) { - LOGGER.error("[Skyblocker] Failed to load auction house item list! ", e); - } - - SearchOverManager.bazaarItems = bazaarItems; - SearchOverManager.auctionItems = auctionItems; - SearchOverManager.namesToId = namesToId; - } - - /** - * Capitalizes the first letter off every word in a string - * @param str string to capitalize - */ - private static String capitalizeFully(String str) { - if (str == null || str.isEmpty()) { - return str; - } - - return Arrays.stream(str.split("\\s+")) - .map(t -> t.substring(0, 1).toUpperCase() + t.substring(1).toLowerCase()) - .collect(Collectors.joining(" ")); - } - - /** - * Receives data when a search is started and resets values - * @param sign the sign that is being edited - * @param front if it's the front of the sign - * @param isAuction if the sign is loaded from the auction house menu or bazaar - */ - public static void updateSign(@NotNull SignBlockEntity sign, boolean front, boolean isAuction) { - signFront = front; - SearchOverManager.sign = sign; - isCommand = false; - SearchOverManager.isAuction = isAuction; - if (SkyblockerConfigManager.get().general.searchOverlay.keepPreviousSearches) { - Text[] messages = SearchOverManager.sign.getText(signFront).getMessages(CLIENT.shouldFilterText()); - search = messages[0].getString(); - if (!messages[1].getString().isEmpty()) { - if (!search.endsWith(" ")) { - search += " "; - } - search += messages[1].getString(); - } - } else { - search = ""; - } - suggestionsArray = new String[]{}; - } - - /** - * Updates the search value and the suggestions based on that value. - * @param newValue new search value - */ - protected static void updateSearch(String newValue) { - search = newValue; - //update the suggestion values - int totalSuggestions = SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions; - if (newValue.isBlank() || totalSuggestions == 0) return; //do not search for empty value - suggestionsArray = (isAuction ? auctionItems : bazaarItems).stream().filter(item -> item.toLowerCase().contains(search.toLowerCase())).limit(totalSuggestions).toArray(String[]::new); - } - - /** - * Gets the suggestion in the suggestion array at the index - * @param index index of suggestion - */ - protected static String getSuggestion(int index) { - if (suggestionsArray.length > index && suggestionsArray[index] != null) { - return suggestionsArray[index]; - } else {//there are no suggestions yet - return ""; - } - } - - protected static String getSuggestionId(int index) { - return namesToId.get(getSuggestion(index)); - } - - /** - * Gets the item name in the history array at the index - * @param index index of suggestion - */ - protected static String getHistory(int index) { - if (isAuction) { - if (SkyblockerConfigManager.get().general.searchOverlay.auctionHistory.size() > index) { - return SkyblockerConfigManager.get().general.searchOverlay.auctionHistory.get(index); - } - } else { - if (SkyblockerConfigManager.get().general.searchOverlay.bazaarHistory.size() > index) { - return SkyblockerConfigManager.get().general.searchOverlay.bazaarHistory.get(index); - } - } - return null; - } - - protected static String getHistoryId(int index) { - return namesToId.get(getHistory(index)); - } - - /** - * Add the current search value to the start of the history list and truncate to the max history value and save this to the config - */ - private static void saveHistory() { - //save to history - SkyblockerConfig.SearchOverlay config = SkyblockerConfigManager.get().general.searchOverlay; - if (isAuction) { - if (config.auctionHistory.isEmpty() || !config.auctionHistory.get(0).equals(search)) { - config.auctionHistory.add(0, search); - if (config.auctionHistory.size() > config.historyLength) { - config.auctionHistory = config.auctionHistory.subList(0, config.historyLength); - } - } - } else { - if (config.bazaarHistory.isEmpty() || !config.bazaarHistory.get(0).equals(search)) { - config.bazaarHistory.add(0, search); - if (config.bazaarHistory.size() > config.historyLength) { - config.bazaarHistory = config.bazaarHistory.subList(0, config.historyLength); - } - } - } - SkyblockerConfigManager.save(); - } - - /** - *Saves the current value of ({@link SearchOverManager#search}) then pushes it to a command or sign depending on how the gui was opened - */ - protected static void pushSearch() { - //save to history - if (!search.isEmpty()) { - saveHistory(); - } - if (isCommand) { - pushCommand(); - } else { - pushSign(); - } - } - - /** - * runs the command to search for the value in ({@link SearchOverManager#search}) - */ - private static void pushCommand() { - if (search.isEmpty()) return; - - String command; - if (isAuction) { - command = "/ahSearch " + search; - } else { - command = "/bz " + search; - } - MessageScheduler.INSTANCE.sendMessageAfterCooldown(command); - } - - /** - * pushes the ({@link SearchOverManager#search}) to the sign. It needs to split it over two lines without splitting a word - */ - private static void pushSign() { - //splits text into 2 lines max = 30 chars - Pair split = splitString(search); - - // send packet to update sign - if (CLIENT.player != null && sign != null) { - Text[] messages = sign.getText(signFront).getMessages(CLIENT.shouldFilterText()); - CLIENT.player.networkHandler.sendPacket(new UpdateSignC2SPacket(sign.getPos(), signFront, - split.left(), - split.right(), - messages[2].getString(), - messages[3].getString() - )); - } - } - - static Pair splitString(String s) { - if (s.length() <= 15) { - return Pair.of(s, ""); - } - int index = s.lastIndexOf(' ', 15); - if (index == -1) { - return Pair.of(s.substring(0, 15), ""); - } - return Pair.of(s.substring(0, index), s.substring(index + 1, Math.min(index + 16, s.length())).trim()); - } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java new file mode 100644 index 00000000..b8907e27 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java @@ -0,0 +1,185 @@ +package de.hysky.skyblocker.skyblock.searchoverlay; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; +import org.lwjgl.glfw.GLFW; + +import static de.hysky.skyblocker.skyblock.itemlist.ItemRepository.getItemStack; + +public class OverlayScreen extends Screen { + + protected static final Identifier SEARCH_ICON_TEXTURE = new Identifier("icon/search"); + private static final int rowHeight = 20; + private TextFieldWidget searchField; + private ButtonWidget finishedButton; + private ButtonWidget[] suggestionButtons; + private ButtonWidget[] historyButtons; + + public OverlayScreen(Text title) { + super(title); + } + + /** + * Creates the layout for the overlay screen. + */ + @Override + protected void init() { + super.init(); + int rowWidth = (int) (this.width * 0.4); + int startX = (int) (this.width * 0.5) - rowWidth / 2; + int startY = (int) ((int) (this.height * 0.5) - (rowHeight * (1 + SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions + 0.75 + SkyblockerConfigManager.get().general.searchOverlay.historyLength)) / 2); + + // Search field + this.searchField = new TextFieldWidget(textRenderer, startX, startY, rowWidth - rowHeight, rowHeight, Text.translatable("gui.recipebook.search_hint")); + searchField.setText(SearchOverManager.search); + searchField.setChangedListener(SearchOverManager::updateSearch); + searchField.setMaxLength(30); + + // finish buttons + finishedButton = ButtonWidget.builder(Text.literal("").setStyle(Style.EMPTY.withColor(Formatting.GREEN)), a -> close()) + .position(startX + rowWidth - rowHeight, startY) + .size(rowHeight, rowHeight).build(); + + // suggested item buttons + int rowOffset = rowHeight; + int totalSuggestions = SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions; + this.suggestionButtons = new ButtonWidget[totalSuggestions]; + for (int i = 0; i < totalSuggestions; i++) { + suggestionButtons[i] = ButtonWidget.builder(Text.literal(SearchOverManager.getSuggestion(i)).setStyle(Style.EMPTY), a -> { + SearchOverManager.updateSearch(a.getMessage().getString()); + close(); + }) + .position(startX, startY + rowOffset) + .size(rowWidth, rowHeight).build(); + suggestionButtons[i].visible = false; + rowOffset += rowHeight; + } + // history item buttons + rowOffset += (int) (rowHeight * 0.75); + int historyLength = SkyblockerConfigManager.get().general.searchOverlay.historyLength; + this.historyButtons = new ButtonWidget[historyLength]; + for (int i = 0; i < historyLength; i++) { + String text = SearchOverManager.getHistory(i); + if (text != null) { + historyButtons[i] = ButtonWidget.builder(Text.literal(text).setStyle(Style.EMPTY), (a) -> { + SearchOverManager.updateSearch(a.getMessage().getString()); + close(); + }) + .position(startX, startY + rowOffset) + .size(rowWidth, rowHeight).build(); + rowOffset += rowHeight; + } else { + break; + } + } + + //add drawables in order to make tab navigation sensible + addDrawableChild(searchField); + for (ButtonWidget suggestion : suggestionButtons) { + addDrawableChild(suggestion); + } + for (ButtonWidget historyOption : historyButtons) { + if (historyOption != null) { + addDrawableChild(historyOption); + } + } + addDrawableChild(finishedButton); + + //focus the search box + this.setInitialFocus(searchField); + } + + /** + * Renders the search icon, label for the history and item Stacks for item names + */ + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + int renderOffset = (rowHeight - 16) / 2; + context.drawGuiTexture(SEARCH_ICON_TEXTURE, finishedButton.getX() + renderOffset, finishedButton.getY() + renderOffset, 16, 16); + if (historyButtons.length > 0 && historyButtons[0] != null) { + context.drawText(textRenderer, Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.historyLabel"), historyButtons[0].getX() + renderOffset, historyButtons[0].getY() - rowHeight / 2, 0xFFFFFFFF, true); + } + + //draw item stacks and tooltip to buttons + for (int i = 0; i < suggestionButtons.length; i++) { + drawItemAndTooltip(context, mouseX, mouseY, SearchOverManager.getSuggestionId(i), suggestionButtons[i], renderOffset); + } + for (int i = 0; i < historyButtons.length; i++) { + drawItemAndTooltip(context, mouseX, mouseY, SearchOverManager.getHistoryId(i), historyButtons[i], renderOffset); + } + } + + /** + * Draws the item and tooltip for the given button + */ + private void drawItemAndTooltip(DrawContext context, int mouseX, int mouseY, String id, ButtonWidget button, int renderOffset) { + if (id == null || id.isEmpty()) return; + ItemStack item = getItemStack(id); + if (item == null) return; + context.drawItem(item, button.getX() + renderOffset, button.getY() + renderOffset); + + // Draw tooltip + if (button.isMouseOver(mouseX, mouseY)) { + context.drawItemTooltip(textRenderer, item, mouseX, mouseY); + } + } + + /** + * updates if the suggestions buttons should be visible based on if they have a value + */ + @Override + public final void tick() { + super.tick(); + //update suggestion buttons text + for (int i = 0; i < SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions; i++) { + String text = SearchOverManager.getSuggestion(i); + if (!text.isEmpty()) { + suggestionButtons[i].visible = true; + + boolean isNewText = !text.equals(suggestionButtons[i].getMessage().getString()); + if (!isNewText) continue; + + suggestionButtons[i].setMessage(Text.literal(text).setStyle(Style.EMPTY)); + } else { + suggestionButtons[i].visible = false; + } + } + } + + /** + * When a key is pressed. If enter key pressed and search box selected close + */ + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ENTER && searchField.isActive()) { + close(); + return true; + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean shouldPause() { + return false; + } + + /** + * Closes the overlay screen and gets the manager to send a packet update about the sign + */ + @Override + public void close() { + assert this.client != null; + assert this.client.player != null; + SearchOverManager.pushSearch(); + super.close(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java new file mode 100644 index 00000000..1d740601 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java @@ -0,0 +1,349 @@ +package de.hysky.skyblocker.skyblock.searchoverlay; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType; +import de.hysky.skyblocker.utils.Http; +import de.hysky.skyblocker.utils.NEURepoManager; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import io.github.moulberry.repo.data.NEUItem; +import it.unimi.dsi.fastutil.Pair; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.block.entity.SignBlockEntity; +import net.minecraft.client.MinecraftClient; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +public class SearchOverManager { + + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Search Overlay"); + + private static final Pattern BAZAAR_ENCHANTMENT_PATTERN = Pattern.compile("ENCHANTMENT_(\\D*)_(\\d+)"); + private static final Pattern AUCTION_PET_AND_RUNE_PATTERN = Pattern.compile("([A-Z0-9_]+);(\\d+)"); + + /** + * converts index (in array) +1 to a roman numeral + */ + private static final String[] ROMAN_NUMERALS = new String[]{ + "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", + "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX" + }; + + private static @Nullable SignBlockEntity sign = null; + private static boolean signFront = true; + private static boolean isAuction; + private static boolean isCommand; + + protected static String search = ""; + + // Use non-final variables and swap them to prevent concurrent modification + private static HashSet bazaarItems; + private static HashSet auctionItems; + private static HashMap namesToId; + + public static String[] suggestionsArray = {}; + + /** + * uses the skyblock api and Moulberry auction to load a list of items in bazaar and auction house + */ + public static void init() { + ClientCommandRegistrationCallback.EVENT.register(SearchOverManager::registerSearchCommands); + NEURepoManager.runAsyncAfterLoad(SearchOverManager::loadItems); + } + + private static void registerSearchCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + if (SkyblockerConfigManager.get().general.searchOverlay.enableCommands) { + dispatcher.register(literal("ahs").executes(context -> startCommand(true))); + dispatcher.register(literal("bzs").executes(context -> startCommand(false))); + } + } + + private static int startCommand(boolean isAuction) { + isCommand = true; + SearchOverManager.isAuction = isAuction; + search = ""; + suggestionsArray = new String[]{}; + CLIENT.send(() -> CLIENT.setScreen(new OverlayScreen(Text.of("")))); + return Command.SINGLE_SUCCESS; + } + + private static void loadItems() { + HashSet bazaarItems = new HashSet<>(); + HashSet auctionItems = new HashSet<>(); + HashMap namesToId = new HashMap<>(); + + //get bazaar items + try { + if (TooltipInfoType.BAZAAR.getData() == null) TooltipInfoType.BAZAAR.run(); + + JsonObject products = TooltipInfoType.BAZAAR.getData(); + for (Map.Entry entry : products.entrySet()) { + if (entry.getValue().isJsonObject()) { + JsonObject product = entry.getValue().getAsJsonObject(); + String id = product.get("id").getAsString(); + int sellVolume = product.get("sellVolume").getAsInt(); + if (sellVolume == 0) + continue; //do not add items that do not sell e.g. they are not actual in the bazaar + Matcher matcher = BAZAAR_ENCHANTMENT_PATTERN.matcher(id); + if (matcher.matches()) {//format enchantments + //remove ultimate if in name + String name = matcher.group(1); + if (!name.contains("WISE")) { //only way found to remove ultimate from everything but ultimate wise + name = name.replace("ULTIMATE_", ""); + } + name = name.replace("_", " "); + name = capitalizeFully(name); + int enchantLevel = Integer.parseInt(matcher.group(2)); + String level = ""; + if (enchantLevel > 0) { + level = ROMAN_NUMERALS[enchantLevel - 1]; + } + bazaarItems.add(name + " " + level); + namesToId.put(name + " " + level, matcher.group(1) + ";" + matcher.group(2)); + continue; + } + //look up id for name + NEUItem neuItem = NEURepoManager.NEU_REPO.getItems().getItemBySkyblockId(id); + if (neuItem != null) { + String name = Formatting.strip(neuItem.getDisplayName()); + bazaarItems.add(name); + namesToId.put(name, id); + continue; + } + } + } + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to load bazaar item list! ", e); + } + + //get auction items + try { + if (TooltipInfoType.THREE_DAY_AVERAGE.getData() == null) { + TooltipInfoType.THREE_DAY_AVERAGE.run(); + } + for (Map.Entry entry : TooltipInfoType.THREE_DAY_AVERAGE.getData().entrySet()) { + String id = entry.getKey(); + + Matcher matcher = AUCTION_PET_AND_RUNE_PATTERN.matcher(id); + if (matcher.find()) {//is a pet or rune convert id to name + String name = matcher.group(1).replace("_", " "); + name = capitalizeFully(name); + auctionItems.add(name); + namesToId.put(name, id); + continue; + } + //something else look up in NEU repo. + id = id.split("[+-]")[0]; + NEUItem neuItem = NEURepoManager.NEU_REPO.getItems().getItemBySkyblockId(id); + if (neuItem != null) { + String name = Formatting.strip(neuItem.getDisplayName()); + auctionItems.add(name); + namesToId.put(name, id); + continue; + } + } + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to load auction house item list! ", e); + } + + SearchOverManager.bazaarItems = bazaarItems; + SearchOverManager.auctionItems = auctionItems; + SearchOverManager.namesToId = namesToId; + } + + /** + * Capitalizes the first letter off every word in a string + * @param str string to capitalize + */ + private static String capitalizeFully(String str) { + if (str == null || str.isEmpty()) { + return str; + } + + return Arrays.stream(str.split("\\s+")) + .map(t -> t.substring(0, 1).toUpperCase() + t.substring(1).toLowerCase()) + .collect(Collectors.joining(" ")); + } + + /** + * Receives data when a search is started and resets values + * @param sign the sign that is being edited + * @param front if it's the front of the sign + * @param isAuction if the sign is loaded from the auction house menu or bazaar + */ + public static void updateSign(@NotNull SignBlockEntity sign, boolean front, boolean isAuction) { + signFront = front; + SearchOverManager.sign = sign; + isCommand = false; + SearchOverManager.isAuction = isAuction; + if (SkyblockerConfigManager.get().general.searchOverlay.keepPreviousSearches) { + Text[] messages = SearchOverManager.sign.getText(signFront).getMessages(CLIENT.shouldFilterText()); + search = messages[0].getString(); + if (!messages[1].getString().isEmpty()) { + if (!search.endsWith(" ")) { + search += " "; + } + search += messages[1].getString(); + } + } else { + search = ""; + } + suggestionsArray = new String[]{}; + } + + /** + * Updates the search value and the suggestions based on that value. + * @param newValue new search value + */ + protected static void updateSearch(String newValue) { + search = newValue; + //update the suggestion values + int totalSuggestions = SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions; + if (newValue.isBlank() || totalSuggestions == 0) return; //do not search for empty value + suggestionsArray = (isAuction ? auctionItems : bazaarItems).stream().filter(item -> item.toLowerCase().contains(search.toLowerCase())).limit(totalSuggestions).toArray(String[]::new); + } + + /** + * Gets the suggestion in the suggestion array at the index + * @param index index of suggestion + */ + protected static String getSuggestion(int index) { + if (suggestionsArray.length > index && suggestionsArray[index] != null) { + return suggestionsArray[index]; + } else {//there are no suggestions yet + return ""; + } + } + + protected static String getSuggestionId(int index) { + return namesToId.get(getSuggestion(index)); + } + + /** + * Gets the item name in the history array at the index + * @param index index of suggestion + */ + protected static String getHistory(int index) { + if (isAuction) { + if (SkyblockerConfigManager.get().general.searchOverlay.auctionHistory.size() > index) { + return SkyblockerConfigManager.get().general.searchOverlay.auctionHistory.get(index); + } + } else { + if (SkyblockerConfigManager.get().general.searchOverlay.bazaarHistory.size() > index) { + return SkyblockerConfigManager.get().general.searchOverlay.bazaarHistory.get(index); + } + } + return null; + } + + protected static String getHistoryId(int index) { + return namesToId.get(getHistory(index)); + } + + /** + * Add the current search value to the start of the history list and truncate to the max history value and save this to the config + */ + private static void saveHistory() { + //save to history + SkyblockerConfig.SearchOverlay config = SkyblockerConfigManager.get().general.searchOverlay; + if (isAuction) { + if (config.auctionHistory.isEmpty() || !config.auctionHistory.get(0).equals(search)) { + config.auctionHistory.add(0, search); + if (config.auctionHistory.size() > config.historyLength) { + config.auctionHistory = config.auctionHistory.subList(0, config.historyLength); + } + } + } else { + if (config.bazaarHistory.isEmpty() || !config.bazaarHistory.get(0).equals(search)) { + config.bazaarHistory.add(0, search); + if (config.bazaarHistory.size() > config.historyLength) { + config.bazaarHistory = config.bazaarHistory.subList(0, config.historyLength); + } + } + } + SkyblockerConfigManager.save(); + } + + /** + *Saves the current value of ({@link SearchOverManager#search}) then pushes it to a command or sign depending on how the gui was opened + */ + protected static void pushSearch() { + //save to history + if (!search.isEmpty()) { + saveHistory(); + } + if (isCommand) { + pushCommand(); + } else { + pushSign(); + } + } + + /** + * runs the command to search for the value in ({@link SearchOverManager#search}) + */ + private static void pushCommand() { + if (search.isEmpty()) return; + + String command; + if (isAuction) { + command = "/ahSearch " + search; + } else { + command = "/bz " + search; + } + MessageScheduler.INSTANCE.sendMessageAfterCooldown(command); + } + + /** + * pushes the ({@link SearchOverManager#search}) to the sign. It needs to split it over two lines without splitting a word + */ + private static void pushSign() { + //splits text into 2 lines max = 30 chars + Pair split = splitString(search); + + // send packet to update sign + if (CLIENT.player != null && sign != null) { + Text[] messages = sign.getText(signFront).getMessages(CLIENT.shouldFilterText()); + CLIENT.player.networkHandler.sendPacket(new UpdateSignC2SPacket(sign.getPos(), signFront, + split.left(), + split.right(), + messages[2].getString(), + messages[3].getString() + )); + } + } + + public static Pair splitString(String s) { + if (s.length() <= 15) { + return Pair.of(s, ""); + } + int index = s.lastIndexOf(' ', 15); + if (index == -1) { + return Pair.of(s.substring(0, 15), ""); + } + return Pair.of(s.substring(0, index), s.substring(index + 1, Math.min(index + 16, s.length())).trim()); + } +} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/searchOverlay/SplitStringTest.java b/src/test/java/de/hysky/skyblocker/skyblock/searchOverlay/SplitStringTest.java deleted file mode 100644 index c397b5a4..00000000 --- a/src/test/java/de/hysky/skyblocker/skyblock/searchOverlay/SplitStringTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.hysky.skyblocker.skyblock.searchOverlay; - -import it.unimi.dsi.fastutil.Pair; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class SplitStringTest { - @Test - void testSplitString1() { - String input = "aaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa a"; - Pair split = SearchOverManager.splitString(input); - Assertions.assertEquals("aaaaaaaaaaaaaaa", split.left()); - Assertions.assertEquals("aaaaaaaaaaaaaaa", split.right()); - } - - @Test - void testSplitString2() { - String input = "a a a a aaa aa aa aaaa aaa aa aa aa a a aa aaa a aaa aa"; - Pair split = SearchOverManager.splitString(input); - Assertions.assertEquals("a a a a aaa aa", split.left()); - Assertions.assertEquals("aa aaaa aaa aa", split.right()); - } - - @Test - void testSplitString3() { - String input = "aaaaa aaaaa aaaaa"; - Pair split = SearchOverManager.splitString(input); - Assertions.assertEquals("aaaaa aaaaa", split.left()); - Assertions.assertEquals("aaaaa", split.right()); - } -} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/searchoverlay/SplitStringTest.java b/src/test/java/de/hysky/skyblocker/skyblock/searchoverlay/SplitStringTest.java new file mode 100644 index 00000000..a9fd541f --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/searchoverlay/SplitStringTest.java @@ -0,0 +1,31 @@ +package de.hysky.skyblocker.skyblock.searchoverlay; + +import it.unimi.dsi.fastutil.Pair; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class SplitStringTest { + @Test + void testSplitString1() { + String input = "aaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa a"; + Pair split = SearchOverManager.splitString(input); + Assertions.assertEquals("aaaaaaaaaaaaaaa", split.left()); + Assertions.assertEquals("aaaaaaaaaaaaaaa", split.right()); + } + + @Test + void testSplitString2() { + String input = "a a a a aaa aa aa aaaa aaa aa aa aa a a aa aaa a aaa aa"; + Pair split = SearchOverManager.splitString(input); + Assertions.assertEquals("a a a a aaa aa", split.left()); + Assertions.assertEquals("aa aaaa aaa aa", split.right()); + } + + @Test + void testSplitString3() { + String input = "aaaaa aaaaa aaaaa"; + Pair split = SearchOverManager.splitString(input); + Assertions.assertEquals("aaaaa aaaaa", split.left()); + Assertions.assertEquals("aaaaa", split.right()); + } +} -- cgit From c2e4a0513ef19aac52c38478fdd34a8d93990e75 Mon Sep 17 00:00:00 2001 From: Ghost <66458557+Ghost-3@users.noreply.github.com> Date: Sun, 18 Feb 2024 22:12:10 +0300 Subject: Better location management (#547) * Better location management * Location enum rework * Removed unused constants --- .../skyblocker/skyblock/crimson/kuudra/Kuudra.java | 1 - .../de/hysky/skyblocker/skyblock/rift/TheRift.java | 5 - .../java/de/hysky/skyblocker/utils/Location.java | 116 +++++++++++++++++++++ src/main/java/de/hysky/skyblocker/utils/Utils.java | 80 ++++++++------ 4 files changed, 167 insertions(+), 35 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/utils/Location.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java index 033a919d..5bc98894 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java @@ -10,7 +10,6 @@ import net.minecraft.text.Text; import net.minecraft.util.Formatting; public class Kuudra { - public static final String LOCATION = "kuudra"; static KuudraPhase phase = KuudraPhase.OTHER; public static void init() { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java index 02b694b6..7413e06f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java @@ -8,11 +8,6 @@ import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; public class TheRift { - /** - * @see de.hysky.skyblocker.utils.Utils#isInTheRift() Utils#isInTheRift(). - */ - public static final String LOCATION = "rift"; - public static void init() { WorldRenderEvents.AFTER_TRANSLUCENT.register(MirrorverseWaypoints::render); WorldRenderEvents.AFTER_TRANSLUCENT.register(EffigyWaypoints::render); diff --git a/src/main/java/de/hysky/skyblocker/utils/Location.java b/src/main/java/de/hysky/skyblocker/utils/Location.java new file mode 100644 index 00000000..bd2773fd --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/Location.java @@ -0,0 +1,116 @@ +package de.hysky.skyblocker.utils; + +import java.util.Arrays; + +/** + * All Skyblock locations + */ +public enum Location { + /** + * mode: dynamic + */ + PRIVATE_ISLAND("dynamic"), + /** + * mode: garden + */ + GARDEN("garden"), + /** + * mode: hub + */ + HUB("hub"), + /** + * mode: farming_1 + */ + THE_FARMING_ISLAND("farming_1"), + /** + * mode: foraging_1 + */ + THE_PARK("foraging_1"), + /** + * mode: combat_1 + */ + SPIDERS_DEN("combat_1"), + /** + * mode: combat_2 + */ + BLAZING_FORTRESS("combat_2"), + /** + * mode: combat_3 + */ + THE_END("combat_3"), + /** + * mode: crimson_isle + */ + CRIMSON_ISLE("crimson_isle"), + /** + * mode: mining_1 + */ + GOLD_MINE("mining_1"), + /** + * mode: mining_2 + */ + DEEP_CAVERNS("mining_2"), + /** + * mode: mining_3 + */ + DWARVEN_MINES("mining_3"), + /** + * mode: dungeon_hub + */ + DUNGEON_HUB("dungeon_hub"), + /** + * mode: winter + */ + WINTER_ISLAND("winter"), + /** + * mode: rift + */ + THE_RIFT("rift"), + /** + * mode: dark_auction + */ + DARK_AUCTION("dark_auction"), + /** + * mode: crystal_hollows + */ + CRYSTAL_HOLLOWS("crystal_hollows"), + /** + * mode: dungeon + */ + DUNGEON("dungeon"), + /** + * mode: kuudra + */ + KUUDRAS_HOLLOW("kuudra"), + /** + * Unknown Skyblock location + */ + UNKNOWN("unknown"); + + /** + * location id from Hypixel API + */ + private final String id; + + /** + * @param id location id from Hypixel API + */ + Location(String id) { + this.id = id; + } + + /** + * @return location id + */ + public String id() { + return this.id; + } + + /** + * @param id location id from Hypixel API + * @return location object + */ + public static Location from(String id) { + return Arrays.stream(Location.values()).filter(loc -> id.equals(loc.id())).findFirst().orElse(UNKNOWN); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index cd739a0c..08d0b167 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -3,10 +3,8 @@ package de.hysky.skyblocker.utils; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import de.hysky.skyblocker.events.SkyblockEvents; -import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; import de.hysky.skyblocker.skyblock.item.MuseumItemCache; import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; -import de.hysky.skyblocker.skyblock.rift.TheRift; import de.hysky.skyblocker.utils.scheduler.MessageScheduler; import de.hysky.skyblocker.utils.scheduler.Scheduler; import it.unimi.dsi.fastutil.objects.ObjectArrayList; @@ -37,14 +35,16 @@ import java.util.concurrent.CompletableFuture; public class Utils { private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class); private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", ""); - private static final String DUNGEONS_LOCATION = "dungeon"; - private static final String CRYSTAL_HOLLOWS_LOCATION = "crystal_hollows"; - private static final String DWARVEN_MINES_LOCATION = "mining_3"; private static final String PROFILE_PREFIX = "Profile: "; private static boolean isOnHypixel = false; private static boolean isOnSkyblock = false; private static boolean isInjected = false; + /** + * Current Skyblock location (from /locraw) + */ + @NotNull + private static Location location = Location.UNKNOWN; /** * The profile name parsed from the player list. */ @@ -88,31 +88,30 @@ public class Utils { } public static boolean isInDungeons() { - return getLocationRaw().equals(DUNGEONS_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment(); + return location == Location.DUNGEON || FabricLoader.getInstance().isDevelopmentEnvironment(); } public static boolean isInCrystalHollows() { - return getLocationRaw().equals(CRYSTAL_HOLLOWS_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment(); + return location == Location.CRYSTAL_HOLLOWS || FabricLoader.getInstance().isDevelopmentEnvironment(); } public static boolean isInDwarvenMines() { - return getLocationRaw().equals(DWARVEN_MINES_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment(); + return location == Location.DWARVEN_MINES || FabricLoader.getInstance().isDevelopmentEnvironment(); } public static boolean isInTheRift() { - return getLocationRaw().equals(TheRift.LOCATION); + return location == Location.THE_RIFT; } /** * @return if the player is in the end island */ public static boolean isInTheEnd() { - // /locraw returns "combat_3" when in The End - return getLocationRaw().equals("combat_3"); + return location == Location.THE_END; } public static boolean isInKuudra() { - return getLocationRaw().equals(Kuudra.LOCATION); + return location == Location.KUUDRAS_HOLLOW; } public static boolean isInjected() { @@ -132,6 +131,14 @@ public class Utils { return profileId; } + /** + * @return the location parsed from /locraw. + */ + @NotNull + public static Location getLocation() { + return location; + } + /** * @return the server parsed from /locraw. */ @@ -375,6 +382,32 @@ public class Utils { } } + /** + * Parses /locraw chat message and updates {@link #server}, {@link #gameType}, {@link #locationRaw}, {@link #map} + * and {@link #location} + * + * @param message json message from chat + */ + private static void parseLocRaw(String message) { + JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject(); + + if (locRaw.has("server")) { + server = locRaw.get("server").getAsString(); + } + if (locRaw.has("gameType")) { + gameType = locRaw.get("gameType").getAsString(); + } + if (locRaw.has("mode")) { + locationRaw = locRaw.get("mode").getAsString(); + location = Location.from(locationRaw); + } else { + location = Location.UNKNOWN; + } + if (locRaw.has("map")) { + map = locRaw.get("map").getAsString(); + } + } + /** * Parses the /locraw reply from the server and updates the player's profile id * @@ -382,25 +415,13 @@ public class Utils { */ public static boolean onChatMessage(Text text, boolean overlay) { String message = text.getString(); - if (message.startsWith("{\"server\":") && message.endsWith("}")) { - JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject(); - if (locRaw.has("server")) { - server = locRaw.get("server").getAsString(); - if (locRaw.has("gameType")) { - gameType = locRaw.get("gameType").getAsString(); - } - if (locRaw.has("mode")) { - locationRaw = locRaw.get("mode").getAsString(); - } - if (locRaw.has("map")) { - map = locRaw.get("map").getAsString(); - } - boolean shouldFilter = !sentLocRaw; - sentLocRaw = false; + if (message.startsWith("{\"server\":") && message.endsWith("}")) { + parseLocRaw(message); + boolean shouldFilter = !sentLocRaw; + sentLocRaw = false; - return shouldFilter; - } + return shouldFilter; } if (isOnSkyblock && message.startsWith("Profile ID: ")) { @@ -419,6 +440,7 @@ public class Utils { gameType = ""; locationRaw = ""; map = ""; + location = Location.UNKNOWN; } private static void tickMayorCache(boolean refresh) { -- cgit From 114e455ae4578c848b59b7a839c6ae043060edb9 Mon Sep 17 00:00:00 2001 From: Ghost <66458557+Ghost-3@users.noreply.github.com> Date: Sun, 18 Feb 2024 23:17:33 +0300 Subject: Spider's Den server Widget --- .../hysky/skyblocker/skyblock/tabhud/util/Ico.java | 1 + .../tabhud/widget/SpidersDenServerWidget.java | 84 ++++++++++++++++++++++ .../skyblocker/tabhud/standard/spider_den.json | 17 +++++ .../skyblocker/tabhud/standard/spider_den.json | 17 +++++ 4 files changed, 119 insertions(+) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java create mode 100644 src/main/resources/assets/skyblocker/tabhud/standard/spider_den.json create mode 100644 src/main/resources/resourcepacks/top_aligned/assets/skyblocker/tabhud/standard/spider_den.json (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java index 82394a78..96ab35d5 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java @@ -60,4 +60,5 @@ public class Ico { public static final ItemStack EXPERIENCE_BOTTLE = new ItemStack(Items.EXPERIENCE_BOTTLE); public static final ItemStack PINK_DYE = new ItemStack(Items.PINK_DYE); public static final ItemStack ENCHANTED_BOOK = new ItemStack(Items.ENCHANTED_BOOK); + public static final ItemStack SPIDER_EYE = new ItemStack(Items.SPIDER_EYE); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java new file mode 100644 index 00000000..3c32f534 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java @@ -0,0 +1,84 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.Arrays; + +/** + * This widget shows info about the Spider's Den server + */ +public class SpidersDenServerWidget extends Widget { + + private static final MutableText TITLE = + Text.literal("Server Info").formatted(Formatting.DARK_AQUA, Formatting.BOLD); + + /** + * Broodmother Mini-Boss tab states + */ + private enum BroodmotherState { + SOON("Soon", Formatting.GOLD), + AWAKENING("Awakening", Formatting.GOLD), + ALIVE("Alive!", Formatting.DARK_RED), + DORMANT("Dormant", Formatting.YELLOW), + UNKNOWN("Unknown", Formatting.GRAY); + + private final String text; + private final Formatting formatting; + + + BroodmotherState(String text, Formatting formatting) { + this.text = text; + this.formatting = formatting; + } + + public String text() { + return this.text; + } + + public Formatting formatting() { + return this.formatting; + } + + /** + * Returns a state object by text + * + * @param text text state from tab + * @return Broodmother State object + */ + public static BroodmotherState from(String text) { + return Arrays.stream(BroodmotherState.values()) + .filter(broodmotherState -> text.equals(broodmotherState.text())).findFirst().orElse(UNKNOWN); + } + } + + public SpidersDenServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + /** + * Parses the Broodmother string from tab and returns a state object. + * + * @return Broodmother State object + */ + private static BroodmotherState parseTab() { + String state = PlayerListMgr.strAt(45); + if (state == null || !state.contains(": ")) return BroodmotherState.UNKNOWN; + + return BroodmotherState.from(state.split(": ")[1]); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); + + BroodmotherState broodmotherState = parseTab(); + this.addSimpleIcoText( + Ico.SPIDER_EYE, "Broodmother: ", broodmotherState.formatting(), broodmotherState.text()); + } +} diff --git a/src/main/resources/assets/skyblocker/tabhud/standard/spider_den.json b/src/main/resources/assets/skyblocker/tabhud/standard/spider_den.json new file mode 100644 index 00000000..faa437ac --- /dev/null +++ b/src/main/resources/assets/skyblocker/tabhud/standard/spider_den.json @@ -0,0 +1,17 @@ +{ + "widgets": [ + { + "name": "SpidersDenServerWidget", + "alias": "psw" + } + ], + "layout": [ + { + "op": "place", + "where": "center", + "apply_to": [ + "psw" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/resourcepacks/top_aligned/assets/skyblocker/tabhud/standard/spider_den.json b/src/main/resources/resourcepacks/top_aligned/assets/skyblocker/tabhud/standard/spider_den.json new file mode 100644 index 00000000..0df51096 --- /dev/null +++ b/src/main/resources/resourcepacks/top_aligned/assets/skyblocker/tabhud/standard/spider_den.json @@ -0,0 +1,17 @@ +{ + "widgets": [ + { + "name": "SpidersDenServerWidget", + "alias": "psw" + } + ], + "layout": [ + { + "op": "place", + "where": "centerTop", + "apply_to": [ + "psw" + ] + } + ] +} \ No newline at end of file -- cgit From 3cfad4cfc4da62ab0ff2f76846201d30e81c3ef7 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sun, 18 Feb 2024 15:25:44 -0500 Subject: Fix powder hud not updating when commissions hud is disabled (#550) --- src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main/java') 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 5913a3c6..608873c0 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java @@ -203,8 +203,8 @@ public class DwarvenHud { } public static void update() { - if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions || (!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<>(); -- cgit From ed48570bc262bf0a9ebe2e3a629d389ee22fd3fe Mon Sep 17 00:00:00 2001 From: Ghost <66458557+Ghost-3@users.noreply.github.com> Date: Sun, 18 Feb 2024 23:27:41 +0300 Subject: Minor fix --- .../skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java index 3c32f534..386e60ec 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java @@ -29,7 +29,6 @@ public class SpidersDenServerWidget extends Widget { private final String text; private final Formatting formatting; - BroodmotherState(String text, Formatting formatting) { this.text = text; this.formatting = formatting; @@ -71,6 +70,9 @@ public class SpidersDenServerWidget extends Widget { return BroodmotherState.from(state.split(": ")[1]); } + /** + * Updates the information in the widget. + */ @Override public void updateContent() { this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); -- cgit From b96921dad617a4cc1fd36b6529596c1391f43f55 Mon Sep 17 00:00:00 2001 From: Ghost <66458557+Ghost-3@users.noreply.github.com> Date: Mon, 19 Feb 2024 07:20:23 +0300 Subject: Added Broodmother states --- .../skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java index 386e60ec..6751ea5d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java @@ -22,7 +22,9 @@ public class SpidersDenServerWidget extends Widget { private enum BroodmotherState { SOON("Soon", Formatting.GOLD), AWAKENING("Awakening", Formatting.GOLD), + IMMINENT("Imminent", Formatting.DARK_RED), ALIVE("Alive!", Formatting.DARK_RED), + SLAIN("Slain", Formatting.YELLOW), DORMANT("Dormant", Formatting.YELLOW), UNKNOWN("Unknown", Formatting.GRAY); @@ -80,7 +82,6 @@ public class SpidersDenServerWidget extends Widget { this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); BroodmotherState broodmotherState = parseTab(); - this.addSimpleIcoText( - Ico.SPIDER_EYE, "Broodmother: ", broodmotherState.formatting(), broodmotherState.text()); + this.addSimpleIcoText(Ico.SPIDER_EYE, "Broodmother: ", broodmotherState.formatting(), broodmotherState.text()); } } -- cgit From 8330e0be154fec3927dadbcdfb3a1ba3e7364dce Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:38:29 -0500 Subject: Improve secrets caching behaviour (#545) --- .../skyblock/dungeon/secrets/SecretsTracker.java | 71 ++++++++++++---------- 1 file changed, 39 insertions(+), 32 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java index 68f09344..2f748792 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java @@ -10,9 +10,6 @@ import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.Http; import de.hysky.skyblocker.utils.Http.ApiResponse; import de.hysky.skyblocker.utils.Utils; -import it.unimi.dsi.fastutil.ints.IntIntPair; -import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.minecraft.client.MinecraftClient; @@ -24,6 +21,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -43,8 +41,6 @@ public class SecretsTracker { ClientReceiveMessageEvents.GAME.register(SecretsTracker::onMessage); } - //If -1 is somehow encountered, it would be very rare, so I just disregard its possibility for now - //people would probably recognize if it was inaccurate so yeah private static void calculate(RunPhase phase) { switch (phase) { case START -> CompletableFuture.runAsync(() -> { @@ -57,11 +53,11 @@ public class SecretsTracker { //The player name will be blank if there isn't a player at that index if (!playerName.isEmpty()) { - //If the player was a part of the last run (and didn't have -1 secret count) and that run ended less than 5 mins ago then copy the secrets over - if (lastRun != null && System.currentTimeMillis() <= lastRunEnded + 300_000 && lastRun.secretCounts().getOrDefault(playerName, -1) != -1) { - newlyStartedRun.secretCounts().put(playerName, lastRun.secretCounts().getInt(playerName)); + //If the player was a part of the last run, had non-empty secret data and that run ended less than 5 mins ago then copy the secret data over + if (lastRun != null && System.currentTimeMillis() <= lastRunEnded + 300_000 && lastRun.playersSecretData().getOrDefault(playerName, SecretData.EMPTY) != SecretData.EMPTY) { + newlyStartedRun.playersSecretData().put(playerName, lastRun.playersSecretData().get(playerName)); } else { - newlyStartedRun.secretCounts().put(playerName, getPlayerSecrets(playerName).leftInt()); + newlyStartedRun.playersSecretData().put(playerName, getPlayerSecrets(playerName)); } } } @@ -72,22 +68,23 @@ public class SecretsTracker { case END -> CompletableFuture.runAsync(() -> { //In case the game crashes from something if (currentRun != null) { - Object2ObjectOpenHashMap secretsFound = new Object2ObjectOpenHashMap<>(); + Object2ObjectOpenHashMap secretsFound = new Object2ObjectOpenHashMap<>(); //Update secret counts - for (Entry entry : currentRun.secretCounts().object2IntEntrySet()) { + for (Entry entry : currentRun.playersSecretData().entrySet()) { String playerName = entry.getKey(); - int startingSecrets = entry.getIntValue(); - IntIntPair secretsNow = getPlayerSecrets(playerName); - int secretsPlayerFound = secretsNow.leftInt() - startingSecrets; + SecretData startingSecrets = entry.getValue(); + SecretData secretsNow = getPlayerSecrets(playerName); + int secretsPlayerFound = secretsNow.secrets() - startingSecrets.secrets(); - secretsFound.put(playerName, IntIntPair.of(secretsPlayerFound, secretsNow.rightInt())); - entry.setValue(secretsNow.leftInt()); + //Add an entry to the secretsFound map with the data - if the secret data from now or the start was cached a warning will be shown + secretsFound.put(playerName, secretsNow.updated(secretsPlayerFound, startingSecrets.cached() || secretsNow.cached())); + entry.setValue(secretsNow); } //Print the results all in one go, so its clean and less of a chance of it being broken up - for (Map.Entry entry : secretsFound.entrySet()) { - sendResultMessage(entry.getKey(), entry.getValue().leftInt(), entry.getValue().rightInt(), true); + for (Map.Entry entry : secretsFound.entrySet()) { + sendResultMessage(entry.getKey(), entry.getValue(), true); } //Swap the current and last run as well as mark the run end time @@ -95,30 +92,31 @@ public class SecretsTracker { lastRun = currentRun; currentRun = null; } else { - sendResultMessage(null, -1, -1, false); + sendResultMessage(null, null, false); } }); } } - private static void sendResultMessage(String player, int secrets, int cacheAge, boolean success) { + private static void sendResultMessage(String player, SecretData secretData, boolean success) { + @SuppressWarnings("resource") PlayerEntity playerEntity = MinecraftClient.getInstance().player; if (playerEntity != null) { if (success) { - playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.feedback", Text.literal(player).withColor(0xf57542), "§7" + secrets, getCacheText(cacheAge)))); + playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.feedback", Text.literal(player).withColor(0xf57542), "§7" + secretData.secrets(), getCacheText(secretData.cached(), secretData.cacheAge())))); } else { playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.failFeedback"))); } } } - private static Text getCacheText(int cacheAge) { - return Text.literal("\u2139").styled(style -> style.withColor(cacheAge == -1 ? 0x218bff : 0xeac864).withHoverEvent( - new HoverEvent(HoverEvent.Action.SHOW_TEXT, cacheAge == -1 ? Text.translatable("skyblocker.api.cache.MISS") : Text.translatable("skyblocker.api.cache.HIT", cacheAge)))); + private static Text getCacheText(boolean cached, int cacheAge) { + return Text.literal("\u2139").styled(style -> style.withColor(cached ? 0xeac864 : 0x218bff).withHoverEvent( + new HoverEvent(HoverEvent.Action.SHOW_TEXT, cached ? Text.translatable("skyblocker.api.cache.HIT", cacheAge) : Text.translatable("skyblocker.api.cache.MISS")))); } private static void onMessage(Text text, boolean overlay) { - if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.playerSecretsTracker) { + if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.playerSecretsTracker && !overlay) { String message = Formatting.strip(text.getString()); try { @@ -136,35 +134,44 @@ public class SecretsTracker { return matcher != null ? matcher.group("name") : ""; } - private static IntIntPair getPlayerSecrets(String name) { + private static SecretData getPlayerSecrets(String name) { String uuid = ApiUtils.name2Uuid(name); if (!uuid.isEmpty()) { try (ApiResponse response = Http.sendHypixelRequest("player", "?uuid=" + uuid)) { - return IntIntPair.of(getSecretCountFromAchievements(JsonParser.parseString(response.content()).getAsJsonObject()), response.age()); + return new SecretData(getSecretCountFromAchievements(JsonParser.parseString(response.content()).getAsJsonObject()), response.cached(), response.age()); } catch (Exception e) { LOGGER.error("[Skyblocker] Encountered an error while trying to fetch {} secret count!", name + "'s", e); } } - return IntIntPair.of(-1, -1); + return SecretData.EMPTY; } /** * Gets a player's secret count from their hypixel achievements */ private static int getSecretCountFromAchievements(JsonObject playerJson) { - JsonObject player = playerJson.get("player").getAsJsonObject(); - JsonObject achievements = (player.has("achievements")) ? player.get("achievements").getAsJsonObject() : null; + JsonObject player = playerJson.getAsJsonObject("player"); + JsonObject achievements = player.has("achievements") ? player.getAsJsonObject("achievements") : null; return (achievements != null && achievements.has("skyblock_treasure_hunter")) ? achievements.get("skyblock_treasure_hunter").getAsInt() : 0; } /** * This will either reflect the value at the start or the end depending on when this is called */ - private record TrackedRun(Object2IntOpenHashMap secretCounts) { + private record TrackedRun(Object2ObjectOpenHashMap playersSecretData) { private TrackedRun() { - this(new Object2IntOpenHashMap<>()); + this(new Object2ObjectOpenHashMap<>()); + } + } + + private record SecretData(int secrets, boolean cached, int cacheAge) { + private static final SecretData EMPTY = new SecretData(0, false, 0); + + //If only we had Derived Record Creation :( - https://bugs.openjdk.org/browse/JDK-8321133 + private SecretData updated(int secrets, boolean cached) { + return new SecretData(secrets, cached, this.cacheAge); } } -- cgit From 12a966eec12b21166bf66ba2ec78bc00d92427a8 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:39:16 -0500 Subject: Fix beacon highlights persisting after the boss dies (#554) --- .../skyblocker/skyblock/end/BeaconHighlighter.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java b/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java index f69fca6d..6c89a07c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java @@ -3,8 +3,11 @@ package de.hysky.skyblocker.skyblock.end; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import java.util.ArrayList; @@ -20,6 +23,20 @@ public class BeaconHighlighter { */ public static void init() { WorldRenderEvents.AFTER_TRANSLUCENT.register(BeaconHighlighter::render); + ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> reset()); + ClientReceiveMessageEvents.GAME.register(BeaconHighlighter::onMessage); + } + + private static void reset() { + beaconPositions.clear(); + } + + private static void onMessage(Text text, boolean overlay) { + if (Utils.isInTheEnd() && !overlay) { + String message = text.getString(); + + if (message.contains("SLAYER QUEST COMPLETE!") || message.contains("NICE! SLAYER BOSS SLAIN!")) reset(); + } } /** @@ -28,7 +45,7 @@ public class BeaconHighlighter { * * @param context An instance of WorldRenderContext for the RenderHelper to use */ - public static void render(WorldRenderContext context) { + private static void render(WorldRenderContext context) { if (Utils.isInTheEnd() && SkyblockerConfigManager.get().slayer.endermanSlayer.highlightBeacons) { for (BlockPos pos : beaconPositions) { RenderHelper.renderFilled(context, pos, RED_COLOR_COMPONENTS, 0.5f, false); -- cgit From 2ffb38db700cabd13438e04b6d04811294230230 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:59:34 -0500 Subject: Add solvers --- .../skyblock/dungeon/puzzle/Boulder.java | 21 +++++++++++++++++++++ .../skyblock/dungeon/puzzle/IceFill.java | 9 +++++++++ .../skyblock/dungeon/puzzle/Silverfish.java | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Boulder.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Boulder.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Boulder.java new file mode 100644 index 00000000..8987599a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Boulder.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.skyblock.dungeon.puzzle; + +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.client.MinecraftClient; + +// right: 5, 63, 8 +// left: 25, 63, 8 +// jungle, birch +public class Boulder extends DungeonPuzzle { + public Boulder() { + super("Boulder", "boxes-room"); + } + + @Override + public void tick(MinecraftClient client) { + } + + @Override + public void render(WorldRenderContext context) { + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java new file mode 100644 index 00000000..069b565e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java @@ -0,0 +1,9 @@ +package de.hysky.skyblocker.skyblock.dungeon.puzzle; + +// 1: 15, 69, 7 +// 2: 15, 70, 12 +// 3: 15, 71, 19 +// ice -> packed_ice +// polished andesite +public class IceFill { +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java new file mode 100644 index 00000000..0784f4ed --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java @@ -0,0 +1,22 @@ +package de.hysky.skyblocker.skyblock.dungeon.puzzle; + +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.client.MinecraftClient; + +//right: 7, 66, 8 +//left: 23, 66, 8 +//right back: 7, 66, 24 +//polished andesite +public class Silverfish extends DungeonPuzzle { + public Silverfish() { + super("Silverfish", "ice-silverfish-room"); + } + + @Override + public void tick(MinecraftClient client) { + } + + @Override + public void render(WorldRenderContext context) { + } +} -- cgit From 443753ad8ed3e9e98e70881a733003a9b6386499 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Thu, 1 Feb 2024 22:35:25 -0500 Subject: Add silverfish board parsing --- .../java/de/hysky/skyblocker/SkyblockerMod.java | 8 +- .../hysky/skyblocker/config/SkyblockerConfig.java | 6 + .../skyblock/dungeon/puzzle/Boulder.java | 21 --- .../skyblock/dungeon/puzzle/IceFill.java | 25 ++- .../skyblock/dungeon/puzzle/Silverfish.java | 177 ++++++++++++++++++++- 5 files changed, 209 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Boulder.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index b8722fc6..dca41bd1 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -8,18 +8,16 @@ import de.hysky.skyblocker.skyblock.*; import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; import de.hysky.skyblocker.skyblock.dungeon.*; import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen; +import de.hysky.skyblocker.skyblock.dungeon.puzzle.*; import de.hysky.skyblocker.skyblock.dungeon.puzzle.boulder.Boulder; -import de.hysky.skyblocker.skyblock.dungeon.puzzle.CreeperBeams; -import de.hysky.skyblocker.skyblock.dungeon.puzzle.DungeonBlaze; -import de.hysky.skyblocker.skyblock.dungeon.puzzle.TicTacToe; import de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard.Waterboard; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.dungeon.secrets.SecretsTracker; import de.hysky.skyblocker.skyblock.dwarven.CrystalsHud; import de.hysky.skyblocker.skyblock.dwarven.CrystalsLocationsManager; import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud; -import de.hysky.skyblocker.skyblock.end.TheEnd; import de.hysky.skyblocker.skyblock.end.BeaconHighlighter; +import de.hysky.skyblocker.skyblock.end.TheEnd; import de.hysky.skyblocker.skyblock.item.*; import de.hysky.skyblocker.skyblock.item.tooltip.BackpackPreview; import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; @@ -117,6 +115,8 @@ public class SkyblockerMod implements ClientModInitializer { DungeonManager.init(); DungeonBlaze.init(); Waterboard.init(); + Silverfish.init(); + IceFill.init(); DungeonScore.init(); PartyFinderScreen.initClass(); ChestValue.init(); diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index b5ddcf5d..78458291 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -722,6 +722,12 @@ public class SkyblockerConfig { @SerialEntry public boolean solveBoulder = true; + @SerialEntry + public boolean solveIceFill = true; + + @SerialEntry + public boolean solveSilverfish = true; + @SerialEntry public boolean fireFreezeStaffTimer = true; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Boulder.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Boulder.java deleted file mode 100644 index 8987599a..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Boulder.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.hysky.skyblocker.skyblock.dungeon.puzzle; - -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.client.MinecraftClient; - -// right: 5, 63, 8 -// left: 25, 63, 8 -// jungle, birch -public class Boulder extends DungeonPuzzle { - public Boulder() { - super("Boulder", "boxes-room"); - } - - @Override - public void tick(MinecraftClient client) { - } - - @Override - public void render(WorldRenderContext context) { - } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java index 069b565e..3eee8a7c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java @@ -1,9 +1,32 @@ package de.hysky.skyblocker.skyblock.dungeon.puzzle; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.client.MinecraftClient; + // 1: 15, 69, 7 // 2: 15, 70, 12 // 3: 15, 71, 19 // ice -> packed_ice // polished andesite -public class IceFill { +public class IceFill extends DungeonPuzzle { + public static final IceFill INSTANCE = new IceFill(); + + public IceFill() { + super("ice-fill", "ice-path"); + } + + public static void init() {} + + @Override + public void tick(MinecraftClient client) { + if (!SkyblockerConfigManager.get().locations.dungeons.solveIceFill) { + return; + } + } + + @Override + public void render(WorldRenderContext context) { + + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java index 0784f4ed..195e38f7 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java @@ -1,22 +1,195 @@ package de.hysky.skyblocker.skyblock.dungeon.puzzle; +import com.mojang.brigadier.Command; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.debug.Debug; +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; +import de.hysky.skyblocker.skyblock.dungeon.secrets.Room; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.mob.SilverfishEntity; +import net.minecraft.util.DyeColor; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import org.joml.Vector2i; +import org.joml.Vector2ic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; //right: 7, 66, 8 //left: 23, 66, 8 //right back: 7, 66, 24 //polished andesite public class Silverfish extends DungeonPuzzle { - public Silverfish() { - super("Silverfish", "ice-silverfish-room"); + private static final Logger LOGGER = LoggerFactory.getLogger(Silverfish.class); + public static final Silverfish INSTANCE = new Silverfish(); + private static final float[] LIME_COLOR_COMPONENTS = DyeColor.LIME.getColorComponents(); + private final boolean[][] silverfishBoard = new boolean[17][17]; + private Vector2ic silverfishPos; + private final List silverfishPath = new ArrayList<>(); + + private Silverfish() { + super("silverfish", "ice-silverfish-room"); + if (Debug.debugEnabled()) { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(puzzleName) + .then(literal("printBoard").executes(context -> { + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(silverfishBoard))); + return Command.SINGLE_SUCCESS; + })).then(literal("printPath").executes(context -> { + context.getSource().sendFeedback(Constants.PREFIX.get().append(silverfishPath.toString())); + return Command.SINGLE_SUCCESS; + })) + ))))); + } + } + + public static void init() {} + + private static String boardToString(boolean[][] silverfishBoard) { + StringBuilder sb = new StringBuilder(); + for (boolean[] row : silverfishBoard) { + sb.append("\n"); + for (boolean cell : row) { + sb.append(cell ? '#' : '.'); + } + } + return sb.toString(); } @Override public void tick(MinecraftClient client) { + if (!SkyblockerConfigManager.get().locations.dungeons.solveSilverfish || client.world == null || !DungeonManager.isCurrentRoomMatched()) { + return; + } + Room room = DungeonManager.getCurrentRoom(); + + boolean boardChanged = false; + BlockPos.Mutable pos = new BlockPos.Mutable(23, 67, 24); + for (int row = 0; row < silverfishBoard.length; pos.move(silverfishBoard[row].length, 0, -1), row++) { + for (int col = 0; col < silverfishBoard[row].length; pos.move(Direction.WEST), col++) { + boolean isBlock = !client.world.getBlockState(room.relativeToActual(pos)).isAir(); + if (silverfishBoard[row][col] != isBlock) { + silverfishBoard[row][col] = isBlock; + boardChanged = true; + } + } + } + + BlockPos blockPos = room.relativeToActual(new BlockPos(16, 16, 16)); + List entities = client.world.getEntitiesByClass(SilverfishEntity.class, Box.of(Vec3d.ofCenter(blockPos), 32, 32, 32), silverfishEntity -> true); + if (entities.isEmpty()) { + return; + } + BlockPos newSilverfishBlockPos = room.actualToRelative(entities.get(0).getBlockPos()); + Vector2ic newSilverfishPos = new Vector2i(23 - newSilverfishBlockPos.getX(), 24 - newSilverfishBlockPos.getZ()); + if (newSilverfishPos.x() < 0 || newSilverfishPos.x() >= 17 || newSilverfishPos.y() < 0 || newSilverfishPos.y() >= 17) { + return; + } + boolean silverfishChanged = !newSilverfishPos.equals(silverfishPos); + if (silverfishChanged) { + silverfishPos = newSilverfishPos; + } + if (silverfishChanged || boardChanged) { + solve(); + } + } + + private void solve() { + if (silverfishPos == null) { + return; + } + Set visited = new HashSet<>(); + Queue> queue = new ArrayDeque<>(); + queue.add(List.of(silverfishPos)); + while (!queue.isEmpty()) { + List path = queue.poll(); + Vector2ic pos = path.get(path.size() - 1); + if (pos.equals(8, 0)) { + silverfishPath.clear(); + silverfishPath.addAll(path); + return; + } + + Vector2i posMutable = new Vector2i(pos); + while (posMutable.x() < 17 && !silverfishBoard[posMutable.x()][posMutable.y()]) { + posMutable.add(1, 0); + } + posMutable.add(-1, 0); + if (!visited.contains(posMutable)) { + ArrayList newPath = new ArrayList<>(path); + newPath.add(new Vector2i(posMutable)); + queue.add(newPath); + visited.add(posMutable); + } + + posMutable.set(pos); + while (posMutable.x() >= 0 && !silverfishBoard[posMutable.x()][posMutable.y()]) { + posMutable.add(-1, 0); + } + posMutable.add(1, 0); + if (!visited.contains(posMutable)) { + ArrayList newPath = new ArrayList<>(path); + newPath.add(new Vector2i(posMutable)); + queue.add(newPath); + visited.add(posMutable); + } + + posMutable.set(pos); + while (posMutable.y() < 17 && !silverfishBoard[posMutable.x()][posMutable.y()]) { + posMutable.add(0, 1); + } + posMutable.add(0, -1); + if (!visited.contains(posMutable)) { + ArrayList newPath = new ArrayList<>(path); + newPath.add(new Vector2i(posMutable)); + queue.add(newPath); + visited.add(posMutable); + } + + posMutable.set(pos); + while (posMutable.y() >= 0 && !silverfishBoard[posMutable.x()][posMutable.y()]) { + posMutable.add(0, -1); + } + posMutable.add(0, 1); + if (!visited.contains(posMutable)) { + ArrayList newPath = new ArrayList<>(path); + newPath.add(new Vector2i(posMutable)); + queue.add(newPath); + visited.add(posMutable); + } + } } @Override public void render(WorldRenderContext context) { + if (!SkyblockerConfigManager.get().locations.dungeons.solveSilverfish || !DungeonManager.isCurrentRoomMatched() || silverfishPath.isEmpty()) { + return; + } + Room room = DungeonManager.getCurrentRoom(); + BlockPos.Mutable pos = new BlockPos.Mutable(); + for (int i = 0; i < silverfishPath.size() - 1; i++) { + Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i).x(), 67, 24 - silverfishPath.get(i).y()))); + Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i + 1).x(), 67, 24 - silverfishPath.get(i + 1).y()))); + RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, end}, LIME_COLOR_COMPONENTS, 1f, 5f, true); + } + } + + @Override + public void reset() { + super.reset(); + for (boolean[] silverfishBoardRow : silverfishBoard) { + Arrays.fill(silverfishBoardRow, false); + } + silverfishPos = null; } } -- cgit From ca61d618df013434bce4fb6995bc40aec4dc07dc Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:40:24 -0500 Subject: Finish silverfish and ice fill solvers --- .../skyblock/dungeon/puzzle/IceFill.java | 171 ++++++++++++++++++++- .../skyblock/dungeon/puzzle/Silverfish.java | 82 ++++------ .../dungeon/puzzle/waterboard/Waterboard.java | 14 +- .../skyblock/dungeon/puzzle/SilverfishTest.java | 40 +++++ 4 files changed, 244 insertions(+), 63 deletions(-) create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/SilverfishTest.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java index 3eee8a7c..7ab96d78 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java @@ -1,32 +1,187 @@ package de.hysky.skyblocker.skyblock.dungeon.puzzle; +import com.google.common.primitives.Booleans; +import com.mojang.brigadier.Command; +import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.debug.Debug; +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; +import de.hysky.skyblocker.skyblock.dungeon.secrets.Room; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.minecraft.client.MinecraftClient; +import net.minecraft.util.DyeColor; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import org.joml.Vector2i; +import org.joml.Vector2ic; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; -// 1: 15, 69, 7 -// 2: 15, 70, 12 -// 3: 15, 71, 19 -// ice -> packed_ice -// polished andesite public class IceFill extends DungeonPuzzle { public static final IceFill INSTANCE = new IceFill(); + private static final float[] RED_COLOR_COMPONENTS = DyeColor.RED.getColorComponents(); + private static final BlockPos BOARD_1_ORIGIN = new BlockPos(16, 70, 9); + private static final BlockPos BOARD_2_ORIGIN = new BlockPos(17, 71, 16); + private static final BlockPos BOARD_3_ORIGIN = new BlockPos(18, 72, 25); + private CompletableFuture solve; + private final boolean[][] iceFillBoard1 = new boolean[3][3]; + private final boolean[][] iceFillBoard2 = new boolean[5][5]; + private final boolean[][] iceFillBoard3 = new boolean[7][7]; + private final List iceFillPath1 = new ArrayList<>(); + private final List iceFillPath2 = new ArrayList<>(); + private final List iceFillPath3 = new ArrayList<>(); - public IceFill() { + private IceFill() { super("ice-fill", "ice-path"); } - public static void init() {} + public static void init() { + if (Debug.debugEnabled()) { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName) + .then(literal("printBoard1").executes(context -> { + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoard1))); + return Command.SINGLE_SUCCESS; + })).then(literal("printBoard2").executes(context -> { + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoard2))); + return Command.SINGLE_SUCCESS; + })).then(literal("printBoard3").executes(context -> { + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoard3))); + return Command.SINGLE_SUCCESS; + })).then(literal("printPath1").executes(context -> { + context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPath1.toString())); + return Command.SINGLE_SUCCESS; + })).then(literal("printPath2").executes(context -> { + context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPath2.toString())); + return Command.SINGLE_SUCCESS; + })).then(literal("printPath3").executes(context -> { + context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPath3.toString())); + return Command.SINGLE_SUCCESS; + })) + ))))); + } + } + + private static String boardToString(boolean[][] iceFillBoard) { + StringBuilder sb = new StringBuilder(); + for (boolean[] row : iceFillBoard) { + sb.append("\n"); + for (boolean cell : row) { + sb.append(cell ? '#' : '.'); + } + } + return sb.toString(); + } @Override public void tick(MinecraftClient client) { - if (!SkyblockerConfigManager.get().locations.dungeons.solveIceFill) { + if (!SkyblockerConfigManager.get().locations.dungeons.solveIceFill || client.world == null || !DungeonManager.isCurrentRoomMatched() || solve != null && !solve.isDone()) { return; } + Room room = DungeonManager.getCurrentRoom(); + + solve = CompletableFuture.runAsync(() -> { + BlockPos.Mutable pos = new BlockPos.Mutable(); + boolean board1Changed = updateBoard(client.world, room, iceFillBoard1, pos.set(BOARD_1_ORIGIN)); + boolean board2Changed = updateBoard(client.world, room, iceFillBoard2, pos.set(BOARD_2_ORIGIN)); + boolean board3Changed = updateBoard(client.world, room, iceFillBoard3, pos.set(BOARD_3_ORIGIN)); + + if (board1Changed) { + solve(iceFillBoard1, iceFillPath1); + } + if (board2Changed) { + solve(iceFillBoard2, iceFillPath2); + } + if (board3Changed) { + solve(iceFillBoard3, iceFillPath3); + } + }); + } + + private boolean updateBoard(World world, Room room, boolean[][] iceFillBoard, BlockPos.Mutable pos) { + boolean boardChanged = false; + for (int row = 0; row < iceFillBoard.length; pos.move(iceFillBoard[row].length, 0, -1), row++) { + for (int col = 0; col < iceFillBoard[row].length; pos.move(Direction.WEST), col++) { + BlockPos actualPos = room.relativeToActual(pos); + boolean isBlock = !world.getBlockState(actualPos).isAir(); + if (iceFillBoard[row][col] != isBlock) { + iceFillBoard[row][col] = isBlock; + boardChanged = true; + } + } + } + return boardChanged; + } + + private void solve(boolean[][] iceFillBoard, List iceFillPath) { + Vector2ic start = new Vector2i(iceFillBoard.length - 1, iceFillBoard[0].length / 2); + int count = iceFillBoard.length * iceFillBoard[0].length - Arrays.stream(iceFillBoard).mapToInt(Booleans::countTrue).sum(); + + Queue> queue = new ArrayDeque<>(); + queue.add(List.of(start)); + while (!queue.isEmpty()) { + List path = queue.poll(); + Vector2ic pos = path.get(path.size() - 1); + if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2 && path.size() == count) { + iceFillPath.clear(); + iceFillPath.addAll(path); + return; + } + + Vector2i posMutable = pos.add(1, 0, new Vector2i()); + if (posMutable.x() < iceFillBoard.length && !iceFillBoard[posMutable.x()][posMutable.y()]) { + addQueue(queue, path, posMutable); + } + + posMutable = pos.add(-1, 0, new Vector2i()); + if (posMutable.x() >= 0 && !iceFillBoard[posMutable.x()][posMutable.y()]) { + addQueue(queue, path, posMutable); + } + + posMutable = pos.add(0, 1, new Vector2i()); + if (posMutable.y() < iceFillBoard[0].length && !iceFillBoard[posMutable.x()][posMutable.y()]) { + addQueue(queue, path, posMutable); + } + + posMutable = pos.add(0, -1, new Vector2i()); + if (posMutable.y() >= 0 && !iceFillBoard[posMutable.x()][posMutable.y()]) { + addQueue(queue, path, posMutable); + } + } + } + + private void addQueue(Queue> queue, List path, Vector2ic newPos) { + if (!path.contains(newPos)) { + List newPath = new ArrayList<>(path); + newPath.add(newPos); + queue.add(newPath); + } } @Override public void render(WorldRenderContext context) { + if (!SkyblockerConfigManager.get().locations.dungeons.solveIceFill || !DungeonManager.isCurrentRoomMatched()) { + return; + } + Room room = DungeonManager.getCurrentRoom(); + renderPath(context, room, iceFillPath1, BOARD_1_ORIGIN); + renderPath(context, room, iceFillPath2, BOARD_2_ORIGIN); + renderPath(context, room, iceFillPath3, BOARD_3_ORIGIN); + } + private void renderPath(WorldRenderContext context, Room room, List iceFillPath, BlockPos originPos) { + BlockPos.Mutable pos = new BlockPos.Mutable(); + for (int i = 0; i < iceFillPath.size() - 1; i++) { + Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(originPos).move(-iceFillPath.get(i).y(), 0, -iceFillPath.get(i).x()))); + Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.set(originPos).move(-iceFillPath.get(i + 1).y(), 0, -iceFillPath.get(i + 1).x()))); + RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, end}, RED_COLOR_COMPONENTS, 1f, 5f, true); + } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java index 195e38f7..b5cbc8ee 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java @@ -26,35 +26,32 @@ import java.util.*; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; -//right: 7, 66, 8 -//left: 23, 66, 8 -//right back: 7, 66, 24 -//polished andesite public class Silverfish extends DungeonPuzzle { private static final Logger LOGGER = LoggerFactory.getLogger(Silverfish.class); public static final Silverfish INSTANCE = new Silverfish(); - private static final float[] LIME_COLOR_COMPONENTS = DyeColor.LIME.getColorComponents(); - private final boolean[][] silverfishBoard = new boolean[17][17]; - private Vector2ic silverfishPos; - private final List silverfishPath = new ArrayList<>(); + private static final float[] RED_COLOR_COMPONENTS = DyeColor.RED.getColorComponents(); + final boolean[][] silverfishBoard = new boolean[17][17]; + Vector2ic silverfishPos; + final List silverfishPath = new ArrayList<>(); private Silverfish() { super("silverfish", "ice-silverfish-room"); + } + + public static void init() { if (Debug.debugEnabled()) { - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(puzzleName) + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName) .then(literal("printBoard").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(silverfishBoard))); + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.silverfishBoard))); return Command.SINGLE_SUCCESS; })).then(literal("printPath").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(silverfishPath.toString())); + context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.silverfishPath.toString())); return Command.SINGLE_SUCCESS; })) ))))); } } - public static void init() {} - private static String boardToString(boolean[][] silverfishBoard) { StringBuilder sb = new StringBuilder(); for (boolean[] row : silverfishBoard) { @@ -85,13 +82,12 @@ public class Silverfish extends DungeonPuzzle { } } - BlockPos blockPos = room.relativeToActual(new BlockPos(16, 16, 16)); - List entities = client.world.getEntitiesByClass(SilverfishEntity.class, Box.of(Vec3d.ofCenter(blockPos), 32, 32, 32), silverfishEntity -> true); + List entities = client.world.getEntitiesByClass(SilverfishEntity.class, Box.of(Vec3d.ofCenter(room.relativeToActual(new BlockPos(15, 66, 16))), 16, 16, 16), silverfishEntity -> true); if (entities.isEmpty()) { return; } BlockPos newSilverfishBlockPos = room.actualToRelative(entities.get(0).getBlockPos()); - Vector2ic newSilverfishPos = new Vector2i(23 - newSilverfishBlockPos.getX(), 24 - newSilverfishBlockPos.getZ()); + Vector2ic newSilverfishPos = new Vector2i(24 - newSilverfishBlockPos.getZ(), 23 - newSilverfishBlockPos.getX()); if (newSilverfishPos.x() < 0 || newSilverfishPos.x() >= 17 || newSilverfishPos.y() < 0 || newSilverfishPos.y() >= 17) { return; } @@ -104,17 +100,18 @@ public class Silverfish extends DungeonPuzzle { } } - private void solve() { + void solve() { if (silverfishPos == null) { return; } Set visited = new HashSet<>(); Queue> queue = new ArrayDeque<>(); queue.add(List.of(silverfishPos)); + visited.add(silverfishPos); while (!queue.isEmpty()) { List path = queue.poll(); Vector2ic pos = path.get(path.size() - 1); - if (pos.equals(8, 0)) { + if (pos.x() == 0 && pos.y() >= 7 && pos.y() <= 9) { silverfishPath.clear(); silverfishPath.addAll(path); return; @@ -125,48 +122,37 @@ public class Silverfish extends DungeonPuzzle { posMutable.add(1, 0); } posMutable.add(-1, 0); - if (!visited.contains(posMutable)) { - ArrayList newPath = new ArrayList<>(path); - newPath.add(new Vector2i(posMutable)); - queue.add(newPath); - visited.add(posMutable); - } + addQueue(visited, queue, path, posMutable); - posMutable.set(pos); + posMutable = new Vector2i(pos); while (posMutable.x() >= 0 && !silverfishBoard[posMutable.x()][posMutable.y()]) { posMutable.add(-1, 0); } posMutable.add(1, 0); - if (!visited.contains(posMutable)) { - ArrayList newPath = new ArrayList<>(path); - newPath.add(new Vector2i(posMutable)); - queue.add(newPath); - visited.add(posMutable); - } + addQueue(visited, queue, path, posMutable); - posMutable.set(pos); + posMutable = new Vector2i(pos); while (posMutable.y() < 17 && !silverfishBoard[posMutable.x()][posMutable.y()]) { posMutable.add(0, 1); } posMutable.add(0, -1); - if (!visited.contains(posMutable)) { - ArrayList newPath = new ArrayList<>(path); - newPath.add(new Vector2i(posMutable)); - queue.add(newPath); - visited.add(posMutable); - } + addQueue(visited, queue, path, posMutable); - posMutable.set(pos); + posMutable = new Vector2i(pos); while (posMutable.y() >= 0 && !silverfishBoard[posMutable.x()][posMutable.y()]) { posMutable.add(0, -1); } posMutable.add(0, 1); - if (!visited.contains(posMutable)) { - ArrayList newPath = new ArrayList<>(path); - newPath.add(new Vector2i(posMutable)); - queue.add(newPath); - visited.add(posMutable); - } + addQueue(visited, queue, path, posMutable); + } + } + + private void addQueue(Set visited, Queue> queue, List path, Vector2ic newPos) { + if (!visited.contains(newPos)) { + List newPath = new ArrayList<>(path); + newPath.add(newPos); + queue.add(newPath); + visited.add(newPos); } } @@ -178,9 +164,9 @@ public class Silverfish extends DungeonPuzzle { Room room = DungeonManager.getCurrentRoom(); BlockPos.Mutable pos = new BlockPos.Mutable(); for (int i = 0; i < silverfishPath.size() - 1; i++) { - Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i).x(), 67, 24 - silverfishPath.get(i).y()))); - Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i + 1).x(), 67, 24 - silverfishPath.get(i + 1).y()))); - RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, end}, LIME_COLOR_COMPONENTS, 1f, 5f, true); + Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i).y(), 67, 24 - silverfishPath.get(i).x()))); + Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i + 1).y(), 67, 24 - silverfishPath.get(i + 1).x()))); + RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, end}, RED_COLOR_COMPONENTS, 1f, 5f, true); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java index 3244996a..ba4b9a5f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java @@ -88,17 +88,20 @@ public class Waterboard extends DungeonPuzzle { private Waterboard() { super("waterboard", "water-puzzle"); - UseBlockCallback.EVENT.register(this::onUseBlock); + } + + public static void init() { + UseBlockCallback.EVENT.register(INSTANCE::onUseBlock); if (Debug.debugEnabled()) { - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(puzzleName) + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName) .then(literal("printBoard").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(cells))); + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.cells))); return Command.SINGLE_SUCCESS; })).then(literal("printDoors").executes(context -> { context.getSource().sendFeedback(Constants.PREFIX.get().append(Integer.toBinaryString(INSTANCE.doors))); return Command.SINGLE_SUCCESS; })).then(literal("printSimulationResults").then(argument("combination", IntegerArgumentType.integer(0, 63)).executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(results[IntegerArgumentType.getInteger(context, "combination")].toString())); + context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.results[IntegerArgumentType.getInteger(context, "combination")].toString())); return Command.SINGLE_SUCCESS; }))).then(literal("printCurrentCombination").executes(context -> { context.getSource().sendFeedback(Constants.PREFIX.get().append(Integer.toBinaryString(INSTANCE.currentCombination))); @@ -111,9 +114,6 @@ public class Waterboard extends DungeonPuzzle { } } - public static void init() { - } - private static String boardToString(Cell[][] cells) { StringBuilder sb = new StringBuilder(); for (Cell[] row : cells) { diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/SilverfishTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/SilverfishTest.java new file mode 100644 index 00000000..cc6178e1 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/SilverfishTest.java @@ -0,0 +1,40 @@ +package de.hysky.skyblocker.skyblock.dungeon.puzzle; + +import org.joml.Vector2i; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class SilverfishTest { + private static final boolean[][] silverfishBoard = new boolean[][]{ + {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, + {false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false}, + {true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, + {false, true, false, false, false, false, false, false, false, false, true, false, false, false, false, true, false}, + {false, false, true, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false}, + {false, false, true, false, false, false, false, false, false, false, false, false, false, true, false, false, false} + }; + + @Test + void testSilverfishSolve() { + for (int i = 0; i < silverfishBoard.length; i++) { + System.arraycopy(silverfishBoard[i], 0, Silverfish.INSTANCE.silverfishBoard[i], 0, silverfishBoard[i].length); + } + Silverfish.INSTANCE.silverfishPos = new Vector2i(15, 15); + Silverfish.INSTANCE.solve(); + List expectedSilverfishPath = List.of(new Vector2i(15, 15), new Vector2i(15, 11), new Vector2i(16, 11), new Vector2i(16, 3), new Vector2i(0, 3), new Vector2i(0, 4), new Vector2i(1, 4), new Vector2i(1, 2), new Vector2i(10, 2), new Vector2i(10, 9), new Vector2i(0, 9)); + Assertions.assertEquals(expectedSilverfishPath, Silverfish.INSTANCE.silverfishPath); + } +} -- cgit From b3132a89b5036e27d7efbb5dbe47f96e520bf851 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sat, 3 Feb 2024 11:55:32 -0500 Subject: Add config --- .../skyblocker/config/categories/DungeonsCategory.java | 14 ++++++++++++++ src/main/resources/assets/skyblocker/lang/en_us.json | 6 ++++-- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 5eb9a066..3ebd5d76 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -416,6 +416,20 @@ public class DungeonsCategory { newValue -> config.locations.dungeons.solveBoulder = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveIceFill")) + .binding(defaults.locations.dungeons.solveIceFill, + () -> config.locations.dungeons.solveIceFill, + newValue -> config.locations.dungeons.solveIceFill = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveSilverfish")) + .binding(defaults.locations.dungeons.solveSilverfish, + () -> config.locations.dungeons.solveSilverfish, + newValue -> config.locations.dungeons.solveSilverfish = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer")) .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer.@Tooltip"))) diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 3c6c6349..7587ad51 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -271,6 +271,8 @@ "text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard.@Tooltip": "Click the levers with green boxes to solve the puzzle.", "text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder": "Solve Boulder Puzzle", "text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder.@Tooltip": "Draws a line to the chest and highlight button", + "text.autoconfig.skyblocker.option.locations.dungeons.solveIceFill": "Solve Waterboard Puzzle", + "text.autoconfig.skyblocker.option.locations.dungeons.solveSilverfish": "Solve Waterboard Puzzle", "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage": "Mimic Message", "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage": "Enable Mimic Message", "text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage.@Tooltip": "Sends a message in chat upon killing a mimic for other players' score calculation mods.", @@ -408,7 +410,7 @@ "skyblocker.dungeons.secrets.customWaypointNotFound": "§cNo custom waypoint found at X: %d, Y: %d, Z: %d for room %s.", "skyblocker.dungeons.dungeonScore.scoreText": "Score: %s", "skyblocker.dungeons.puzzle.boulder.noSolution": "No solution found!", - + "skyblocker.dungeons.secretsTracker.feedback": "%s§f found %s§f secrets. %s", "skyblocker.dungeons.secretsTracker.failFeedback": "§cUnable to calculate the amount of secrets everybody did this run!", @@ -526,7 +528,7 @@ "skyblocker.partyFinder.yourParty": "Your party", "skyblocker.partyFinder.deList": "Click to de-list", "skyblocker.partyFinder.join": "Click to join", - + "skyblocker.crimson.kuudra.noArrowPoison": "No Arrow Poison!", "skyblocker.crimson.kuudra.lowArrowPoison": "Low on Arrow Poison!", -- cgit From 2fa902328a4a4a76f5ae300db7810530183b47d2 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sat, 3 Feb 2024 13:43:41 -0500 Subject: Change IceFill from bfs to dfs --- .../skyblock/dungeon/puzzle/IceFill.java | 137 ++++++++++++++++----- .../skyblock/dungeon/puzzle/IceFillTest.java | 27 ++++ 2 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java index 7ab96d78..166047b2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java @@ -120,50 +120,131 @@ public class IceFill extends DungeonPuzzle { return boardChanged; } - private void solve(boolean[][] iceFillBoard, List iceFillPath) { + void solve(boolean[][] iceFillBoard, List iceFillPath) { Vector2ic start = new Vector2i(iceFillBoard.length - 1, iceFillBoard[0].length / 2); int count = iceFillBoard.length * iceFillBoard[0].length - Arrays.stream(iceFillBoard).mapToInt(Booleans::countTrue).sum(); - Queue> queue = new ArrayDeque<>(); - queue.add(List.of(start)); - while (!queue.isEmpty()) { - List path = queue.poll(); - Vector2ic pos = path.get(path.size() - 1); - if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2 && path.size() == count) { - iceFillPath.clear(); - iceFillPath.addAll(path); - return; - } + List newPath = solveDfs(iceFillBoard, count - 1, new ArrayList<>(List.of(start))); + if (newPath != null) { + iceFillPath.clear(); + iceFillPath.addAll(newPath); + } + } - Vector2i posMutable = pos.add(1, 0, new Vector2i()); - if (posMutable.x() < iceFillBoard.length && !iceFillBoard[posMutable.x()][posMutable.y()]) { - addQueue(queue, path, posMutable); + private List solveDfs(boolean[][] iceFillBoard, int count, List path) { + Vector2ic pos = path.get(path.size() - 1); + if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2 && count == 0) { + return path; + } + + Vector2ic newPos = pos.add(1, 0, new Vector2i()); + if (newPos.x() < iceFillBoard.length && !iceFillBoard[newPos.x()][newPos.y()] && !path.contains(newPos)) { + path.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path); + if (newPath != null) { + return newPath; + } else { + path.remove(path.size() - 1); } + } - posMutable = pos.add(-1, 0, new Vector2i()); - if (posMutable.x() >= 0 && !iceFillBoard[posMutable.x()][posMutable.y()]) { - addQueue(queue, path, posMutable); + newPos = pos.add(-1, 0, new Vector2i()); + if (newPos.x() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !path.contains(newPos)) { + path.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path); + if (newPath != null) { + return newPath; + } else { + path.remove(path.size() - 1); } + } - posMutable = pos.add(0, 1, new Vector2i()); - if (posMutable.y() < iceFillBoard[0].length && !iceFillBoard[posMutable.x()][posMutable.y()]) { - addQueue(queue, path, posMutable); + newPos = pos.add(0, 1, new Vector2i()); + if (newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !path.contains(newPos)) { + path.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path); + if (newPath != null) { + return newPath; + } else { + path.remove(path.size() - 1); } + } - posMutable = pos.add(0, -1, new Vector2i()); - if (posMutable.y() >= 0 && !iceFillBoard[posMutable.x()][posMutable.y()]) { - addQueue(queue, path, posMutable); + newPos = pos.add(0, -1, new Vector2i()); + if (newPos.y() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !path.contains(newPos)) { + path.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path); + if (newPath != null) { + return newPath; + } else { + path.remove(path.size() - 1); } } + + return null; + } + + /* + void solve(boolean[][] iceFillBoard, List iceFillPath) { + Vector2ic start = new Vector2i(iceFillBoard.length - 1, iceFillBoard[0].length / 2); + int count = iceFillBoard.length * iceFillBoard[0].length - Arrays.stream(iceFillBoard).mapToInt(Booleans::countTrue).sum(); + + Vector2ic[] newPath = solveDfs(iceFillBoard, count - 1, new Vector2ic[]{start}); + if (newPath != null) { + iceFillPath.clear(); + iceFillPath.addAll(Arrays.asList(newPath)); + } } - private void addQueue(Queue> queue, List path, Vector2ic newPos) { - if (!path.contains(newPos)) { - List newPath = new ArrayList<>(path); - newPath.add(newPos); - queue.add(newPath); + private Vector2ic[] solveDfs(boolean[][] iceFillBoard, int count, Vector2ic[] path) { + Vector2ic pos = path[path.length - 1]; + if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2 && count == 0) { + return path; + } + + Vector2ic newPos = pos.add(1, 0, new Vector2i()); + if (newPos.x() < iceFillBoard.length && !iceFillBoard[newPos.x()][newPos.y()] && !ArrayUtils.contains(path, newPos)) { + Vector2ic[] newPath = Arrays.copyOf(path, path.length + 1); + newPath[path.length] = newPos; + newPath = solveDfs(iceFillBoard, count - 1, newPath); + if (newPath != null) { + return newPath; + } + } + + newPos = pos.add(-1, 0, new Vector2i()); + if (newPos.x() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !ArrayUtils.contains(path, newPos)) { + Vector2ic[] newPath = Arrays.copyOf(path, path.length + 1); + newPath[path.length] = newPos; + newPath = solveDfs(iceFillBoard, count - 1, newPath); + if (newPath != null) { + return newPath; + } + } + + newPos = pos.add(0, 1, new Vector2i()); + if (newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !ArrayUtils.contains(path, newPos)) { + Vector2ic[] newPath = Arrays.copyOf(path, path.length + 1); + newPath[path.length] = newPos; + newPath = solveDfs(iceFillBoard, count - 1, newPath); + if (newPath != null) { + return newPath; + } } + + newPos = pos.add(0, -1, new Vector2i()); + if (newPos.y() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !ArrayUtils.contains(path, newPos)) { + Vector2ic[] newPath = Arrays.copyOf(path, path.length + 1); + newPath[path.length] = newPos; + newPath = solveDfs(iceFillBoard, count - 1, newPath); + if (newPath != null) { + return newPath; + } + } + + return null; } + */ @Override public void render(WorldRenderContext context) { diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java new file mode 100644 index 00000000..511d1148 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.skyblock.dungeon.puzzle; + +import org.joml.Vector2ic; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +public class IceFillTest { + private static final boolean[][] iceFillBoard = new boolean[][]{ + {false, false, true, false, false, false, false}, + {false, false, false, false, false, false, false}, + {false, false, false, true, true, false, false}, + {false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false}, + {true, false, false, false, false, false, false}, + }; + private static final List iceFillPath = new ArrayList<>(); + + @Test + void testIceFillSolve() { + IceFill.INSTANCE.solve(iceFillBoard, iceFillPath); + System.out.println(iceFillPath); + System.out.println(iceFillPath.size()); + } +} -- cgit From b463261d5d1e70d6f0f350bc2a2507e4483d31c3 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:15:47 -0500 Subject: Optimize ice fill --- .../skyblock/dungeon/puzzle/IceFill.java | 110 ++++++--------------- .../skyblock/dungeon/puzzle/IceFillTest.java | 6 +- 2 files changed, 32 insertions(+), 84 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java index 166047b2..9b6e7b1f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java @@ -124,127 +124,73 @@ public class IceFill extends DungeonPuzzle { Vector2ic start = new Vector2i(iceFillBoard.length - 1, iceFillBoard[0].length / 2); int count = iceFillBoard.length * iceFillBoard[0].length - Arrays.stream(iceFillBoard).mapToInt(Booleans::countTrue).sum(); - List newPath = solveDfs(iceFillBoard, count - 1, new ArrayList<>(List.of(start))); + List newPath = solveDfs(iceFillBoard, count - 1, new ArrayList<>(List.of(start)), new HashSet<>(List.of(start))); if (newPath != null) { iceFillPath.clear(); iceFillPath.addAll(newPath); } } - private List solveDfs(boolean[][] iceFillBoard, int count, List path) { + private List solveDfs(boolean[][] iceFillBoard, int count, List path, Set visited) { Vector2ic pos = path.get(path.size() - 1); - if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2 && count == 0) { - return path; + if (count == 0) { + if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2) { + return path; + } else { + return null; + } } Vector2ic newPos = pos.add(1, 0, new Vector2i()); - if (newPos.x() < iceFillBoard.length && !iceFillBoard[newPos.x()][newPos.y()] && !path.contains(newPos)) { + if (newPos.x() < iceFillBoard.length && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { path.add(newPos); - List newPath = solveDfs(iceFillBoard, count - 1, path); + visited.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path, visited); if (newPath != null) { return newPath; - } else { - path.remove(path.size() - 1); } + path.remove(path.size() - 1); + visited.remove(newPos); } newPos = pos.add(-1, 0, new Vector2i()); - if (newPos.x() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !path.contains(newPos)) { + if (newPos.x() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { path.add(newPos); - List newPath = solveDfs(iceFillBoard, count - 1, path); + visited.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path, visited); if (newPath != null) { return newPath; - } else { - path.remove(path.size() - 1); } + path.remove(path.size() - 1); + visited.remove(newPos); } newPos = pos.add(0, 1, new Vector2i()); - if (newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !path.contains(newPos)) { + if (newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { path.add(newPos); - List newPath = solveDfs(iceFillBoard, count - 1, path); + visited.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path, visited); if (newPath != null) { return newPath; - } else { - path.remove(path.size() - 1); } + path.remove(path.size() - 1); + visited.remove(newPos); } newPos = pos.add(0, -1, new Vector2i()); - if (newPos.y() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !path.contains(newPos)) { + if (newPos.y() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { path.add(newPos); - List newPath = solveDfs(iceFillBoard, count - 1, path); - if (newPath != null) { - return newPath; - } else { - path.remove(path.size() - 1); - } - } - - return null; - } - - /* - void solve(boolean[][] iceFillBoard, List iceFillPath) { - Vector2ic start = new Vector2i(iceFillBoard.length - 1, iceFillBoard[0].length / 2); - int count = iceFillBoard.length * iceFillBoard[0].length - Arrays.stream(iceFillBoard).mapToInt(Booleans::countTrue).sum(); - - Vector2ic[] newPath = solveDfs(iceFillBoard, count - 1, new Vector2ic[]{start}); - if (newPath != null) { - iceFillPath.clear(); - iceFillPath.addAll(Arrays.asList(newPath)); - } - } - - private Vector2ic[] solveDfs(boolean[][] iceFillBoard, int count, Vector2ic[] path) { - Vector2ic pos = path[path.length - 1]; - if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2 && count == 0) { - return path; - } - - Vector2ic newPos = pos.add(1, 0, new Vector2i()); - if (newPos.x() < iceFillBoard.length && !iceFillBoard[newPos.x()][newPos.y()] && !ArrayUtils.contains(path, newPos)) { - Vector2ic[] newPath = Arrays.copyOf(path, path.length + 1); - newPath[path.length] = newPos; - newPath = solveDfs(iceFillBoard, count - 1, newPath); - if (newPath != null) { - return newPath; - } - } - - newPos = pos.add(-1, 0, new Vector2i()); - if (newPos.x() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !ArrayUtils.contains(path, newPos)) { - Vector2ic[] newPath = Arrays.copyOf(path, path.length + 1); - newPath[path.length] = newPos; - newPath = solveDfs(iceFillBoard, count - 1, newPath); - if (newPath != null) { - return newPath; - } - } - - newPos = pos.add(0, 1, new Vector2i()); - if (newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !ArrayUtils.contains(path, newPos)) { - Vector2ic[] newPath = Arrays.copyOf(path, path.length + 1); - newPath[path.length] = newPos; - newPath = solveDfs(iceFillBoard, count - 1, newPath); - if (newPath != null) { - return newPath; - } - } - - newPos = pos.add(0, -1, new Vector2i()); - if (newPos.y() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !ArrayUtils.contains(path, newPos)) { - Vector2ic[] newPath = Arrays.copyOf(path, path.length + 1); - newPath[path.length] = newPos; - newPath = solveDfs(iceFillBoard, count - 1, newPath); + visited.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path, visited); if (newPath != null) { return newPath; } + path.remove(path.size() - 1); + visited.remove(newPos); } return null; } - */ @Override public void render(WorldRenderContext context) { diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java index 511d1148..a3cbb93f 100644 --- a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java @@ -1,6 +1,8 @@ package de.hysky.skyblocker.skyblock.dungeon.puzzle; +import org.joml.Vector2i; import org.joml.Vector2ic; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -21,7 +23,7 @@ public class IceFillTest { @Test void testIceFillSolve() { IceFill.INSTANCE.solve(iceFillBoard, iceFillPath); - System.out.println(iceFillPath); - System.out.println(iceFillPath.size()); + List expectedIceFillPath = List.of(new Vector2i(6, 3), new Vector2i(5, 3), new Vector2i(4, 3), new Vector2i(3, 3), new Vector2i(3, 2), new Vector2i(4, 2), new Vector2i(5, 2), new Vector2i(6, 2), new Vector2i(6, 1), new Vector2i(5, 1), new Vector2i(5, 0), new Vector2i(4, 0), new Vector2i(4, 1), new Vector2i(3, 1), new Vector2i(3, 0), new Vector2i(2, 0), new Vector2i(1, 0), new Vector2i(0, 0), new Vector2i(0, 1), new Vector2i(1, 1), new Vector2i(2, 1), new Vector2i(2, 2), new Vector2i(1, 2), new Vector2i(1, 3), new Vector2i(1, 4), new Vector2i(1, 5), new Vector2i(2, 5), new Vector2i(3, 5), new Vector2i(3, 4), new Vector2i(4, 4), new Vector2i(5, 4), new Vector2i(6, 4), new Vector2i(6, 5), new Vector2i(6, 6), new Vector2i(5, 6), new Vector2i(5, 5), new Vector2i(4, 5), new Vector2i(4, 6), new Vector2i(3, 6), new Vector2i(2, 6), new Vector2i(1, 6), new Vector2i(0, 6), new Vector2i(0, 5), new Vector2i(0, 4), new Vector2i(0, 3)); + Assertions.assertEquals(expectedIceFillPath, iceFillPath); } } -- cgit From 15268d52e7529c11dd4734005ae7b0815c68b7a4 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:38:12 -0500 Subject: Refactor IceFill --- .../skyblock/dungeon/puzzle/IceFill.java | 106 ++++++--------------- 1 file changed, 31 insertions(+), 75 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java index 9b6e7b1f..57386674 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java @@ -28,16 +28,14 @@ import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.lit public class IceFill extends DungeonPuzzle { public static final IceFill INSTANCE = new IceFill(); private static final float[] RED_COLOR_COMPONENTS = DyeColor.RED.getColorComponents(); - private static final BlockPos BOARD_1_ORIGIN = new BlockPos(16, 70, 9); - private static final BlockPos BOARD_2_ORIGIN = new BlockPos(17, 71, 16); - private static final BlockPos BOARD_3_ORIGIN = new BlockPos(18, 72, 25); + private static final BlockPos[] BOARD_ORIGINS = { + new BlockPos(16, 70, 9), + new BlockPos(17, 71, 16), + new BlockPos(18, 72, 25) + }; private CompletableFuture solve; - private final boolean[][] iceFillBoard1 = new boolean[3][3]; - private final boolean[][] iceFillBoard2 = new boolean[5][5]; - private final boolean[][] iceFillBoard3 = new boolean[7][7]; - private final List iceFillPath1 = new ArrayList<>(); - private final List iceFillPath2 = new ArrayList<>(); - private final List iceFillPath3 = new ArrayList<>(); + private final boolean[][][] iceFillBoards = {new boolean[3][3], new boolean[5][5], new boolean[7][7]}; + private final List> iceFillPaths = List.of(new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); private IceFill() { super("ice-fill", "ice-path"); @@ -47,22 +45,22 @@ public class IceFill extends DungeonPuzzle { if (Debug.debugEnabled()) { ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName) .then(literal("printBoard1").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoard1))); + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[0]))); return Command.SINGLE_SUCCESS; })).then(literal("printBoard2").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoard2))); + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[1]))); return Command.SINGLE_SUCCESS; })).then(literal("printBoard3").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoard3))); + context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[2]))); return Command.SINGLE_SUCCESS; })).then(literal("printPath1").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPath1.toString())); + context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(0).toString())); return Command.SINGLE_SUCCESS; })).then(literal("printPath2").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPath2.toString())); + context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(1).toString())); return Command.SINGLE_SUCCESS; })).then(literal("printPath3").executes(context -> { - context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPath3.toString())); + context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(2).toString())); return Command.SINGLE_SUCCESS; })) ))))); @@ -89,18 +87,10 @@ public class IceFill extends DungeonPuzzle { solve = CompletableFuture.runAsync(() -> { BlockPos.Mutable pos = new BlockPos.Mutable(); - boolean board1Changed = updateBoard(client.world, room, iceFillBoard1, pos.set(BOARD_1_ORIGIN)); - boolean board2Changed = updateBoard(client.world, room, iceFillBoard2, pos.set(BOARD_2_ORIGIN)); - boolean board3Changed = updateBoard(client.world, room, iceFillBoard3, pos.set(BOARD_3_ORIGIN)); - - if (board1Changed) { - solve(iceFillBoard1, iceFillPath1); - } - if (board2Changed) { - solve(iceFillBoard2, iceFillPath2); - } - if (board3Changed) { - solve(iceFillBoard3, iceFillPath3); + for (int i = 0; i < 3; i++) { + if (updateBoard(client.world, room, iceFillBoards[i], pos.set(BOARD_ORIGINS[i]))) { + solve(iceFillBoards[i], iceFillPaths.get(i)); + } } }); } @@ -141,52 +131,18 @@ public class IceFill extends DungeonPuzzle { } } - Vector2ic newPos = pos.add(1, 0, new Vector2i()); - if (newPos.x() < iceFillBoard.length && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { - path.add(newPos); - visited.add(newPos); - List newPath = solveDfs(iceFillBoard, count - 1, path, visited); - if (newPath != null) { - return newPath; - } - path.remove(path.size() - 1); - visited.remove(newPos); - } - - newPos = pos.add(-1, 0, new Vector2i()); - if (newPos.x() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { - path.add(newPos); - visited.add(newPos); - List newPath = solveDfs(iceFillBoard, count - 1, path, visited); - if (newPath != null) { - return newPath; - } - path.remove(path.size() - 1); - visited.remove(newPos); - } - - newPos = pos.add(0, 1, new Vector2i()); - if (newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { - path.add(newPos); - visited.add(newPos); - List newPath = solveDfs(iceFillBoard, count - 1, path, visited); - if (newPath != null) { - return newPath; - } - path.remove(path.size() - 1); - visited.remove(newPos); - } - - newPos = pos.add(0, -1, new Vector2i()); - if (newPos.y() >= 0 && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { - path.add(newPos); - visited.add(newPos); - List newPath = solveDfs(iceFillBoard, count - 1, path, visited); - if (newPath != null) { - return newPath; + Vector2ic[] newPosArray = {pos.add(1, 0, new Vector2i()), pos.add(-1, 0, new Vector2i()), pos.add(0, 1, new Vector2i()), pos.add(0, -1, new Vector2i())}; + for (Vector2ic newPos : newPosArray) { + if (newPos.x() >= 0 && newPos.x() < iceFillBoard.length && newPos.y() >= 0 && newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) { + path.add(newPos); + visited.add(newPos); + List newPath = solveDfs(iceFillBoard, count - 1, path, visited); + if (newPath != null) { + return newPath; + } + path.remove(path.size() - 1); + visited.remove(newPos); } - path.remove(path.size() - 1); - visited.remove(newPos); } return null; @@ -198,9 +154,9 @@ public class IceFill extends DungeonPuzzle { return; } Room room = DungeonManager.getCurrentRoom(); - renderPath(context, room, iceFillPath1, BOARD_1_ORIGIN); - renderPath(context, room, iceFillPath2, BOARD_2_ORIGIN); - renderPath(context, room, iceFillPath3, BOARD_3_ORIGIN); + for (int i = 0; i < 3; i++) { + renderPath(context, room, iceFillPaths.get(i), BOARD_ORIGINS[i]); + } } private void renderPath(WorldRenderContext context, Room room, List iceFillPath, BlockPos originPos) { -- cgit From 6c3dd6b662ec86b8b7a185de9778ee97d435f3d1 Mon Sep 17 00:00:00 2001 From: Kevin <92656833+kevinthegreat1@users.noreply.github.com> Date: Mon, 19 Feb 2024 14:42:08 -0500 Subject: Fix discoveries index (#557) --- .../skyblock/tabhud/widget/DungeonSecretWidget.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java index 6f40f5a8..309ba9ca 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java @@ -1,16 +1,19 @@ package de.hysky.skyblocker.skyblock.tabhud.widget; import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import java.util.regex.Pattern; + // this widget shows info about the secrets of the dungeon public class DungeonSecretWidget extends Widget { - private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); + private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE, Formatting.BOLD); + private static final Pattern DISCOVERIES = Pattern.compile("Discoveries: (\\d+)"); public DungeonSecretWidget() { super(TITLE, Formatting.DARK_PURPLE.getColorValue()); @@ -18,9 +21,12 @@ public class DungeonSecretWidget extends Widget { @Override public void updateContent() { - this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31); - this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32); - + if (PlayerListMgr.regexAt(31, DISCOVERIES) != null) { + this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 32); + this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 33); + } else { + this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31); + this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32); + } } - } -- cgit From 20117e764d3a4f444efd06c846449395caac20aa Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Mon, 19 Feb 2024 14:42:58 -0500 Subject: Support the local memory cache for the API (#558) --- src/main/java/de/hysky/skyblocker/utils/Http.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/utils/Http.java b/src/main/java/de/hysky/skyblocker/utils/Http.java index 17079d15..58deced2 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Http.java +++ b/src/main/java/de/hysky/skyblocker/utils/Http.java @@ -53,7 +53,7 @@ public class Http { String body = new String(decodedInputStream.readAllBytes()); HttpHeaders headers = response.headers(); - return new ApiResponse(body, response.statusCode(), getCacheStatus(headers), getAge(headers)); + return new ApiResponse(body, response.statusCode(), getCacheStatuses(headers), getAge(headers)); } public static HttpHeaders sendHeadRequest(String url) throws IOException, InterruptedException { @@ -115,12 +115,12 @@ public class Http { } /** - * Returns the cache status of the resource + * Returns the cache statuses of the resource. All possible cache status values conform to Cloudflare's. * - * @see Cloudflare Cache Docs + * @see Cloudflare Cache Docs */ - private static String getCacheStatus(HttpHeaders headers) { - return headers.firstValue("CF-Cache-Status").orElse("UNKNOWN"); + private static String[] getCacheStatuses(HttpHeaders headers) { + return new String[] { headers.firstValue("CF-Cache-Status").orElse("UNKNOWN"), headers.firstValue("Local-Cache-Status").orElse("UNKNOWN") }; } private static int getAge(HttpHeaders headers) { @@ -128,7 +128,7 @@ public class Http { } //TODO If ever needed, we could just replace cache status with the response headers and go from there - public record ApiResponse(String content, int statusCode, String cacheStatus, int age) implements AutoCloseable { + public record ApiResponse(String content, int statusCode, String[] cacheStatuses, int age) implements AutoCloseable { public boolean ok() { return statusCode == 200; @@ -139,7 +139,7 @@ public class Http { } public boolean cached() { - return cacheStatus.equals("HIT"); + return cacheStatuses[0].equals("HIT") || cacheStatuses[1].equals("HIT"); } @Override -- cgit