From ca2bb2ba49fb648d81cf7a594969115ee38c34dd Mon Sep 17 00:00:00 2001
From: esteban4567890 <wt3dkilgg@mozmail.com>
Date: Tue, 6 Feb 2024 00:41:18 +0100
Subject: Visitor helper

---
 .../hysky/skyblocker/config/SkyblockerConfig.java  |   3 +
 .../config/categories/GeneralCategory.java         |   7 +
 .../hysky/skyblocker/mixin/HandledScreenMixin.java |  21 ++-
 .../skyblocker/skyblock/garden/VisitorHelper.java  | 171 +++++++++++++++++++++
 .../resources/assets/skyblocker/lang/en_us.json    |   1 +
 5 files changed, 201 insertions(+), 2 deletions(-)
 create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java

(limited to 'src')

diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index 4acb8064..fd272c71 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -174,6 +174,9 @@ public class SkyblockerConfig {
 		@SerialEntry
 		public boolean dontStripSkinAlphaValues = true;
 
+		@SerialEntry
+		public boolean visitorHelper = true;
+
 		@SerialEntry
 		public TabHudConf tabHud = new TabHudConf();
 
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
index 808b3c2c..bb2fdee5 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
@@ -78,6 +78,13 @@ public class GeneralCategory {
 						.controller(ConfigUtils::createBooleanController)
 						.flag(OptionFlag.ASSET_RELOAD)
 						.build())
+				.option(Option.<Boolean>createBuilder()
+						.name(Text.translatable("text.autoconfig.skyblocker.option.general.visitorHelper"))
+						.binding(defaults.general.visitorHelper,
+								() -> config.general.visitorHelper,
+								newValue -> config.general.visitorHelper = newValue)
+						.controller(ConfigUtils::createBooleanController)
+						.build())
 
 				//Tab Hud
 				.group(OptionGroup.createBuilder()
diff --git a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java
index 71203381..5d635e27 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java
@@ -7,6 +7,7 @@ import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver;
 import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver;
 import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver;
 import de.hysky.skyblocker.skyblock.experiment.UltrasequencerSolver;
+import de.hysky.skyblocker.skyblock.garden.VisitorHelper;
 import de.hysky.skyblocker.skyblock.item.ItemProtection;
 import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds;
 import de.hysky.skyblocker.skyblock.item.WikiLookup;
@@ -37,7 +38,6 @@ import org.spongepowered.asm.mixin.Unique;
 import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
 import org.spongepowered.asm.mixin.injection.ModifyVariable;
-import org.spongepowered.asm.mixin.injection.Redirect;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 
@@ -71,6 +71,20 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
         }
     }
 
+    @Inject(at = @At("RETURN"), method = "render")
+    public void skyblocker$renderScreen(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
+        if (!Utils.isOnSkyblock()) return;
+
+        if (SkyblockerConfigManager.get().general.visitorHelper && Utils.getLocationRaw().equals("garden") && !this.getTitle().getString().contains("Logbook")) 
+            VisitorHelper.renderScreen(this.getTitle().getString(), context, textRenderer, handler, mouseX, mouseY);
+    }
+
+    @Inject(at = @At("HEAD"), method = "mouseClicked")
+    public void skyblocker$mouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
+        if (SkyblockerConfigManager.get().general.visitorHelper)
+            VisitorHelper.onMouseClicked(mouseX, mouseY, button, this.textRenderer);
+    }
+
     @SuppressWarnings("DataFlowIssue")
     // makes intellij be quiet about this.focusedSlot maybe being null. It's already null checked in mixined method.
     @Inject(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V"), cancellable = true)
@@ -167,8 +181,11 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
                     if (ItemProtection.isItemProtected(stack)) ci.cancel();
                 }
 
