diff --git a/src/main/java/com/github/mkram17/bazaarutils/events/ChestLoadedEvent.java b/src/main/java/com/github/mkram17/bazaarutils/events/ChestLoadedEvent.java index b0b5dde1..b0e0bbee 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/events/ChestLoadedEvent.java +++ b/src/main/java/com/github/mkram17/bazaarutils/events/ChestLoadedEvent.java @@ -10,6 +10,7 @@ import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.LoreComponent; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.screen.GenericContainerScreenHandler; @@ -143,11 +144,24 @@ private static boolean isItemLoading(Inventory inventory) { if (item.isEmpty()) continue; Text customName = item.get(DataComponentTypes.CUSTOM_NAME); - if (customName != null) { - String displayName = Util.removeFormatting(customName.getString()); - if (displayName.contains("Loading")) { - PlayerActionUtil.notifyAll("Loading item...", NotificationType.GUI); - return true; + if (customName == null) continue; + + String name = Util.removeFormatting(customName.getString()); + + if (name.contains("Loading")) { + return true; + } + + // Only bottleneck on lore data of items known to have partialized lore + if (name.contains("Sell")) { + LoreComponent lore = item.get(DataComponentTypes.LORE); + + if (lore != null && !lore.lines().isEmpty()) { + for (Text line : lore.lines()) { + if (Util.removeFormatting(line.getString()).contains("Loading")) { + return true; + } + } } } } diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/inventory/InstantSellHighlight.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/inventory/InstantSellHighlight.java index c7367dd4..d4e89725 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/inventory/InstantSellHighlight.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/inventory/InstantSellHighlight.java @@ -1,113 +1,122 @@ package com.github.mkram17.bazaarutils.features.gui.inventory; +import com.github.mkram17.bazaarutils.BazaarUtils; import com.github.mkram17.bazaarutils.config.features.gui.InventoryConfig; import com.github.mkram17.bazaarutils.events.ChestLoadedEvent; import com.github.mkram17.bazaarutils.events.listener.BUListener; -import com.github.mkram17.bazaarutils.generated.BazaarUtilsModules; -import com.github.mkram17.bazaarutils.misc.SlotHighlightCache; +import com.github.mkram17.bazaarutils.utils.Util; +import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenHandler; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreens; import com.github.mkram17.bazaarutils.utils.annotations.modules.Module; +import com.github.mkram17.bazaarutils.utils.bazaar.components.InstantSellParser; import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderInfo; import com.github.mkram17.bazaarutils.utils.config.BUToggleableFeature; +import com.github.mkram17.bazaarutils.utils.minecraft.ItemInfo; +import com.github.mkram17.bazaarutils.utils.minecraft.SlotHighlight; +import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenContext; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; -import com.github.mkram17.bazaarutils.utils.Util; -import com.github.mkram17.bazaarutils.utils.InstaSellUtil; import meteordevelopment.orbit.EventHandler; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.item.ItemStack; +import net.minecraft.screen.slot.Slot; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; @Module -public class InstantSellHighlight extends BUListener implements BUToggleableFeature { - private static final List highlightedSlotIndexes = new ArrayList<>(); +public class InstantSellHighlight extends BUListener implements BUToggleableFeature, SlotHighlight { + public static final Identifier IDENTIFIER = Identifier.tryParse(BazaarUtils.MOD_ID, "highlights/standard_background"); @Override - public boolean isEnabled() { - return InventoryConfig.INSTANT_SELL_HIGHLIGHT_TOGGLE; + public Identifier getIdentifier() { + return IDENTIFIER; } - public InstantSellHighlight() {} + private static final Map colorCache = new ConcurrentHashMap<>(); - @EventHandler - private void onScreenLoad(ChestLoadedEvent e) { - highlightedSlotIndexes.clear(); + private void populateCache(Set names, HandledScreen screen, PlayerInventory playerInventory) { + colorCache.clear(); - if (!isEnabled() || !ScreenManager.getInstance().isCurrent(BazaarScreens.MAIN_PAGE)) { - return; - } + for (Slot slot : screen.getScreenHandler().slots) { + if (!slot.hasStack() || slot.inventory != playerInventory) continue; + + Text customName = slot.getStack().getCustomName(); + + if (customName == null) continue; - Optional optionalInventory = getInventory(); - if (optionalInventory.isEmpty()) { - Util.notifyError("Failed to get player inventory.", new Throwable()); - return; + String itemName = customName.getString(); + + if (names.stream().anyMatch(itemName::equalsIgnoreCase)) { + colorCache.put(slot.getIndex(), InventoryConfig.INSTANT_SELL_HIGHLIGHT_COLOR); + } } - PlayerInventory inventory = optionalInventory.get(); - - List instaSellOrders = InstaSellUtil.getInstaSellOrders(e.getItemStacks()); - List names = instaSellOrders.stream() - .map(OrderInfo::getName) - .distinct() - .toList(); - - List inventoryStacks = getInventoryStacks(names); - - highlightedSlotIndexes.addAll( - inventoryStacks.stream() - .filter(itemStack -> !itemStack.isEmpty()) - .map(ScreenManager::getInventorySlotFromItemStack) - .flatMap(Optional::stream) - .toList() - ); } - private Optional getInventory(){ - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player == null) { - Util.notifyError("Player is null, cannot get inventory stacks.", new Throwable()); - return Optional.empty(); - } + @Override + public Integer getHighlightColor(int slotIndex) { + return colorCache.get(slotIndex); + } - return Optional.of(player.getInventory()); + @Override + public boolean isEnabled() { + return InventoryConfig.INSTANT_SELL_HIGHLIGHT_TOGGLE; } + public InstantSellHighlight() { + super(); + } - private List getInventoryStacks(List names){ - List inventoryStacks = new ArrayList<>(); + @Override + protected void registerFabricEvents() { + ScreenEvents.AFTER_INIT.register(this::onScreenInitialized); + } - var inventoryOpt = getInventory(); - if (inventoryOpt.isEmpty()) return Collections.emptyList(); - var inventory = inventoryOpt.get(); + @EventHandler + private void onChestLoaded(ChestLoadedEvent event) { + colorCache.clear(); - var stacks = inventory.getMainStacks(); + if (!isEnabled()) return; - stacks.forEach(itemStack -> { - if(itemStack.isEmpty()) return; - String itemName = itemStack.getName().getString(); - if (names.stream().anyMatch(name -> itemName.toLowerCase().contains(name.toLowerCase()))) { - inventoryStacks.add(itemStack); - } - }); - return inventoryStacks; - } + ScreenManager.getInstance().current().ifPresent(context -> { + HandledScreen screen = ScreenManager.getCurrentlyHandledScreen(HandledScreen.class).orElse(null); + MinecraftClient client = MinecraftClient.getInstance(); - public static void updateHighlightCache() { - if (!BazaarUtilsModules.InstantSellHighlight.isEnabled()) { - return; - } + if (screen == null || client.player == null) return; - for (Integer index : highlightedSlotIndexes) { - getColorFromIndex(index).ifPresent(instaSellHighlightColor -> SlotHighlightCache.instaSellHighlightCache.computeIfAbsent(index, (k) -> instaSellHighlightColor)); - } + List orders = resolveOrders(context); + + if (orders.isEmpty()) return; + + Set names = orders.stream() + .map(OrderInfo::getName) + .collect(Collectors.toSet()); + + populateCache(names, screen, client.player.getInventory()); + }); } - public static OptionalInt getColorFromIndex(int slotIndex) { - if (highlightedSlotIndexes.stream().noneMatch(i -> i.equals(slotIndex))) { - return OptionalInt.empty(); - } + private void onScreenInitialized(MinecraftClient client, Screen screen, int width, int height) { + colorCache.clear(); + } - return OptionalInt.of(InventoryConfig.INSTANT_SELL_HIGHLIGHT_COLOR); + private static List resolveOrders(ScreenContext context) { + if (context.isAnyOf(BazaarScreens.ITEM_PAGE)) + return BazaarScreenHandler.getInstantSellItem(context) + .map(ItemInfo::itemStack) + .flatMap(InstantSellParser::parseItemPageOrder) + .map(InstantSellParser.InstantSellResult::items) + .orElse(List.of()); + + return BazaarScreenHandler.getInstantSellItem(context) + .map(ItemInfo::itemStack) + .map(InstantSellParser::parseOrders) + .map(InstantSellParser.InstantSellResult::items) + .orElse(List.of()); } -} +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/inventory/OrderStatusHighlight.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/inventory/OrderStatusHighlight.java index 94d2d7e0..2f787994 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/inventory/OrderStatusHighlight.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/inventory/OrderStatusHighlight.java @@ -1,17 +1,24 @@ package com.github.mkram17.bazaarutils.features.gui.inventory; +import com.github.mkram17.bazaarutils.BazaarUtils; import com.github.mkram17.bazaarutils.config.features.DeveloperConfig; import com.github.mkram17.bazaarutils.config.features.gui.InventoryConfig; +import com.github.mkram17.bazaarutils.events.ChestLoadedEvent; import com.github.mkram17.bazaarutils.events.listener.BUListener; import com.github.mkram17.bazaarutils.generated.BazaarUtilsModules; +import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreens; import com.github.mkram17.bazaarutils.utils.config.BUToggleableFeature; -import com.github.mkram17.bazaarutils.misc.SlotHighlightCache; import com.github.mkram17.bazaarutils.utils.annotations.modules.Module; import com.github.mkram17.bazaarutils.utils.bazaar.market.order.*; import com.github.mkram17.bazaarutils.utils.Util; import com.github.mkram17.bazaarutils.utils.bazaar.market.price.PricingPosition; +import com.github.mkram17.bazaarutils.utils.minecraft.SlotHighlight; +import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; +import meteordevelopment.orbit.EventHandler; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.item.ItemStack; import net.minecraft.item.tooltip.TooltipType; @@ -21,155 +28,99 @@ import net.minecraft.text.TextColor; import net.minecraft.util.Identifier; +import java.util.ArrayList; import java.util.List; -import java.util.Optional; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; -//drawing done in MixinHandledScreen @Module -public class OrderStatusHighlight extends BUListener implements BUToggleableFeature { - public static final Identifier IDENTIFIER = Identifier.tryParse("bazaarutils", "orderstatushighlight/background"); +public class OrderStatusHighlight extends BUListener implements BUToggleableFeature, SlotHighlight { + public static final Identifier IDENTIFIER = Identifier.tryParse(BazaarUtils.MOD_ID, "highlights/standard_background"); @Override - public boolean isEnabled() { - return InventoryConfig.ORDER_STATUS_HIGHLIGHT_TOGGLE; + public Identifier getIdentifier() { + return IDENTIFIER; } - public OrderStatusHighlight() {} + private static final Map colorCache = new ConcurrentHashMap<>(); + private static final Map> tooltipCache = new ConcurrentHashMap<>(); - public static Order getHighlightedOrder(int slotIndex) { - Optional order = OrderUtil.getUserOrderFromIndex(slotIndex); + private void populateCache(ItemStack stack, HandledScreen screen) { + int index = getSlotIndex(stack, screen); + if (index == -1) return; - return order.filter( - bazaarOrder -> bazaarOrder.getStatus() != null && bazaarOrder.getStatus() == OrderStatus.SET) - .orElse(null); + Order order = getOrderForHighlight(index); + if (order == null) return; + + PricingPosition pos = order.getPricingPosition(); + if (pos == null) return; + + colorCache.put(index, getArgbFromPricingPosition(pos)); + tooltipCache.put(index, buildTooltipLines(order, pos)); } @Override - protected void registerFabricEvents() { - super.subscribeToMeteorEventBus = false; - registerTooltipListener(); + public Integer getHighlightColor(int slotIndex) { + return colorCache.get(slotIndex); } - public static void updateHighlightCache(List itemStacks) { - if (!BazaarUtilsModules.OrderStatusHighlight.isEnabled()) { - return; - } - - for (ItemStack stack : itemStacks) { - MinecraftClient client = MinecraftClient.getInstance(); - - if (client.player == null || !(client.currentScreen instanceof HandledScreen handledScreen)) { - continue; - } - - int index = -1; - - for (Slot slot : handledScreen.getScreenHandler().slots) { - if (!slot.hasStack() || !slot.getStack().equals(stack)) { - continue; - } - - index = slot.getIndex(); - } - - if (index == -1) { - continue; - } + @Override + public boolean isEnabled() { + return InventoryConfig.ORDER_STATUS_HIGHLIGHT_TOGGLE; + } - SlotHighlightCache.orderStatusHighlightCache.computeIfAbsent(index, OrderStatusHighlight::getHighlightColorFromIndex); - } + public OrderStatusHighlight() { + super(); } - private static Integer getHighlightColorFromIndex(int index) { - Order order = getHighlightedOrder(index); + @Override + protected void registerFabricEvents() { + ScreenEvents.AFTER_INIT.register(this::onScreenInitialized); + ItemTooltipCallback.EVENT.register(this::onTooltip); + } - if (order == null) { - return null; + @EventHandler + private void onChestLoaded(ChestLoadedEvent event) { + if (!BazaarUtilsModules.OrderStatusHighlight.isEnabled() || !ScreenManager.getInstance().isCurrent(BazaarScreens.ORDERS_PAGE)) { + return; } - PricingPosition pricingPosition = order.getPricingPosition(); - - if (pricingPosition == null) { - return null; - } + HandledScreen screen = ScreenManager.getCurrentlyHandledScreen(HandledScreen.class).orElse(null); + if (screen == null) return; - return getArgbFromPricingPosition(pricingPosition); + event.getItemStacks().forEach(stack -> populateCache(stack, screen)); } - //maybe could be split into separate methods, but this is fine for now - private void registerTooltipListener() { - ItemTooltipCallback.EVENT.register((ItemStack stack, net.minecraft.item.Item.TooltipContext context, TooltipType type, List lines) -> { - if (!isEnabled()) { - return; - } - - MinecraftClient client = MinecraftClient.getInstance(); - - if (!(client.currentScreen instanceof HandledScreen handledScreen)) { - return; - } - - int index = -1; - - for (Slot slot : handledScreen.getScreenHandler().slots) { - if (!slot.hasStack() || !(slot.getStack().equals(stack))) { - continue; - } - - index = slot.getIndex(); - } + private void onScreenInitialized(MinecraftClient client, Screen screen, int width, int height) { + colorCache.clear(); + tooltipCache.clear(); + } - if (!SlotHighlightCache.orderStatusHighlightCache.containsKey(index)) { - return; - } + private void onTooltip(ItemStack stack, net.minecraft.item.Item.TooltipContext context, TooltipType type, List lines) { + if (!isEnabled() || !ScreenManager.getInstance().isCurrent(BazaarScreens.ORDERS_PAGE)) return; - Order order = getHighlightedOrder(index); + HandledScreen screen = ScreenManager.getCurrentlyHandledScreen(HandledScreen.class).orElse(null); + if (screen == null) return; - if (order == null) { - return; - } + int index = getSlotIndex(stack, screen); + if (index == -1) return; - PricingPosition pricingPosition = order.getPricingPosition(); + List cached = tooltipCache.get(index); + if (cached != null) lines.addAll(1, cached); + } - if (pricingPosition == null) { - return; - } + private static int getSlotIndex(ItemStack stack, HandledScreen screen) { + for (Slot slot : screen.getScreenHandler().slots) { + if (slot.hasStack() && slot.getStack().equals(stack)) return slot.getIndex(); + } - switch (pricingPosition) { - case COMPETITIVE: - lines.add(1, Text.literal("COMPETITIVE") - .setStyle(Style.EMPTY - .withColor(TextColor.fromRgb(InventoryConfig.ORDER_STATUS_HIGHLIGHT_COMPETITIVE_COLOR)) - .withBold(true))); - break; - - case MATCHED: - lines.add(1, Text.literal("MATCHED") - .setStyle(Style.EMPTY - .withColor(TextColor.fromRgb(InventoryConfig.ORDER_STATUS_HIGHLIGHT_MATCHED_COLOR)) - .withBold(true))); - break; - - case OUTBID: - lines.add(1, Text.literal("OUTBID") - .setStyle(Style.EMPTY - .withColor(TextColor.fromRgb(InventoryConfig.ORDER_STATUS_HIGHLIGHT_OUTBID_COLOR)) - .withBold(true))); - - lines.add(2, Text.literal("Market Price: " + - Util.getPrettyString(order.getMarketPrice(order.getTransactionType().getSide()))) - .setStyle(Style.EMPTY - .withColor(TextColor.fromRgb(InventoryConfig.ORDER_STATUS_HIGHLIGHT_OUTBID_COLOR)))); - break; - } - if (DeveloperConfig.DEVELOPER_MODE_TOGGLE) { - var sellPrice = order.getMarketPrice(TransactionType.Side.BUY); - var buyPrice = order.getMarketPrice(TransactionType.Side.SELL); + return -1; + } - lines.add(Text.literal("[BU] Buy: " + Util.getPrettyString(sellPrice) + " coins")); - lines.add(Text.literal("[BU] Sell: " + Util.getPrettyString(buyPrice) + " coins")); - } - }); + private static Order getOrderForHighlight(int slotIndex) { + return OrderUtil.getUserOrderFromIndex(slotIndex) + .filter(o -> o.getStatus() != null && o.getStatus() == OrderStatus.SET) + .orElse(null); } private static int getArgbFromPricingPosition(PricingPosition pricingPosition) { @@ -179,4 +130,28 @@ private static int getArgbFromPricingPosition(PricingPosition pricingPosition) { case OUTBID -> InventoryConfig.ORDER_STATUS_HIGHLIGHT_OUTBID_COLOR; }; } -} + + private static List buildTooltipLines(Order order, PricingPosition pos) { + List lines = new ArrayList<>(); + + switch (pos) { + case COMPETITIVE -> lines.add(styledText("COMPETITIVE", InventoryConfig.ORDER_STATUS_HIGHLIGHT_COMPETITIVE_COLOR, true)); + case MATCHED -> lines.add(styledText("MATCHED", InventoryConfig.ORDER_STATUS_HIGHLIGHT_MATCHED_COLOR, true)); + case OUTBID -> { + lines.add(styledText("OUTBID", InventoryConfig.ORDER_STATUS_HIGHLIGHT_OUTBID_COLOR, true)); + lines.add(styledText("Market Price: " + Util.getPrettyString(order.getMarketPrice(order.getTransactionType().getSide())), InventoryConfig.ORDER_STATUS_HIGHLIGHT_OUTBID_COLOR, false)); + } + } + + if (DeveloperConfig.DEVELOPER_MODE_TOGGLE) { + lines.add(Text.literal("[BU] Buy: " + Util.getPrettyString(order.getMarketPrice(TransactionType.Side.BUY)) + " coins")); + lines.add(Text.literal("[BU] Sell: " + Util.getPrettyString(order.getMarketPrice(TransactionType.Side.SELL)) + " coins")); + } + + return lines; + } + + private static Text styledText(String content, int rgb, boolean bold) { + return Text.literal(content).setStyle(Style.EMPTY.withColor(TextColor.fromRgb(rgb)).withBold(bold)); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/misc/SlotHighlightCache.java b/src/main/java/com/github/mkram17/bazaarutils/misc/SlotHighlightCache.java deleted file mode 100644 index 9f8c2d24..00000000 --- a/src/main/java/com/github/mkram17/bazaarutils/misc/SlotHighlightCache.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.github.mkram17.bazaarutils.misc; - -import com.github.mkram17.bazaarutils.BazaarUtils; -import com.github.mkram17.bazaarutils.events.ChestLoadedEvent; -import com.github.mkram17.bazaarutils.events.listener.BUListener; -import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreens; -import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; -import com.github.mkram17.bazaarutils.features.gui.inventory.InstantSellHighlight; -import com.github.mkram17.bazaarutils.features.gui.inventory.OrderStatusHighlight; -import meteordevelopment.orbit.EventHandler; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class SlotHighlightCache extends BUListener { - - // key: slotIndex, value: highlightColor - public static final Map orderStatusHighlightCache = new ConcurrentHashMap<>(); - public static final Map instaSellHighlightCache = new ConcurrentHashMap<>(); - - public SlotHighlightCache() { - super(); - } - - public void registerFabricEvents(){ - ScreenEvents.AFTER_INIT.register((client, screen, width, height) -> { - orderStatusHighlightCache.clear(); - instaSellHighlightCache.clear(); - }); - } - - @EventHandler - public static void updateCaches(ChestLoadedEvent event) { - if (!ScreenManager.getInstance().isCurrent(BazaarScreens.ORDERS_PAGE, BazaarScreens.MAIN_PAGE)) { - return; - } - - OrderStatusHighlight.updateHighlightCache(event.getItemStacks()); - InstantSellHighlight.updateHighlightCache(); - } -} diff --git a/src/main/java/com/github/mkram17/bazaarutils/mixin/MixinHandledScreen.java b/src/main/java/com/github/mkram17/bazaarutils/mixin/MixinHandledScreen.java index 4b0aa49e..0cdf7aa4 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/mixin/MixinHandledScreen.java +++ b/src/main/java/com/github/mkram17/bazaarutils/mixin/MixinHandledScreen.java @@ -2,11 +2,8 @@ package com.github.mkram17.bazaarutils.mixin; import com.github.mkram17.bazaarutils.BazaarUtils; -import com.github.mkram17.bazaarutils.config.util.ConfigUtil; import com.github.mkram17.bazaarutils.events.SlotClickEvent; -import com.github.mkram17.bazaarutils.features.gui.inventory.OrderStatusHighlight; import com.github.mkram17.bazaarutils.generated.BazaarUtilsModules; -import com.github.mkram17.bazaarutils.misc.SlotHighlightCache; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreens; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; import net.minecraft.client.MinecraftClient; @@ -14,11 +11,11 @@ import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.widget.ClickableWidget; import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.SlotActionType; import net.minecraft.text.Text; import net.minecraft.util.Atlases; +import net.minecraft.util.Identifier; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -34,9 +31,7 @@ protected MixinHandledScreen(Text title) { @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true) private void onHandleMouseClick(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { - if (slot == null) { - return; - } + if (slot == null) return; HandledScreen screen = (HandledScreen) (Object) this; SlotClickEvent event = new SlotClickEvent(screen, slot, slotId, button, actionType); @@ -64,7 +59,8 @@ private void onHandleMouseClick(Slot slot, int slotId, int button, SlotActionTyp @Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V")) private void drawOnItem_OrderStatusHighlight(DrawContext context, Slot slot, int x, int y, CallbackInfo ci) { - if (slot == null || !slot.hasStack() || !ScreenManager.getInstance().isCurrent(BazaarScreens.ORDERS_PAGE)) { + if (slot == null || !slot.hasStack() || !BazaarUtilsModules.OrderStatusHighlight.isEnabled() + || !ScreenManager.getInstance().isCurrent(BazaarScreens.ORDERS_PAGE)) { return; } @@ -72,34 +68,31 @@ private void drawOnItem_OrderStatusHighlight(DrawContext context, Slot slot, int return; } - if (BazaarUtilsModules.OrderStatusHighlight.isEnabled() && SlotHighlightCache.orderStatusHighlightCache.containsKey(slot.getIndex())) { - draw(context, x, y, SlotHighlightCache.orderStatusHighlightCache.get(slot.getIndex())); - } + Integer color = BazaarUtilsModules.OrderStatusHighlight.getHighlightColor(slot.getIndex()); + if (color != null) draw(context, slot.x, slot.y, BazaarUtilsModules.OrderStatusHighlight.getIdentifier(), color); } @Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V")) - private void drawOnItem_InstaSellHighlight(DrawContext context, Slot slot, int x, int y, CallbackInfo ci) { - if (slot == null || !slot.hasStack() || !ScreenManager.getInstance().isCurrent(BazaarScreens.MAIN_PAGE)) { + private void drawOnItem_InstantSellHighlight(DrawContext context, Slot slot, int x, int y, CallbackInfo ci) { + if (slot == null || !slot.hasStack() || !BazaarUtilsModules.InstantSellHighlight.isEnabled() + || !ScreenManager.getInstance().isCurrent(BazaarScreens.MAIN_PAGE, BazaarScreens.ITEMS_GROUP_PAGE, BazaarScreens.ITEM_PAGE)) { return; } - if (MinecraftClient.getInstance().player != null && !(slot.inventory == MinecraftClient.getInstance().player.getInventory())) { + if (MinecraftClient.getInstance().player != null && slot.inventory != MinecraftClient.getInstance().player.getInventory()) { return; } - if (BazaarUtilsModules.InstantSellHighlight.isEnabled() && SlotHighlightCache.instaSellHighlightCache.containsKey(slot.getIndex())) { - draw(context, x, y, SlotHighlightCache.instaSellHighlightCache.get(slot.getIndex())); - } + Integer color = BazaarUtilsModules.InstantSellHighlight.getHighlightColor(slot.getIndex()); + if (color != null) draw(context, slot.x, slot.y, BazaarUtilsModules.InstantSellHighlight.getIdentifier(), color); } @Unique - protected void draw(DrawContext context, int x, int y, int argb) { - final var sprite = MinecraftClient.getInstance().getAtlasManager().getAtlasTexture(Atlases.GUI) - .getSprite(OrderStatusHighlight.IDENTIFIER); + protected void draw(DrawContext context, int x, int y, Identifier identifier, int argb) { + final var sprite = MinecraftClient.getInstance().getAtlasManager() + .getAtlasTexture(Atlases.GUI) + .getSprite(identifier); - context.drawSpriteStretched(RenderPipelines.GUI_TEXTURED, - sprite, x, y, 16, 16, argb - ); + context.drawSpriteStretched(RenderPipelines.GUI_TEXTURED, sprite, x, y, 16, 16, argb); } - } \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/InstaSellUtil.java b/src/main/java/com/github/mkram17/bazaarutils/utils/InstaSellUtil.java deleted file mode 100644 index 938721e5..00000000 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/InstaSellUtil.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.github.mkram17.bazaarutils.utils; - -import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreens; -import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderInfo; -import com.github.mkram17.bazaarutils.utils.bazaar.market.order.TransactionType; -import com.github.mkram17.bazaarutils.utils.minecraft.components.TextSearch; -import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; -import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.LoreComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.text.Text; - -import java.util.*; - -//NOTE FROM INSTASELLRESTRICTION: if there are items with no buy orders in inv, you get "Some items can't be sold" and there are 2 extra components - -public class InstaSellUtil { - public static List getInstaSellOrders(List itemStacks) { - if (!ScreenManager.getInstance().isCurrent(BazaarScreens.MAIN_PAGE)) { - return Collections.emptyList(); - } - - Optional instaSellItemStack = getInstaSellItemStack(itemStacks); - - if (instaSellItemStack.isEmpty()) { - Util.notifyError("Could not find insta-sell item stack in Bazaar GUI. Please report this issue.", new Throwable()); - - return Collections.emptyList(); - } - - return getInstaSellOrderData(instaSellItemStack.get()); - } - - public static Optional getInstaSellItemStack(List itemStacks) { - return itemStacks.stream().filter(itemStack -> itemStack.getName().getString().contains("Sell Inventory Now")).findFirst(); - } - - private static List getInstaSellOrderData(ItemStack instaSellItemStack) { - List orderData = new ArrayList<>(); - - LoreComponent loreComponents = instaSellItemStack.get(DataComponentTypes.LORE); - - if (loreComponents == null) { - return Collections.emptyList(); - } - - List loreLines = loreComponents.lines(); - - return findInstaSellOrderData(loreLines); - } - - private static List findInstaSellOrderData(List loreLines) { - List orderData = new ArrayList<>(); - - List itemLoreLines = getItemLoreLines(loreLines); - - for (Text line : itemLoreLines) { - int volume = getVolume(line); - - double totalPrice = getTotalPrice(line); - double pricePerUnit = Math.round(totalPrice / volume * 10)/10.0; - - String name = getName(line); - - OrderInfo buyOrderItem = new OrderInfo(name, TransactionType.Side.BUY, null, volume, pricePerUnit, null); - - orderData.add(buyOrderItem); - } - - return orderData; - } - - private static List getItemLoreLines(List loreLines) { - int firstItemIndex = TextSearch.indexOf(loreLines, "coins"); - int totalCoinIndex = TextSearch.lastIndexOf(loreLines, "coins"); - - if (firstItemIndex == -1 || totalCoinIndex == -1) { - return Collections.emptyList(); - } - - int lastItemIndex = TextSearch.lastIndexOf(loreLines.subList(0, totalCoinIndex-1), "coins"); - - return loreLines.subList(firstItemIndex, lastItemIndex+1); - } - - private static int getVolume(Text text) { - String volumeString = text.getSiblings().get(1).getString(); - - return Util.parseNumber(volumeString); - } - private static String getName(Text text) { - String nameString = text.getSiblings().get(3).getString(); - - return nameString.trim(); - } - private static double getTotalPrice(Text text) { - String priceString = text.getSiblings().get(5).getString(); - - return Util.parseNumber(priceString); - } -} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/RestrictionHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/RestrictionHelper.java new file mode 100644 index 00000000..3d5d68f5 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/RestrictionHelper.java @@ -0,0 +1,110 @@ +package com.github.mkram17.bazaarutils.utils.bazaar; + +import com.github.mkram17.bazaarutils.events.ChestLoadedEvent; +import com.github.mkram17.bazaarutils.events.SlotClickEvent; +import com.github.mkram17.bazaarutils.events.listener.BUListener; +import com.github.mkram17.bazaarutils.features.gui.inventory.restrictions.controls.RestrictionControl; +import com.github.mkram17.bazaarutils.utils.PlayerActionUtil; +import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderInfo; +import com.github.mkram17.bazaarutils.utils.config.BUToggleableFeature; +import com.github.mkram17.bazaarutils.utils.minecraft.ItemInfo; +import lombok.Getter; +import meteordevelopment.orbit.EventHandler; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; + +public abstract class RestrictionHelper extends BUListener implements BUToggleableFeature { + + public interface RestrictionState { + @NotNull + ItemInfo restrictedItem(); + } + + @Getter + protected String name; + + protected abstract int getClicksOverride(); + protected abstract String getMessagePrefix(); + protected abstract List> getRestrictors(); + + @Getter + private transient int clicks = 0; + private transient boolean isRestricted = true; + private transient Optional state = Optional.empty(); + + protected abstract Optional makeState(ChestLoadedEvent event); + + protected void resetState() { + state = Optional.empty(); + } + + public RestrictionHelper(String name) { + super(); + this.name = name; + } + + @Override + protected void registerFabricEvents() { + ScreenEvents.AFTER_INIT.register((client, screen, width, height) -> { + clicks = 0; + isRestricted = true; + state = Optional.empty(); + }); + } + + @EventHandler + public void onChestLoaded(ChestLoadedEvent event) { + if (!(isEnabled() && inCorrectScreen())) { + resetState(); + return; + } + + state = makeState(event); + isRestricted = state.map(this::computeRestriction).orElse(true); + clicks = 0; + } + + @EventHandler + public void onSlotClicked(SlotClickEvent event) { + if (!(isEnabled() && inCorrectScreen())) return; + + boolean isRestrictedSlot = state.map(RestrictionState::restrictedItem) + .map(info -> info.slotIndex() == event.slotId) + .orElse(false); + + if (!isRestrictedSlot) return; + + if (isRestricted && clicks < getClicksOverride()) { + clicks++; + PlayerActionUtil.notifyAll(getMessage(state.get())); + event.cancel(); + } + } + + public abstract boolean inCorrectScreen(); + + protected abstract boolean computeRestriction(T state); + + protected boolean isItemRestricted(OrderInfo item) { + for (RestrictionControl control : getRestrictors()) { + if (!control.isEnabled()) continue; + if (control.shouldRestrict(item)) return true; + } + return false; + } + + protected String getMessage(T state) { + StringBuilder message = new StringBuilder(getMessagePrefix()); + + for (RestrictionControl control : getRestrictors()) { + if (!control.isEnabled()) continue; + message.append(" ").append(control.describeRule()); + } + + message.append(" (Safety Clicks Left: ").append(getClicksOverride() - getClicks()).append(")"); + return message.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/components/InstantSellParser.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/components/InstantSellParser.java new file mode 100644 index 00000000..64bad860 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/components/InstantSellParser.java @@ -0,0 +1,60 @@ +package com.github.mkram17.bazaarutils.utils.bazaar.components; + +import com.github.mkram17.bazaarutils.utils.Util; +import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderInfo; +import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderType; +import com.github.mkram17.bazaarutils.utils.minecraft.components.LoreParser; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; + +import java.util.List; +import java.util.Optional; + +public final class InstantSellParser { + public record InstantSellResult(List items) {} + + private InstantSellParser() {} + + public static InstantSellResult parseOrders(ItemStack instantSellStack) { + List items = LoreParser.lines(instantSellStack).stream() + .filter(line -> line.getSiblings().size() == 6) + .map(InstantSellParser::parseLine) + .flatMap(Optional::stream) + .toList(); + + return new InstantSellResult(items); + } + + public static Optional parseItemPageOrder(ItemStack sellInstantlyStack) { + List lines = LoreParser.lines(sellInstantlyStack); + if (lines.size() < 6) return Optional.empty(); + + try { + String name = lines.get(0).getSiblings().getFirst().getString().trim(); + int volume = Util.parseNumber(lines.get(4).getSiblings().get(1).getString()); + double totalPrice = Double.parseDouble(lines.get(5).getSiblings().get(1).getString().replace(" coins", "").replace(",", "")); + double pricePerUnit = Math.round(totalPrice / volume * 10) / 10.0; + + return Optional.of(new InstantSellResult(List.of(new OrderInfo(name, OrderType.BUY, null, volume, pricePerUnit, null)))); + } catch (Exception e) { + return Optional.empty(); + } + } + + private static Optional parseLine(Text line) { + List s = line.getSiblings(); + if (s.size() != 6) return Optional.empty(); + + String name = s.get(3).getString().trim(); + + try { + int volume = Util.parseNumber(s.get(1).getString()); + double totalPrice = Double.parseDouble(s.get(5).getString().replace(" coins", "").replace(",", "")); + double pricePerUnit = Math.round(totalPrice / volume * 10) / 10.0; + + return Optional.of(new OrderInfo(name, OrderType.BUY, null, volume, pricePerUnit, null)); + } catch (Exception e) { + return Optional.empty(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/components/SellSacksParser.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/components/SellSacksParser.java new file mode 100644 index 00000000..bfd3e94d --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/components/SellSacksParser.java @@ -0,0 +1,46 @@ +package com.github.mkram17.bazaarutils.utils.bazaar.components; + +import com.github.mkram17.bazaarutils.utils.Util; +import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderInfo; +import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderType; +import com.github.mkram17.bazaarutils.utils.minecraft.components.LoreParser; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class SellSacksParser { + public record SellSacksResult(List items, Optional otherItems) { + public record OtherItems(int volume, double totalValue) {} + } + + private SellSacksParser() {} + + public static SellSacksResult parseOrders(ItemStack sellSacksStack) { + List items = new ArrayList<>(); + Optional otherItems = Optional.empty(); + + for (Text line : LoreParser.lines(sellSacksStack)) { + List s = line.getSiblings(); + if (s.size() != 6) continue; + + String name = s.get(3).getString().trim(); + + try { + int volume = Util.parseNumber(s.get(1).getString()); + double totalPrice = Double.parseDouble(s.get(5).getString().replace(" coins", "").replace(",", "")); + double pricePerUnit = Math.round(totalPrice / volume * 10) / 10.0; + + if (name.equals("Other items")) { + otherItems = Optional.of(new SellSacksResult.OtherItems(volume, totalPrice)); + } else { + items.add(new OrderInfo(name, OrderType.BUY, null, volume, pricePerUnit, null)); + } + } catch (Exception ignored) {} + } + + return new SellSacksResult(List.copyOf(items), otherItems); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/gui/BazaarScreenHandler.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/gui/BazaarScreenHandler.java index ddce0ae3..30fc0e12 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/gui/BazaarScreenHandler.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/gui/BazaarScreenHandler.java @@ -4,6 +4,7 @@ import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderInfo; import com.github.mkram17.bazaarutils.utils.bazaar.market.order.TransactionType; import com.github.mkram17.bazaarutils.utils.minecraft.ItemInfo; +import com.github.mkram17.bazaarutils.utils.minecraft.ItemInfo; import com.github.mkram17.bazaarutils.utils.minecraft.SlotLookup; import com.github.mkram17.bazaarutils.utils.minecraft.components.LoreParser; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenContext; @@ -19,21 +20,44 @@ import java.util.regex.Pattern; public final class BazaarScreenHandler { - public static final Pattern AMOUNT_PATTERN = Pattern.compile("Amount: (?[0-9,.]+)x"); + public static final Pattern AMOUNT_PATTERN = Pattern.compile("Amount: (?[0-9,.]+)x"); public static final Pattern SELL_LIMIT_PATTERN = Pattern.compile("Inventory: (?[0-9,.]+) items"); public static final Pattern PURCHASE_LIMIT_PATTERN = Pattern.compile("Buy up to (?[0-9,.]+)x."); private BazaarScreenHandler() {} + public static Optional getInstantSellItem(@NotNull ScreenContext context) { + if (context.isAnyOf(BazaarScreens.MAIN_PAGE)) + return getItemFromSlot(context, BazaarSlots.OVERVIEW_PAGE.SELL_INVENTORY.slot); + + if (context.isAnyOf(BazaarScreens.ITEM_PAGE)) + return getItemFromSlot(context, BazaarSlots.ITEM_PAGE.SELL_INSTANTLY.slot); + + if (context.isAnyOf(BazaarScreens.ITEMS_GROUP_PAGE)) + return getItemFromSlot(context, BazaarSlots.ITEMS_GROUP_PAGE.SELL_INVENTORY.slot); + + return Optional.empty(); + } + + public static Optional getSellSacksItem(@NotNull ScreenContext context) { + if (context.isAnyOf(BazaarScreens.MAIN_PAGE)) + return getItemFromSlot(context, BazaarSlots.OVERVIEW_PAGE.SELL_SACKS.slot); + + if (context.isAnyOf(BazaarScreens.ITEM_PAGE)) + return getItemFromSlot(context, BazaarSlots.ITEM_PAGE.SELL_SACKS.slot); + + if (context.isAnyOf(BazaarScreens.ITEMS_GROUP_PAGE)) + return getItemFromSlot(context, BazaarSlots.ITEMS_GROUP_PAGE.SELL_SACKS.slot); + + return Optional.empty(); + } + public static Optional getDisplayItem(@NotNull ScreenContext context) { // #isAnyOf rather than #matches — likely to hit computation cache from the // preceding isCurrent call in the same stack. if (!context.isAnyOf(BazaarScreens.ITEM_PAGE)) return Optional.empty(); - return context.as(GenericContainerScreen.class) - .map(screen -> SlotLookup.getInventoryItem( - screen.getScreenHandler().getInventory(), - BazaarSlots.ITEM_PAGE.ITEM_DISPLAY.slot)); + return getItemFromSlot(context, BazaarSlots.ITEM_PAGE.ITEM_DISPLAY.slot); } public static Optional getDisplayItemName(@NotNull ScreenContext context) { @@ -81,6 +105,11 @@ private static String getItemNameFromStacks(List stacks, String nameF return "???"; } + private static Optional getItemFromSlot(@NotNull ScreenContext context, BazaarSlots.BazaarSlot slot) { + return context.as(GenericContainerScreen.class) + .map(screen -> SlotLookup.getInventoryItem(screen.getScreenHandler().getInventory(), slot)); + } + public static Optional findOptionAmount(ItemStack option) { return LoreParser.matchDouble(option, AMOUNT_PATTERN, "amount", "option amount on " + option.getCustomName()); } diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/gui/BazaarSlots.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/gui/BazaarSlots.java index 03a690b2..96bdae40 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/gui/BazaarSlots.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/gui/BazaarSlots.java @@ -22,6 +22,31 @@ public ContainerQuery query(Inventory container) { } } + @AllArgsConstructor + public enum OVERVIEW_PAGE { + SELL_INVENTORY(new BazaarSlot( + new SlotLookup.IndexReference.FixedIndex(47), + (query) -> query + .itemType(Items.CHEST) + .withCustomName("Sell Inventory Now") + ) + ), + + SELL_SACKS(new BazaarSlot( + new SlotLookup.IndexReference.FixedIndex(48), + (query) -> query + .itemType(Items.CAULDRON) + .withCustomName("Sell Sacks Now") + ) + ); + + public final BazaarSlot slot; + + public ContainerQuery query(Inventory container) { + return slot.query(container); + } + } + @AllArgsConstructor public enum ITEM_PAGE { BUY_INSTANTLY(new BazaarSlot( @@ -86,7 +111,24 @@ public ContainerQuery query(Inventory container) { @AllArgsConstructor public enum ITEMS_GROUP_PAGE { - SWITCH_VIEW_MODE (new BazaarSlot( + SELL_INVENTORY( + new BazaarSlot( + new SlotLookup.IndexReference.ContainerSizeNegativeOffset(6), + (query) -> query + .itemType(Items.CHEST) + .withCustomName("Sell Inventory Now") + ) + ), + + SELL_SACKS(new BazaarSlot( + new SlotLookup.IndexReference.ContainerSizeNegativeOffset(2), + (query) -> query + .itemType(Items.CAULDRON) + .withCustomName("Sell Sacks Now") + ) + ), + + SWITCH_VIEW_MODE(new BazaarSlot( new SlotLookup.IndexReference.ContainerSizeNegativeOffset(1), (query) -> query .itemType(Items.IRON_ORE, Items.GOLD_ORE) diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/SlotHighlight.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/SlotHighlight.java new file mode 100644 index 00000000..89f7c068 --- /dev/null +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/SlotHighlight.java @@ -0,0 +1,9 @@ +package com.github.mkram17.bazaarutils.utils.minecraft; + +import net.minecraft.util.Identifier; + +public interface SlotHighlight { + Identifier getIdentifier(); + + Integer getHighlightColor(int slotIndex); +} \ No newline at end of file diff --git a/src/main/resources/assets/bazaarutils/textures/gui/sprites/orderstatushighlight/background.png b/src/main/resources/assets/bazaarutils/textures/gui/sprites/highlights/standard_background.png similarity index 100% rename from src/main/resources/assets/bazaarutils/textures/gui/sprites/orderstatushighlight/background.png rename to src/main/resources/assets/bazaarutils/textures/gui/sprites/highlights/standard_background.png