From 8575fb83989ce783bf1faf4ca14908c4135560d0 Mon Sep 17 00:00:00 2001 From: Wanja <105125277+Manchick0@users.noreply.github.com> Date: Sun, 5 Jan 2025 03:06:40 +0100 Subject: ItemList Changes (#1119) * ItemList changes This update introduces a bunch of changes to the ItemList feature, to hopefully enhance user experience. Most importantly, you can now filter out specific elements in the list to narrow down the results. The lore-search now works properly, and you're now able to use the wiki lookup key in the recipe book. Besides all that, shift-clicking on the recipe result button will suggest the recipe to the player using the `viewrecipe` command, to allow quick super-craft access. * Add missing GUI filter icons * Update FilterOption.java Properly use parentheses in the `(animal)` and `(pest)` flagged entries. * Refactor recipe view command to use MessageScheduler. Replaces direct chat command handling with MessageScheduler. * Refactor filtering logic and remove Identifiable interface. Replaced the Identifiable interface with a Supplier-based design for enum, updated related classes to use Supplier methods and refactored filtering logic for consistency. Additionally, added a new tip for viewing recipes in the recipe book, and changed the Shift+Click requirement to a simple right click. * Clean up the code --- .../java/de/hysky/skyblocker/skyblock/Tips.java | 3 +- .../skyblock/item/ItemRarityBackgrounds.java | 12 ++-- .../hysky/skyblocker/skyblock/item/WikiLookup.java | 19 ++++-- .../skyblock/itemlist/recipebook/FilterOption.java | 39 +++++++++++ .../itemlist/recipebook/RecipeAreaDisplay.java | 10 ++- .../recipebook/SkyblockCraftingRecipeResults.java | 38 +++++++++-- .../itemlist/recipebook/SkyblockCraftingTab.java | 15 +++-- .../recipebook/SkyblockRecipeBookWidget.java | 41 ++++++++++-- .../recipebook/SkyblockRecipeResultButton.java | 4 +- .../itemlist/recipebook/UpcomingEventsTab.java | 2 +- .../utils/render/gui/CyclingTextureWidget.java | 77 ++++++++++++++++++++++ 11 files changed, 228 insertions(+), 32 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/FilterOption.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/CyclingTextureWidget.java (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/Tips.java b/src/main/java/de/hysky/skyblocker/skyblock/Tips.java index 9b95abb2..8c7e107e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/Tips.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/Tips.java @@ -64,7 +64,8 @@ public class Tips { getTipFactory("skyblocker.tips.gardenMouseLock", ClickEvent.Action.SUGGEST_COMMAND, "/skyblocker config"), getTipFactory("skyblocker.tips.newYearCakesHelper"), getTipFactory("skyblocker.tips.accessoryHelper"), - getTipFactory("skyblocker.tips.fancyAuctionHouseCheapHighlight") + getTipFactory("skyblocker.tips.fancyAuctionHouseCheapHighlight"), + getTipFactory("skyblocker.tips.viewRecipe") )); private static boolean sentTip = false; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemRarityBackgrounds.java b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemRarityBackgrounds.java index ca29db16..cc0b3be8 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemRarityBackgrounds.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemRarityBackgrounds.java @@ -57,16 +57,12 @@ public class ItemRarityBackgrounds { } public static void tryDraw(ItemStack stack, DrawContext context, int x, int y) { - MinecraftClient client = MinecraftClient.getInstance(); - - if (client.player != null) { - SkyblockItemRarity itemRarity = getItemRarity(stack, client.player); - - if (itemRarity != null) draw(context, x, y, itemRarity); - } + SkyblockItemRarity itemRarity = getItemRarity(stack); + if (itemRarity != null) + draw(context, x, y, itemRarity); } - private static SkyblockItemRarity getItemRarity(ItemStack stack, ClientPlayerEntity player) { + private static SkyblockItemRarity getItemRarity(ItemStack stack) { if (stack == null || stack.isEmpty()) return null; String itemUuid = stack.getUuid(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java b/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java index 0e1c9d0e..9ebf72a6 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java @@ -8,6 +8,7 @@ import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.option.KeyBinding; import net.minecraft.client.util.InputUtil; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; import net.minecraft.screen.slot.Slot; import net.minecraft.text.Text; import net.minecraft.util.Util; @@ -33,12 +34,16 @@ public class WikiLookup { } public static void openWiki(@NotNull Slot slot, @NotNull PlayerEntity player) { - ItemUtils.getItemIdOptional(slot.getStack()) - .map(ItemRepository::getWikiLink) - .ifPresentOrElse(wikiLink -> CompletableFuture.runAsync(() -> Util.getOperatingSystem().open(wikiLink)).exceptionally(e -> { - LOGGER.error("[Skyblocker] Error while retrieving wiki article...", e); - player.sendMessage(Constants.PREFIX.get().append("Error while retrieving wiki article, see logs..."), false); - return null; - }), () -> player.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.wikiLookup.noArticleFound")), false)); + WikiLookup.openWiki(slot.getStack(), player); } + + public static void openWiki(ItemStack stack, PlayerEntity player) { + ItemUtils.getItemIdOptional(stack) + .map(ItemRepository::getWikiLink) + .ifPresentOrElse(wikiLink -> CompletableFuture.runAsync(() -> Util.getOperatingSystem().open(wikiLink)).exceptionally(e -> { + LOGGER.error("[Skyblocker] Error while retrieving wiki article...", e); + player.sendMessage(Constants.PREFIX.get().append("Error while retrieving wiki article, see logs..."), false); + return null; + }), () -> player.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.wikiLookup.noArticleFound")), false)); + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/FilterOption.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/FilterOption.java new file mode 100644 index 00000000..1f33528d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/FilterOption.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.skyblock.itemlist.recipebook; + +import de.hysky.skyblocker.SkyblockerMod; +import net.minecraft.util.Identifier; + +import java.util.function.Predicate; +import java.util.function.Supplier; + +public enum FilterOption implements Supplier, Predicate { + + ALL(query -> true, Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/filter/all.png")), + ENTITIES(query -> query.endsWith("(monster)") || query.endsWith("(miniboss)") || query.endsWith("(boss)") + || query.endsWith("(animal)") || query.endsWith("(pest)") || query.endsWith("(sea creature)"), + Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/filter/entities.png")), + NPCS(query -> query.endsWith("(npc)") || query.endsWith("(rift npc)"), Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/filter/npcs.png")), + MAYORS(query -> query.endsWith("(mayor)") || query.endsWith("(retired mayor)"), Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/filter/mayors.png")), + + // Basically a negation on everything else. + ITEMS(query -> !ENTITIES.test(query) && !NPCS.test(query) && !MAYORS.test(query), + Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/filter/items.png")); + + private final Predicate matchingPredicate; + private final Identifier texture; + + FilterOption(Predicate matchingPredicate, Identifier texture) { + this.matchingPredicate = matchingPredicate; + this.texture = texture; + } + + @Override + public boolean test(String query) { + return matchingPredicate.test(query); + } + + @Override + public Identifier get() { + return texture; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/RecipeAreaDisplay.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/RecipeAreaDisplay.java index 36c5e500..8abc8b6a 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/RecipeAreaDisplay.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/RecipeAreaDisplay.java @@ -15,8 +15,16 @@ public interface RecipeAreaDisplay { boolean mouseClicked(double mouseX, double mouseY, int button); + default boolean keyPressed(double mouseX, double mouseY, int keyCode, int scanCode, int modifiers) { + return false; + } + + default void updateSearchResults(String query, FilterOption filterOption) { + updateSearchResults(query, filterOption, false); + } + /** * If this tab does not use the search bar then no-op this. */ - void updateSearchResults(String query); + void updateSearchResults(String query, FilterOption filterOption, boolean refresh); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingRecipeResults.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingRecipeResults.java index b06928dd..d6d7ea56 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingRecipeResults.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingRecipeResults.java @@ -6,13 +6,17 @@ import java.util.Locale; import com.google.common.collect.Lists; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.WikiLookup; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; import de.hysky.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.recipebook.RecipeBookResults; import net.minecraft.client.gui.widget.ToggleButtonWidget; import net.minecraft.component.DataComponentTypes; @@ -181,18 +185,21 @@ public class SkyblockCraftingRecipeResults implements RecipeAreaDisplay { * @implNote The {@code query} is always passed as lower case. */ @Override - public void updateSearchResults(String query) { - if (!query.equals(this.lastSearchQuery)) { + public void updateSearchResults(String query, FilterOption filterOption, boolean refresh) { + if (!ItemRepository.filesImported()) return; + if (!query.equals(this.lastSearchQuery) || refresh) { this.lastSearchQuery = query; this.searchResults.clear(); //Search for stacks which contain the search term for (ItemStack stack : ItemRepository.getItems()) { String name = stack.getName().getString().toLowerCase(Locale.ENGLISH); + if (!filterOption.test(name)) continue; List lore = ItemUtils.getLore(stack); - //TODO turn lore lowercase - if (name.contains(query) || lore.stream().map(Text::getString).anyMatch(line -> line.contains(query))) { + if (name.contains(query) || lore.stream().map(Text::getString) + .map(string -> string.toLowerCase(Locale.ENGLISH)) + .anyMatch(line -> line.contains(query))) { this.searchResults.add(stack); } } @@ -271,6 +278,16 @@ public class SkyblockCraftingRecipeResults implements RecipeAreaDisplay { return true; } + if (this.recipeView && button == 1) { + // The crafting result button + var result = resultButtons.get(14); + var rawID = ItemUtils.getItemId(result.getDisplayStack()); + if (result.isMouseOver(mouseX, mouseY)) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown(String.format("/viewrecipe %s", rawID), true); + return true; + } + } + for (SkyblockRecipeResultButton resultButton : this.resultButtons) { //If the result button was clicked then try and show a recipe if there is one //for the item @@ -297,4 +314,17 @@ public class SkyblockCraftingRecipeResults implements RecipeAreaDisplay { return false; } + + @Override + public boolean keyPressed(double mouseX, double mouseY, int keyCode, int scanCode, int modifiers) { + if (SkyblockerConfigManager.get().general.wikiLookup.enableWikiLookup + && WikiLookup.wikiLookup.matchesKey(keyCode, scanCode)) + return this.resultButtons.stream() + .filter(button -> button.isMouseOver(mouseX, mouseY)) + .findFirst().map(button -> { + WikiLookup.openWiki(button.getDisplayStack(), client.player); + return true; + }).orElse(false); + return false; + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingTab.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingTab.java index 480c91df..953c1da1 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingTab.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockCraftingTab.java @@ -20,7 +20,9 @@ record SkyblockCraftingTab(SkyblockRecipeBookWidget recipeBook, ItemStack icon, @Override public void draw(DrawContext context, int x, int y, int mouseX, int mouseY, float delta) { + assert recipeBook.searchField != null; recipeBook.searchField.render(context, mouseX, mouseY, delta); + recipeBook.filterOption.render(context, mouseX, mouseY, delta); results.draw(context, x, y, mouseX, mouseY, delta); } @@ -43,8 +45,8 @@ record SkyblockCraftingTab(SkyblockRecipeBookWidget recipeBook, ItemStack icon, return true; } - recipeBook.searchField.setFocused(false); + return recipeBook.filterOption.mouseClicked(mouseX, mouseY, button); } } @@ -52,14 +54,19 @@ record SkyblockCraftingTab(SkyblockRecipeBookWidget recipeBook, ItemStack icon, } @Override - public void updateSearchResults(String query) { - results.updateSearchResults(query); + public boolean keyPressed(double mouseX, double mouseY, int keyCode, int scanCode, int modifiers) { + return this.results.keyPressed(mouseX, mouseY, keyCode, scanCode, modifiers); + } + + @Override + public void updateSearchResults(String query, FilterOption filterOption, boolean refresh) { + results.updateSearchResults(query, filterOption, refresh); } @Override public void initializeSearchResults(String query) { if (ItemRepository.filesImported()) { - updateSearchResults(query); + updateSearchResults(query, FilterOption.ALL); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeBookWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeBookWidget.java index f8c6872e..0390c981 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeBookWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeBookWidget.java @@ -3,6 +3,7 @@ package de.hysky.skyblocker.skyblock.itemlist.recipebook; import java.util.List; import java.util.Locale; +import de.hysky.skyblocker.utils.render.gui.CyclingTextureWidget; import net.minecraft.screen.ScreenHandler; import org.jetbrains.annotations.Nullable; @@ -34,8 +35,9 @@ public class SkyblockRecipeBookWidget extends RecipeBookWidget> tabButtons = Lists.newArrayList(); private Pair currentTab; + protected CyclingTextureWidget filterOption; + public SkyblockRecipeBookWidget(ScreenHandler screenHandler) { super(new NoopRecipeScreenHandler(screenHandler.syncId), List.of()); } @@ -72,6 +76,10 @@ public class SkyblockRecipeBookWidget extends RecipeBookWidget(this.searchField.getRight() + 4, this.searchField.getY(), 14, 14, FilterOption.ALL); + this.filterOption.setCycleListener(this::refilterSearchResults); + this.filterOption.setTextSupplier(option -> Text.translatable("skyblocker.config.general.itemList.filter." + option.name().toLowerCase(Locale.ENGLISH))); + //Setup Tabs this.tabButtons.clear(); @@ -150,6 +158,21 @@ public class SkyblockRecipeBookWidget extends RecipeBookWidget The type of the enum entries, which must be an {@link Enum} and implement {@link Supplier}. + */ +public class CyclingTextureWidget & Supplier> extends ClickableWidget { + + private Function textSupplier = t -> Text.of(t.name()); + private Function tooltipSupplier = t -> Tooltip.of(Text.of(textSupplier.apply(t))); + private Consumer onCycle = t -> {}; + private T current; + + private static final ButtonTextures BUTTON = new ButtonTextures(Identifier.ofVanilla("widget/button"), + Identifier.ofVanilla("widget/button_disabled"), Identifier.ofVanilla("widget/button_highlighted")); + + public CyclingTextureWidget(int x, int y, int width, int height, T initial) { + super(x, y, width, height, Text.empty()); + this.current = initial; + this.setTooltip(tooltipSupplier.apply(initial)); + } + + public void setCycleListener(Consumer onCycle) { + this.onCycle = onCycle; + } + + public void setTextSupplier(Function textSupplier) { + this.textSupplier = textSupplier; + setTooltip(tooltipSupplier.apply(getCurrent())); + } + + public void setTooltipSupplier(Function tooltipSupplier) { + this.tooltipSupplier = tooltipSupplier; + setTooltip(tooltipSupplier.apply(getCurrent())); + } + + @Override + public void onClick(double mouseX, double mouseY) { + this.current = EnumUtils.cycle(current); + this.setTooltip(tooltipSupplier.apply(getCurrent())); + this.onCycle.accept(getCurrent()); + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + var button = BUTTON.get(this.active, this.isFocused()); + context.drawGuiTexture(RenderLayer::getGuiTextured, button, this.getX(), + this.getY(), width, height); + context.drawTexture(RenderLayer::getGuiTextured, getCurrent().get(), + this.getX(), this.getY(), 0, 0, width, height, width, height); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + builder.put(NarrationPart.TITLE, this.textSupplier.apply(getCurrent())); + } + + public T getCurrent() { + return current; + } +} -- cgit