From 1254320033d9345c8cf27e2dbeeb0b098735d029 Mon Sep 17 00:00:00 2001
From: Chris Xiong <chirs241097@gmail.com>
Date: Sat, 11 May 2024 00:35:40 -0400
Subject: (AutoTrade) Allow setting a maximum acceptable price.

---
 .../trashyaddon/modules/AcceptablePrices.java      | 93 ++++++++++++++++++++++
 .../modules/AcceptablePricesScreen.java            | 78 ++++++++++++++++++
 .../chrisoft/trashyaddon/modules/AutoTrade.java    | 30 +++++--
 3 files changed, 193 insertions(+), 8 deletions(-)
 create mode 100644 src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePrices.java
 create mode 100644 src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePricesScreen.java

(limited to 'src/main/java')

diff --git a/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePrices.java b/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePrices.java
new file mode 100644
index 0000000..9e96dfe
--- /dev/null
+++ b/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePrices.java
@@ -0,0 +1,93 @@
+package org.chrisoft.trashyaddon.modules;
+
+import meteordevelopment.meteorclient.MeteorClient;
+import meteordevelopment.meteorclient.gui.GuiTheme;
+import meteordevelopment.meteorclient.gui.WidgetScreen;
+import meteordevelopment.meteorclient.utils.misc.ICopyable;
+import meteordevelopment.meteorclient.utils.misc.ISerializable;
+import meteordevelopment.meteorclient.gui.utils.IScreenFactory;
+import net.minecraft.item.Item;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.nbt.NbtList;
+import net.minecraft.registry.Registries;
+import net.minecraft.util.Identifier;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Stream;
+
+public class AcceptablePrices implements ICopyable<AcceptablePrices>, ISerializable<AcceptablePrices>, IScreenFactory {
+    public static final List<Item> allItems = Stream.concat(AutoTrade.allSellItems.stream(), AutoTrade.allBuyItems.stream()).toList();
+    private HashMap<Item, Integer> prices;
+    AcceptablePrices(HashMap<Item, Integer> prices) {
+        this.prices = prices;
+    }
+    Integer getMaxPriceForItem(Item i) {
+        return prices.get(i);
+    }
+    void setMaxPriceForItem(Item i, int p) {
+        prices.put(i, p);
+    }
+    void unsetMaxPriceForItem(Item i) {
+        prices.remove(i);
+    }
+    List<Item> allConfiguredItems() {
+        return prices.keySet().stream().toList();
+    }
+
+    @Override
+    public WidgetScreen createScreen(GuiTheme theme) {
+        return new AcceptablePricesScreen(theme, this);
+    }
+
+    @Override
+    public AcceptablePrices set(AcceptablePrices value) {
+        this.prices = value.prices;
+        return this;
+    }
+
+    @Override
+    public AcceptablePrices copy() {
+        return new AcceptablePrices(new HashMap<>(this.prices));
+    }
+
+    @Override
+    public NbtCompound toTag() {
+        NbtCompound ret = new NbtCompound();
+        NbtList l = new NbtList();
+        for (Item i : this.prices.keySet()) {
+            NbtCompound a = new NbtCompound();
+            a.putString("Item", Registries.ITEM.getId(i).toString());
+            a.putInt("Price", prices.get(i));
+            l.add(a);
+        }
+        ret.put("Prices", l);
+        MeteorClient.LOG.warn("AcceptablePrices NBT result: " + ret.asString());
+        return ret;
+    }
+
+    @Override
+    public AcceptablePrices fromTag(NbtCompound tag) {
+        MeteorClient.LOG.warn("AcceptablePrices NBT from: " + tag.asString());
+        HashMap<Item, Integer> ret = new HashMap<>();
+        try {
+            NbtList l = tag.getList("Prices", NbtElement.COMPOUND_TYPE);
+            for (int i = 0; i < l.size(); ++i) {
+                NbtCompound a = l.getCompound(i);
+                String item_id = a.getString("Item");
+                Item item = Registries.ITEM.get(new Identifier(item_id));
+                int price = a.getInt("Price");
+                ret.put(item, price);
+            }
+        } catch (NullPointerException e) {
+            this.prices = new HashMap<>();
+            return this;
+        }
+        MeteorClient.LOG.warn(ret.toString());
+        this.prices = ret;
+        return this;
+    }
+}
diff --git a/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePricesScreen.java b/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePricesScreen.java
new file mode 100644
index 0000000..aec721d
--- /dev/null
+++ b/src/main/java/org/chrisoft/trashyaddon/modules/AcceptablePricesScreen.java
@@ -0,0 +1,78 @@
+package org.chrisoft.trashyaddon.modules;
+
+import meteordevelopment.meteorclient.gui.GuiTheme;
+import meteordevelopment.meteorclient.gui.WindowScreen;
+import meteordevelopment.meteorclient.gui.screens.settings.ItemSettingScreen;
+import meteordevelopment.meteorclient.gui.widgets.WItemWithLabel;
+import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList;
+import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
+import meteordevelopment.meteorclient.gui.widgets.input.WIntEdit;
+import meteordevelopment.meteorclient.gui.widgets.pressable.WMinus;
+import meteordevelopment.meteorclient.settings.ItemSetting;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.item.Item;
+import net.minecraft.item.Items;
+
+import java.util.ArrayList;
+
+public class AcceptablePricesScreen extends WindowScreen {
+    private final AcceptablePrices targetValue;
+    private WTable table;
+    private ArrayList<Item> rowItems;
+    public AcceptablePricesScreen(GuiTheme theme, AcceptablePrices prices) {
+        super(theme, "Configure max prices for each trade");
+        targetValue = prices;
+        rowItems = new ArrayList<>();
+    }
+
+    @Override
+    public void initWidgets() {
+        this.add(theme.label("The amount set here corresponds to the max number of items in the first input slot."));
+        table = this.add(theme.table()).expandX().widget();
+        initTable();
+        WHorizontalList hl = theme.horizontalList();
+        hl.add(theme.label("")).expandCellX();
+        hl.add(theme.plus()).widget().action = () -> {
+            ItemSetting t = new ItemSetting("", "", Items.AIR, null, null, null,
+                (Item item) -> AcceptablePrices.allItems.contains(item) && (targetValue.getMaxPriceForItem(item) == null));
+            ItemSettingScreen screen = new ItemSettingScreen(this.theme, t);
+            screen.onClosed(() -> {
+                Item item = t.get();
+                if (item != null && item != Items.AIR)
+                    addRowForItem(item);
+            });
+            MinecraftClient.getInstance().setScreen(screen);
+        };
+        this.add(hl).expandX();
+    }
+
+    private void initTable() {
+        for (Item i : targetValue.allConfiguredItems()) {
+            addRowForItem(i);
+        }
+    }
+    private void addRowForItem(Item item) {
+        rowItems.add(item);
+        WItemWithLabel itemDisplay = theme.itemWithLabel(item.getDefaultStack(), item.getName().getString());
+        table.add(itemDisplay).expandCellX();
+        Integer v = targetValue.getMaxPriceForItem(item);
+        if (v == null) {
+            targetValue.setMaxPriceForItem(item, 1);
+            v = 1;
+        }
+        WIntEdit priceEdit = theme.intEdit(v, 1, 64, true);
+        priceEdit.action = () -> {
+            targetValue.setMaxPriceForItem(item, priceEdit.get());
+        };
+        table.add(priceEdit);
+        WMinus deleteButton = theme.minus();
+        deleteButton.action = () -> {
+            table.removeRow(rowItems.indexOf(item));
+            this.invalidate();
+            rowItems.remove(item);
+            targetValue.unsetMaxPriceForItem(item);
+        };
+        table.add(deleteButton);
+        table.row();
+    }
+}
diff --git a/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java b/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java
index c5df1b9..0b71598 100644
--- a/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java
+++ b/src/main/java/org/chrisoft/trashyaddon/modules/AutoTrade.java
@@ -20,14 +20,11 @@ import net.minecraft.village.TradeOffer;
 import net.minecraft.village.TradeOfferList;
 import org.chrisoft.trashyaddon.mixin.MerchantScreenAccessor;
 
