diff --git a/src/main/java/de/hysky/skyblocker/skyblock/Tips.java b/src/main/java/de/hysky/skyblocker/skyblock/Tips.java index 9b95abb2e0..8c7e107ee1 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 ca29db167a..cc0b3be8e0 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 static void init() { } 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 0e1c9d0e74..9ebf72a6e8 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.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 static void init() { } 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 0000000000..1f33528d04 --- /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 36c5e500a8..8abc8b6a08 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 b06928dd66..d6d7ea56f4 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 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 @@ protected void closeRecipeView() { * @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 boolean mouseClicked(double mouseX, double mouseY, int button) { 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 boolean mouseClicked(double mouseX, double mouseY, int button) { 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 480c91df66..953c1da1a1 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 @@ public void initialize(MinecraftClient client, int parentLeft, int parentTop) { @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 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { return true; } - recipeBook.searchField.setFocused(false); + return recipeBook.filterOption.mouseClicked(mouseX, mouseY, button); } } @@ -52,14 +54,19 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { } @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 f8c6872e64..0390c98110 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 @@ 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 @@ protected void reset() { //This field's name is misleading, the rectangle is actually the area of the magnifying glass icon rather than the entire search field this.searchFieldRect = ScreenRect.of(NavigationAxis.HORIZONTAL, left + 8, this.searchField.getY(), this.searchField.getX() - left, this.searchField.getHeight()); + this.filterOption = new CyclingTextureWidget<>(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 boolean mouseClicked(double mouseX, double mouseY, int button) { return false; } + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + var client = MinecraftClient.getInstance(); + if (client.isWindowFocused()) { + var mouse = client.mouse; + var window = client.getWindow(); + var mouseX = (mouse.getX() * ((double) window.getScaledWidth() / (double) window.getWidth())); + var mouseY = (mouse.getY() * ((double) window.getScaledHeight() / (double) window.getHeight())); + if (this.currentTab.left().keyPressed(mouseX, mouseY, keyCode, scanCode, modifiers)) { + return true; + } + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + /** * Same as the super classes implementation just that it checks for our custom tabs. */ @@ -179,13 +202,21 @@ protected void refreshTabButtons(boolean filteringCraftable) { } } + protected void refilterSearchResults(FilterOption filterOption) { + assert this.searchField != null; + String query = this.searchField.getText().toLowerCase(Locale.ENGLISH); + // Doesn't trigger the pirate speak check since the query wasn't changed. + this.currentTab.left().updateSearchResults(query, filterOption, true); + } + @Override protected void refreshSearchResults() { + assert this.searchField != null; String query = this.searchField.getText().toLowerCase(Locale.ENGLISH); this.triggerPirateSpeakEasterEgg(query); //Note: The rest of the query checks are implemented by the results class - this.currentTab.left().updateSearchResults(query); + this.currentTab.left().updateSearchResults(query, this.filterOption.getCurrent()); } private RecipeBookWidgetAccessor accessor() { @@ -194,7 +225,9 @@ private RecipeBookWidgetAccessor accessor() { @Override protected void refreshResults(boolean resetCurrentPage, boolean filteringCraftable) { - this.currentTab.left().updateSearchResults(this.searchField.getText()); + assert this.searchField != null; + this.currentTab.left().updateSearchResults(this.searchField.getText().toLowerCase(Locale.ENGLISH), + this.filterOption.getCurrent()); } /** diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResultButton.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResultButton.java index 769b8ac647..abd93c6b2c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResultButton.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/SkyblockRecipeResultButton.java @@ -19,7 +19,7 @@ public class SkyblockRecipeResultButton extends ClickableWidget { private static final int SIZE = 25; private static final int ITEM_OFFSET = 4; - private ItemStack itemStack = null; + private ItemStack itemStack = ItemStack.EMPTY; protected SkyblockRecipeResultButton() { super(0, 0, SIZE, SIZE, ScreenTexts.EMPTY); @@ -37,7 +37,7 @@ protected void setDisplayStack(ItemStack stack) { protected void clearDisplayStack() { this.visible = false; - this.itemStack = null; + this.itemStack = ItemStack.EMPTY; } @Override diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/UpcomingEventsTab.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/UpcomingEventsTab.java index b19860bca5..c30df2943b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/UpcomingEventsTab.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/recipebook/UpcomingEventsTab.java @@ -95,7 +95,7 @@ public ItemStack icon() { } @Override - public void updateSearchResults(String query) {} + public void updateSearchResults(String query, FilterOption filterOption, boolean refresh) {} @Override public void initializeSearchResults(String query) {} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/CyclingTextureWidget.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/CyclingTextureWidget.java new file mode 100644 index 0000000000..71f23cbada --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/CyclingTextureWidget.java @@ -0,0 +1,77 @@ +package de.hysky.skyblocker.utils.render.gui; + +import de.hysky.skyblocker.utils.EnumUtils; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ButtonTextures; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.screen.narration.NarrationPart; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * A widget designed for cycling through a set of textures, represented by an enum. + * + * @param 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; + } +} diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 0fa2d2a5dd..1bcbb00a9e 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -280,6 +280,12 @@ "skyblocker.config.general.itemList": "Item List", "skyblocker.config.general.itemList.enableItemList": "Enable Item List", + "skyblocker.config.general.itemList.filter.all": "All", + "skyblocker.config.general.itemList.filter.entities": "Entities", + "skyblocker.config.general.itemList.filter.npcs": "NPCs", + "skyblocker.config.general.itemList.filter.mayors": "Mayors", + "skyblocker.config.general.itemList.filter.items": "Items", + "skyblocker.config.general.itemProtection": "Item Protection", "skyblocker.config.general.itemProtection.protectValuableConsumables": "Protect Valuable Consumables", "skyblocker.config.general.itemProtection.protectValuableConsumables.@Tooltip": "Prevents consuming items such as Bottles of Jyrre, Dark Cacao Truffles, and Discrite before they have evolved.", @@ -1135,6 +1141,7 @@ "skyblocker.tips.newYearCakesHelper": "Open your New Year Cake Bag and Skyblocker will remember them and highlight duplicate cakes red and missing cakes green.", "skyblocker.tips.accessoryHelper": "Open your accessory bag and Skyblocker will remember them. The accessory helper will tell you what accessories you have and what accessories are missing.", "skyblocker.tips.fancyAuctionHouseCheapHighlight": "Cheap BINs are highlighted in green in the Fancy Auction House.", + "skyblocker.tips.viewRecipe": "Right-Click on a recipe result in the recipe book to access the super-craft menu.", "skyblocker.partyFinder.tabs.partyFinder": "Party Finder", "skyblocker.partyFinder.tabs.searchSettings": "Search Filters", diff --git a/src/main/resources/assets/skyblocker/textures/gui/filter/all.png b/src/main/resources/assets/skyblocker/textures/gui/filter/all.png new file mode 100644 index 0000000000..a4d8a262c7 Binary files /dev/null and b/src/main/resources/assets/skyblocker/textures/gui/filter/all.png differ diff --git a/src/main/resources/assets/skyblocker/textures/gui/filter/entities.png b/src/main/resources/assets/skyblocker/textures/gui/filter/entities.png new file mode 100644 index 0000000000..33847764ca Binary files /dev/null and b/src/main/resources/assets/skyblocker/textures/gui/filter/entities.png differ diff --git a/src/main/resources/assets/skyblocker/textures/gui/filter/items.png b/src/main/resources/assets/skyblocker/textures/gui/filter/items.png new file mode 100644 index 0000000000..c5ceb51fb6 Binary files /dev/null and b/src/main/resources/assets/skyblocker/textures/gui/filter/items.png differ diff --git a/src/main/resources/assets/skyblocker/textures/gui/filter/mayors.png b/src/main/resources/assets/skyblocker/textures/gui/filter/mayors.png new file mode 100644 index 0000000000..cc250d966c Binary files /dev/null and b/src/main/resources/assets/skyblocker/textures/gui/filter/mayors.png differ diff --git a/src/main/resources/assets/skyblocker/textures/gui/filter/npcs.png b/src/main/resources/assets/skyblocker/textures/gui/filter/npcs.png new file mode 100644 index 0000000000..bca5e28aab Binary files /dev/null and b/src/main/resources/assets/skyblocker/textures/gui/filter/npcs.png differ