-                //Prevent selling to NPC shops
+
                 if (this.client != null && this.handler instanceof GenericContainerScreenHandler genericContainerScreenHandler && genericContainerScreenHandler.getRows() == 6) {
+                    VisitorHelper.onSlotClick(slot, slotId, this.getTitle().getString());
+
+                    //Prevent selling to NPC shops
                     ItemStack sellItem = this.handler.slots.get(49).getStack();
 
                     if (sellItem.getName().getString().equals("Sell Item") || skyblocker$doesLoreContain(sellItem, this.client, "buyback")) {
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java
new file mode 100644
index 00000000..2c7ede30
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java
@@ -0,0 +1,171 @@
+package de.hysky.skyblocker.skyblock.garden;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
+import de.hysky.skyblocker.utils.NEURepoManager;
+import io.github.moulberry.repo.data.NEUItem;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtList;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text.Serialization;
+import net.minecraft.util.Formatting;
+
+//TODO: check inventory items, sum all repeated items into one
+public class VisitorHelper {
+    private static final Map<String, Map<String, Integer>> itemMap = new HashMap<>();
+    private static final Map<String, NEUItem> itemCache = new HashMap<>();
+    private static final int TEXT_START_X = 3;
+    private static final int TEXT_START_Y = 13;
+    private static final int LINE_SPACING = 3;
+
+    public static void renderScreen(String title, DrawContext context, TextRenderer textRenderer,
+            ScreenHandler handler, int mouseX, int mouseY) {
+        if (handler.getCursorStack() == ItemStack.EMPTY)
+            processVisitorItem(title, handler);
+        drawScreenItems(context, textRenderer, mouseX, mouseY);
+    }
+
+    public static void onMouseClicked(double mouseX, double mouseY, int mouseButton, TextRenderer textRenderer) {
+        int yPosition = TEXT_START_Y;
+
+        for (var entry : itemMap.entrySet()) {
+            String text = entry.getKey();
+            int textWidth = textRenderer.getWidth(text);
+            int textHeight = textRenderer.fontHeight;
+
+            yPosition += LINE_SPACING + textHeight;
+
+            for (var nestedEntry : entry.getValue().entrySet()) {
+                String nestedText = nestedEntry.getKey();
+                textWidth = textRenderer.getWidth(nestedText);
+
+                if (isMouseOverText((int) mouseX, (int) mouseY, TEXT_START_X, yPosition, textWidth, textHeight)) {
+                    MinecraftClient client = MinecraftClient.getInstance();
+                    client.player.networkHandler.sendChatMessage("/bz " + nestedText);
+                    return;
+                }
+                yPosition += LINE_SPACING + textHeight;
+            }
+        }
+    }
+
+    public static void onSlotClick(Slot slot, int slotId, String title) {
+        if (slotId == 29 || slotId == 13) {
+            itemMap.remove(title);
+        }
+    }
+
+    private static void processVisitorItem(String title, ScreenHandler handler) {
+        var visitorItem = handler.getSlot(13).getStack();
+        if (visitorItem != null && visitorItem.hasNbt() && visitorItem.getNbt().toString().contains("Times Visited")) {
+            var acceptButton = handler.getSlot(29).getStack();
+            if (acceptButton != null && acceptButton.hasNbt()) {
+                var acceptButtonNbt = acceptButton.getNbt().getCompound("display");
+                if (acceptButtonNbt.getType("Lore") == 9) {
+                    processLore(title, acceptButtonNbt.getList("Lore", 8));
+                }
+            }
+        }
+    }
+
+    private static void processLore(String title, NbtList nbtList) {
+        boolean saveRequiredItems = false;
+        for (var j = 0; j < nbtList.size(); j++) {
+            var string = nbtList.getString(j);
+            if (string.contains("Items Required"))
+                saveRequiredItems = true;
+            else if (string.contains("Rewards"))
+                break;
+            else if (saveRequiredItems)
+                updateItemMap(title, string);
+        }
+    }
+
+    private static void updateItemMap(String title, String lore) {
+        var mutableText = Serialization.fromJson(lore);
+        var split = mutableText.getString().split(" x");
+        var itemName = split[0].trim();
+        if (!itemName.isEmpty()) {
+            var amount = split.length == 2 ? Integer.parseInt(split[1].trim()) : 1;
+            Map<String, Integer> nestedMap = itemMap.getOrDefault(title, new HashMap<>());
+            if (!nestedMap.containsKey(itemName)) {
+                nestedMap.put(itemName, amount);
+            }
+            if (!itemMap.containsKey(title)) {
+                itemMap.put(title, nestedMap);
+            }
+        }
+    }
+
+    private static void drawScreenItems(DrawContext context, TextRenderer textRenderer, int mouseX, int mouseY) {
+        int index = 0;
+        for (var entry : itemMap.entrySet()) {
+            var primaryKey = entry.getKey();
+            drawTextWithOptionalUnderline(context, textRenderer, primaryKey, TEXT_START_X,
+                    TEXT_START_Y + (index * (LINE_SPACING + textRenderer.fontHeight)), -1, mouseX, mouseY);
+            index++;
+
+            for (var nestedEntry : entry.getValue().entrySet()) {
+                index = drawItemEntryWithHover(context, textRenderer, nestedEntry, index, mouseX, mouseY);
+            }
+        }
+    }
+
+    private static int drawItemEntryWithHover(DrawContext context, TextRenderer textRenderer,
+            Map.Entry<String, Integer> nestedEntry, int index, int mouseX, int mouseY) {
+        var subItem = nestedEntry.getKey();
+        var amount = nestedEntry.getValue();
+        var item = getCachedItem(subItem);
+        if (item != null) {
+            var text = "  " + item.getDisplayName() + " x" + amount;
+            drawTextWithOptionalUnderline(context, textRenderer, text, TEXT_START_X,
+                    TEXT_START_Y + (index * (LINE_SPACING + textRenderer.fontHeight)), -1, mouseX, mouseY);
+            drawItemStack(context, textRenderer, item, amount, index);
+        }
+        return index + 1;
+    }
+
+    private static NEUItem getCachedItem(String displayName) {
+        var strippedName = Formatting.strip(displayName);
+        var cachedItem = itemCache.get(strippedName);
+        if (cachedItem == null) {
+            var item = NEURepoManager.NEU_REPO.getItems().getItems().values().stream()
+                    .filter(i -> Formatting.strip(i.getDisplayName()).equals(strippedName))
+                    .findFirst()
+                    .orElse(null);
+            if (item != null) {
+                itemCache.put(strippedName, item);
+            }
+            return item;
+        }
+        return cachedItem;
+    }
+
+    private static void drawTextWithOptionalUnderline(DrawContext context, TextRenderer textRenderer, String text,
+            int x, int y, int color, int mouseX, int mouseY) {
+        context.drawText(textRenderer, text, x, y, color, true);
+        if (isMouseOverText(mouseX, mouseY, x, y, textRenderer.getWidth(text), textRenderer.fontHeight)) {
+            context.drawHorizontalLine(x, x + textRenderer.getWidth(text), y + textRenderer.fontHeight, color);
+        }
+    }
+
+    private static boolean isMouseOverText(int mouseX, int mouseY, int x, int y, int width, int height) {
+        return mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height;
+    }
+
+    private static void drawItemStack(DrawContext context, TextRenderer textRenderer, NEUItem item, int amount,
+            int index) {
+        var stack = ItemRepository.getItemStack(item.getSkyblockItemId());
+        var text = "  " + item.getDisplayName() + " x" + amount;
+        context.drawItem(stack, TEXT_START_X + 2 + textRenderer.getWidth(text),
+                TEXT_START_Y + (index * (LINE_SPACING + textRenderer.fontHeight)) - textRenderer.fontHeight + 5);
+        context.drawText(textRenderer, text, TEXT_START_X,
+                TEXT_START_Y + (index * (LINE_SPACING + textRenderer.fontHeight)), -1, true);
+    }
+}
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index f17f109e..e28d34bb 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -31,6 +31,7 @@
   "text.autoconfig.skyblocker.option.general.experiments.enableSuperpairsSolver": "Enable Superpairs Solver",
   "text.autoconfig.skyblocker.option.general.experiments.enableUltrasequencerSolver": "Enable Ultrasequencer Solver",
   "text.autoconfig.skyblocker.option.general.acceptReparty": "Auto accept Reparty",
+  "text.autoconfig.skyblocker.option.general.visitorHelper": "Visitor helper",
   "text.autoconfig.skyblocker.option.general.etherwarpOverlay": "Etherwarp Overlay",
   "text.autoconfig.skyblocker.option.general.fishing": "Fishing Helper",
   "text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper": "Enable Fishing Helper",
-- 
cgit