-import java.util.ArrayList;
-import java.util.Formatter;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
 
 public class AutoTrade extends Module {
     private final SettingGroup sgGeneral = settings.getDefaultGroup();
-    private static final List<Item> allSellItems = List.of(
+    public static final List<Item> allSellItems = List.of(
         Items.COAL,
         Items.CHICKEN,
         Items.PORKCHOP,
@@ -73,7 +70,7 @@ public class AutoTrade extends Module {
         Items.QUARTZ
     );
 
-    private static final List<Item> allBuyItems = List.of(
+    public static final List<Item> allBuyItems = List.of(
         Items.BELL,
         Items.COOKED_PORKCHOP,
         Items.COOKED_CHICKEN,
@@ -129,6 +126,12 @@ public class AutoTrade extends Module {
         .filter((item) -> allBuyItems.contains(item))
         .build()
     );
+    private  final Setting<AcceptablePrices> acceptablePricesSetting = sgGeneral.add(new GenericSetting.Builder<AcceptablePrices>()
+        .name("acceptable-prices")
+        .description("Configure maximum acceptable price for each item.")
+        .defaultValue(new AcceptablePrices(new HashMap<>()))
+        .build()
+    );
     private final Setting<Integer> interactionRate = sgGeneral.add(new IntSetting.Builder()
         .name("Interaction Rate")
         .description("Number of ticks between interactions.")
@@ -190,10 +193,21 @@ public class AutoTrade extends Module {
         ItemStack msell = o.getSellItem();
         List<Item> sells = sellingEnabled.get() ? this.sellsSetting.get() : List.of();
         List<Item> buys = buyingEnabled.get() ? this.buysSetting.get() : List.of();
-        if (!sells.contains(mbuy.getItem()) && !buys.contains(msell.getItem()))
+        Item interestedItem = null;
+        if (sells.contains(mbuy.getItem()))
+            interestedItem = mbuy.getItem();
+        if (buys.contains(msell.getItem()))
+            interestedItem = msell.getItem();
+        if (interestedItem == null)
             return false;
+        Integer maxPrice = acceptablePricesSetting.get().getMaxPriceForItem(interestedItem);
+        if (maxPrice != null && mbuy.getCount() > maxPrice) {
+            return false;
+        }
         FindItemResult rs = InvUtils.find((stack) -> stack.getItem().equals(mbuy.getItem()) && stack.getCount() >= mbuy.getCount());
-        if (!rs.found())
+        ItemStack s0is = screen.getScreenHandler().slots.get(0).getStack();
+        boolean remainingFirstSlotSufficient = s0is.getItem().equals(mbuy.getItem()) && s0is.getCount() >= mbuy.getCount();
+        if (!rs.found() && !remainingFirstSlotSufficient)
             return false;
         return true;
     }
-- 
cgit v1.2.3