aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/io')
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java2
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java4
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java19
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/ApiData.java84
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/Minion.java112
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/MinionHelperManager.java332
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java305
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperChatLoader.java91
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperInventoryLoader.java175
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/MinionHelperRepoLoader.java218
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/MinionHelperRepoMinionLoader.java132
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/RecipeBuilder.java75
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperOverlay.java400
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperOverlayHover.java240
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperTooltips.java91
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/renderables/OverviewLine.java25
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/renderables/OverviewText.java42
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/CollectionRequirement.java44
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/CustomRequirement.java33
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/MinionRequirement.java24
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/ReputationRequirement.java51
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/SlayerRequirement.java46
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/CraftingSource.java35
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/CustomSource.java33
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/MinionSource.java23
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/NpcSource.java47
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/util/MinionHelperPriceCalculation.java175
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/util/MinionHelperRequirementsManager.java129
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java8
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MinionHelper.java45
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java4
31 files changed, 3043 insertions, 1 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
index 09c910e9..301aec26 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java
@@ -67,6 +67,7 @@ import io.github.moulberry.notenoughupdates.miscgui.CalendarOverlay;
import io.github.moulberry.notenoughupdates.miscgui.InventoryStorageSelector;
import io.github.moulberry.notenoughupdates.miscgui.SignCalculator;
import io.github.moulberry.notenoughupdates.miscgui.TrophyRewardOverlay;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
import io.github.moulberry.notenoughupdates.mixins.AccessorMinecraft;
import io.github.moulberry.notenoughupdates.options.NEUConfig;
import io.github.moulberry.notenoughupdates.overlays.EquipmentOverlay;
@@ -307,6 +308,7 @@ public class NotEnoughUpdates {
MinecraftForge.EVENT_BUS.register(AbiphoneWarning.getInstance());
MinecraftForge.EVENT_BUS.register(new BetterContainers());
MinecraftForge.EVENT_BUS.register(AuctionBINWarning.getInstance());
+ MinecraftForge.EVENT_BUS.register(MinionHelperManager.getInstance());
MinecraftForge.EVENT_BUS.register(navigation);
if (Minecraft.getMinecraft().getResourceManager() instanceof IReloadableResourceManager) {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java
index bad9afe8..6036e796 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java
@@ -29,6 +29,7 @@ import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.Custom
import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent;
import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.SpecialBlockZone;
import io.github.moulberry.notenoughupdates.miscgui.GuiPriceGraph;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
import io.github.moulberry.notenoughupdates.util.PronounDB;
import io.github.moulberry.notenoughupdates.util.SBInfo;
import io.github.moulberry.notenoughupdates.util.TabListUtils;
@@ -197,6 +198,9 @@ public class DevTestCommand extends ClientCommandBase {
double z = Math.floor(Minecraft.getMinecraft().thePlayer.posZ) + 0.5f;
Minecraft.getMinecraft().thePlayer.setPosition(x, Minecraft.getMinecraft().thePlayer.posY, z);
}
+ if (args.length >= 1 && args[0].equalsIgnoreCase("minion")) {
+ MinionHelperManager.getInstance().handleCommand(args);
+ }
if (args.length == 1 && args[0].equalsIgnoreCase("copytablist")) {
StringBuilder builder = new StringBuilder();
for (String name : TabListUtils.getTabList()) {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java
index 3ddb05f6..e465f3d3 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/RenderListener.java
@@ -50,6 +50,7 @@ import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe;
import io.github.moulberry.notenoughupdates.miscgui.StorageOverlay;
import io.github.moulberry.notenoughupdates.miscgui.TradeWindow;
import io.github.moulberry.notenoughupdates.miscgui.TrophyRewardOverlay;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
import io.github.moulberry.notenoughupdates.miscgui.hex.GuiCustomHex;
import io.github.moulberry.notenoughupdates.mixins.AccessorGuiContainer;
import io.github.moulberry.notenoughupdates.options.NEUConfig;
@@ -536,6 +537,12 @@ public class RenderListener {
x += diffX;
}
}
+ if (MinionHelperManager.getInstance().inCraftedMinionsInventory()) {
+ int diffX = 172;
+ if (x > guiLeft + xSize && x < guiLeft + xSize + diffX + 5 && y > guiTop - 18 && y < guiTop + 128) {
+ x += diffX;
+ }
+ }
if (AuctionProfit.inAuctionPage()) {
if (x + 18 > guiLeft + xSize && x + 18 < guiLeft + xSize + 4 + 28 + 20 && y > guiTop - 180 &&
y < guiTop + 56) {
@@ -664,6 +671,12 @@ public class RenderListener {
x += diffX;
}
}
+ if (MinionHelperManager.getInstance().inCraftedMinionsInventory()) {
+ int diffX = 172;
+ if (x > guiLeft + xSize && x < guiLeft + xSize + diffX + 5 && y > guiTop - 18 && y < guiTop + 128) {
+ x += diffX;
+ }
+ }
if (AuctionProfit.inAuctionPage()) {
if (x + 18 > guiLeft + xSize && x + 18 < guiLeft + xSize + 4 + 28 + 20 && y > guiTop - 180 &&
y < guiTop + 56) {
@@ -1141,6 +1154,12 @@ public class RenderListener {
x += diffX;
}
}
+ if (MinionHelperManager.getInstance().inCraftedMinionsInventory()) {
+ int diffX = 172;
+ if (x > guiLeft + xSize && x < guiLeft + xSize + diffX + 5 && y > guiTop - 18 && y < guiTop + 128) {
+ x += diffX;
+ }
+ }
if (AuctionProfit.inAuctionPage()) {
if (x + 18 > guiLeft + xSize && x + 18 < guiLeft + xSize + 4 + 28 + 20 && y > guiTop - 180 &&
y < guiTop + 56) {
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/ApiData.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/ApiData.java
new file mode 100644
index 00000000..215c3fe7
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/ApiData.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper;
+
+import java.util.List;
+import java.util.Map;
+
+public class ApiData {
+
+ private final Map<String, Integer> highestCollectionTier;
+ private final Map<String, Integer> slayerTiers;
+ private final int magesReputation;
+ private final int barbariansReputation;
+ private final boolean collectionApiDisabled;
+ private final List<String> craftedMinions;
+ private int peltCount;
+
+ public ApiData(
+ Map<String, Integer> highestCollectionTier,
+ Map<String, Integer> slayerTiers,
+ int magesReputation,
+ int barbariansReputation,
+ boolean collectionApiDisabled,
+ List<String> craftedMinions,
+ int peltCount
+ ) {
+ this.highestCollectionTier = highestCollectionTier;
+ this.slayerTiers = slayerTiers;
+ this.magesReputation = magesReputation;
+ this.barbariansReputation = barbariansReputation;
+ this.collectionApiDisabled = collectionApiDisabled;
+ this.craftedMinions = craftedMinions;
+ this.peltCount = peltCount;
+ }
+
+ public Map<String, Integer> getHighestCollectionTier() {
+ return highestCollectionTier;
+ }
+
+ public Map<String, Integer> getSlayerTiers() {
+ return slayerTiers;
+ }
+
+ public int getMagesReputation() {
+ return magesReputation;
+ }
+
+ public int getBarbariansReputation() {
+ return barbariansReputation;
+ }
+
+ public boolean isCollectionApiDisabled() {
+ return collectionApiDisabled;
+ }
+
+ public List<String> getCraftedMinions() {
+ return craftedMinions;
+ }
+
+ public int getPeltCount() {
+ return peltCount;
+ }
+
+ public void setPeltCount(int peltCount) {
+ this.peltCount = peltCount;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/Minion.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/Minion.java
new file mode 100644
index 00000000..ba38b01d
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/Minion.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper;
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.renderables.OverviewLine;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.MinionRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.CustomSource;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.MinionSource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Minion extends OverviewLine {
+ private final String internalName;
+ private final int tier;
+ private String displayName;
+ private MinionSource minionSource;
+ private CustomSource customSource;
+ private Minion parent;
+ private final List<MinionRequirement> requirements = new ArrayList<>();
+
+ private boolean crafted = false;
+ private boolean meetRequirements = false;
+
+ public Minion(String internalName, int tier) {
+ this.internalName = internalName;
+ this.tier = tier;
+ }
+
+ public MinionSource getMinionSource() {
+ return minionSource;
+ }
+
+ public void setMinionSource(MinionSource minionSource) {
+ this.minionSource = minionSource;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public boolean isCrafted() {
+ return crafted;
+ }
+
+ public void setCrafted(boolean crafted) {
+ this.crafted = crafted;
+ }
+
+ public String getInternalName() {
+ return internalName;
+ }
+
+ public void setParent(Minion parent) {
+ this.parent = parent;
+ }
+
+ public Minion getParent() {
+ return parent;
+ }
+
+ public int getTier() {
+ return tier;
+ }
+
+ public List<MinionRequirement> getRequirements() {
+ return requirements;
+ }
+
+ public boolean doesMeetRequirements() {
+ return meetRequirements;
+ }
+
+ public void setMeetRequirements(boolean meetRequirements) {
+ this.meetRequirements = meetRequirements;
+ }
+
+ @Override
+ public void onClick() {
+ NotEnoughUpdates.INSTANCE.manager.displayGuiItemRecipe(internalName);
+ }
+
+ public void setCustomSource(CustomSource customSource) {
+ this.customSource = customSource;
+ }
+
+ public CustomSource getCustomSource() {
+ return customSource;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/MinionHelperManager.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/MinionHelperManager.java
new file mode 100644
index 00000000..f3c8a86a
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/MinionHelperManager.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper;
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders.MinionHelperApiLoader;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders.MinionHelperChatLoader;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders.MinionHelperInventoryLoader;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders.repo.MinionHelperRepoLoader;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.MinionHelperOverlay;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.MinionHelperTooltips;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.CustomSource;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.MinionSource;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.util.MinionHelperPriceCalculation;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.util.MinionHelperRequirementsManager;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.ContainerChest;
+import net.minecraftforge.common.MinecraftForge;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MinionHelperManager {
+ private static MinionHelperManager instance = null;
+ private final Map<String, Minion> minions = new HashMap<>();
+ private int needForNextSlot = -1;
+ private int localPelts = -1;
+
+ private final MinionHelperPriceCalculation priceCalculation = new MinionHelperPriceCalculation(this);
+ private final MinionHelperRequirementsManager requirementsManager = new MinionHelperRequirementsManager(this);
+ private final MinionHelperApiLoader api = new MinionHelperApiLoader(this);
+ private final MinionHelperRepoLoader repo = new MinionHelperRepoLoader(this);
+ private final MinionHelperOverlay overlay = new MinionHelperOverlay(this);
+ private final MinionHelperInventoryLoader inventoryLoader = new MinionHelperInventoryLoader(this);
+ private String debugPlayerUuid;
+ private String debugProfileName;
+ private int debugNeedForNextSlot = -1;
+
+ public static MinionHelperManager getInstance() {
+ if (instance == null) {
+ instance = new MinionHelperManager();
+ }
+ return instance;
+ }
+
+ private MinionHelperManager() {
+ MinecraftForge.EVENT_BUS.register(priceCalculation);
+ MinecraftForge.EVENT_BUS.register(api);
+ MinecraftForge.EVENT_BUS.register(repo);
+ MinecraftForge.EVENT_BUS.register(overlay);
+ MinecraftForge.EVENT_BUS.register(new MinionHelperTooltips(this));
+ MinecraftForge.EVENT_BUS.register(new MinionHelperChatLoader(this));
+ MinecraftForge.EVENT_BUS.register(inventoryLoader);
+ }
+
+ public boolean inCraftedMinionsInventory() {
+ if (!NotEnoughUpdates.INSTANCE.isOnSkyblock()) return false;
+
+ Minecraft minecraft = Minecraft.getMinecraft();
+ if (minecraft == null || minecraft.thePlayer == null) return false;
+
+ Container inventoryContainer = minecraft.thePlayer.openContainer;
+ if (!(inventoryContainer instanceof ContainerChest)) return false;
+ ContainerChest containerChest = (ContainerChest) inventoryContainer;
+ String name = containerChest.getLowerChestInventory().getDisplayName().getUnformattedText();
+ return name.equalsIgnoreCase("Crafted Minions");
+ }
+
+ public boolean notReady() {
+ return !repo.isReadyToUse() || !api.isReadyToUse();
+ }
+
+ public boolean isInvalidApiKey() {
+ return api.isInvalidApiKey();
+ }
+
+ public Minion getMinionById(String internalName) {
+ if (minions.containsKey(internalName)) {
+ return minions.get(internalName);
+ } else {
+ System.err.println("Cannot get minion for id '" + internalName + "'!");
+ return null;
+ }
+ }
+
+ public Minion getMinionByName(String displayName, int tier) {
+ for (Minion minion : minions.values()) {
+ if (displayName.equals(minion.getDisplayName())) {
+ if (minion.getTier() == tier) {
+ return minion;
+ }
+ }
+ }
+ System.err.println("Cannot get minion for display name '" + displayName + "'!");
+ return null;
+ }
+
+ public void createMinion(String internalName, int tier) {
+ minions.put(internalName, new Minion(internalName, tier));
+ }
+
+ public String formatInternalName(String minionName) {
+ return minionName.toUpperCase().replace(" ", "_");
+ }
+
+ private List<Minion> getChildren(Minion minion) {
+ List<Minion> list = new ArrayList<>();
+ for (Minion other : minions.values()) {
+ if (minion == other.getParent()) {
+ list.add(other);
+ list.addAll(getChildren(other));
+ break;
+ }
+ }
+ return list;
+ }
+
+ public void onProfileSwitch() {
+ for (Minion minion : minions.values()) {
+ minion.setCrafted(false);
+ minion.setMeetRequirements(false);
+ }
+
+ needForNextSlot = -1;
+ api.onProfileSwitch();
+ overlay.onProfileSwitch();
+ inventoryLoader.onProfileSwitch();
+ }
+
+ public void reloadData() {
+ requirementsManager.reloadRequirements();
+
+ ApiData apiData = api.getApiData();
+ if (apiData != null) {
+ for (String minion : apiData.getCraftedMinions()) {
+ setCrafted(getMinionById(minion));
+ }
+ }
+ }
+
+ public void setCrafted(Minion minion) {
+ minion.setCrafted(true);
+
+ if (minion.getCustomSource() != null) {
+ minion.setMeetRequirements(true);
+
+ for (Minion child : getChildren(minion)) {
+ child.setMeetRequirements(true);
+ }
+ }
+ }
+
+ public void handleCommand(String[] args) {
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) {
+ Utils.addChatMessage("§e[NEU] Minion Helper gui is disabled!");
+ return;
+ }
+
+ if (args.length > 1) {
+ String parameter = args[1];
+
+ if (parameter.equals("debugplayer")) {
+ if (args.length == 3) {
+ if (args[2].equals("reset")) {
+ Utils.addChatMessage("§e[NEU] Minion debug player reset.");
+ setDebugPlayer(null, null, -1);
+ return;
+ }
+ }
+ if (args.length < 4) {
+ Utils.addChatMessage("§c[NEU] Usage: /neudevtest minion " +
+ "setplayer <player-uuid> <player-profile-name> [need-for-next-slot]");
+ return;
+ }
+ String playerUuid = args[2];
+ String playerProfileName = args[3];
+ int need = args.length == 5 ? Integer.parseInt(args[4]) : -1;
+ setDebugPlayer(playerUuid, playerProfileName, need);
+ Utils.addChatMessage("§e[NEU] Minion debug player set.");
+ return;
+ }
+
+ if (args.length == 2) {
+ if (parameter.equals("clearminion")) {
+ minions.clear();
+ Utils.addChatMessage("minion map cleared");
+ return;
+ }
+ if (parameter.equals("reloadrepo")) {
+ repo.setDirty();
+ Utils.addChatMessage("repo reload requested");
+ return;
+ }
+ if (parameter.equals("reloadapi")) {
+ api.resetData();
+ api.setDirty();
+ Utils.addChatMessage("api reload requested");
+ return;
+ }
+ if (parameter.equals("clearapi")) {
+ api.resetData();
+ Utils.addChatMessage("api data cleared");
+ return;
+ }
+ }
+
+ if (args.length == 3) {
+ if (parameter.equals("maxperpage")) {
+ api.resetData();
+ int maxPerPage = Integer.parseInt(args[2]);
+ Utils.addChatMessage("set max per page to " + maxPerPage);
+ overlay.setMaxPerPage(maxPerPage);
+ return;
+ }
+ }
+
+ if (args.length == 4) {
+ if (parameter.equals("arrowpos")) {
+ int x = Integer.parseInt(args[2]);
+ int y = Integer.parseInt(args[3]);
+ Utils.addChatMessage("set page pos to " + x + ";" + y);
+ overlay.setTopLeft(new int[]{x, y});
+ return;
+ }
+ }
+ }
+
+ Utils.addChatMessage("");
+ Utils.addChatMessage("§3NEU Minion Helper commands: §c(for testing only!)");
+ Utils.addChatMessage("§6/neudevtest minion clearminion §7Clears the minion map");
+ Utils.addChatMessage("§6/neudevtest minion reloadrepo §7Manually loading the data from repo");
+ Utils.addChatMessage("§6/neudevtest minion reloadapi §7Manually loading the data from api");
+ Utils.addChatMessage("§6/neudevtest minion clearapi §7Clears the api data");
+ Utils.addChatMessage("§6/neudevtest minion maxperpage <number> §7Changes the max minions per page number");
+ Utils.addChatMessage("§6/neudevtest minion arrowpos <x, y> §7Changes the position of the page numbers");
+ Utils.addChatMessage("§6/neudevtest minion debugplayer <player-uuid> <player-profile-name> [need-for-next-slot] §7" +
+ "See the Minions missing of other player");
+ Utils.addChatMessage("");
+ }
+
+ private void setDebugPlayer(String playerUuid, String playerProfileName, int fakeNeedForNextSlot) {
+ this.debugPlayerUuid = playerUuid;
+ this.debugProfileName = playerProfileName;
+ this.debugNeedForNextSlot = fakeNeedForNextSlot;
+
+ onProfileSwitch();
+ }
+
+ public MinionHelperPriceCalculation getPriceCalculation() {
+ return priceCalculation;
+ }
+
+ public MinionHelperRequirementsManager getRequirementsManager() {
+ return requirementsManager;
+ }
+
+ public MinionHelperApiLoader getApi() {
+ return api;
+ }
+
+ public MinionHelperOverlay getOverlay() {
+ return overlay;
+ }
+
+ public Map<String, Minion> getAllMinions() {
+ return minions;
+ }
+
+ public void setNeedForNextSlot(int needForNextSlot) {
+ this.needForNextSlot = needForNextSlot;
+ overlay.resetCache();
+ }
+
+ public int getNeedForNextSlot() {
+ return needForNextSlot;
+ }
+
+ public void setCustomSource(Minion minion, CustomSource customSource) {
+ MinionSource minionSource = minion.getMinionSource();
+ if (minionSource == null) {
+ minion.setMinionSource(customSource);
+ }
+ minion.setCustomSource(customSource);
+ }
+
+ public int getLocalPelts() {
+ return localPelts;
+ }
+
+ public void setLocalPelts(int pelts) {
+ localPelts = pelts;
+ if (localPelts != -1) {
+ ApiData apiData = api.getApiData();
+ if (apiData != null) {
+ apiData.setPeltCount(localPelts);
+ }
+ }
+ }
+
+ public String getDebugPlayerUuid() {
+ return debugPlayerUuid;
+ }
+
+ public String getDebugProfileName() {
+ return debugProfileName;
+ }
+
+ public int getDebugNeedForNextSlot() {
+ return debugNeedForNextSlot;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java
new file mode 100644
index 00000000..aaa398f4
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.events.ProfileDataLoadedEvent;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.ApiData;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.profileviewer.ProfileViewer;
+import io.github.moulberry.notenoughupdates.util.Constants;
+import io.github.moulberry.notenoughupdates.util.SBInfo;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.event.world.WorldEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MinionHelperApiLoader {
+ private final MinionHelperManager manager;
+ private boolean dirty = true;
+ private int ticks = 0;
+ private boolean collectionApiEnabled = true;
+ private boolean ignoreWorldSwitches = false;
+ private boolean readyToUse = false;
+ private ApiData apiData = null;
+ private boolean notifyNoCollectionApi = false;
+ private long lastLoaded = 0;
+ private boolean invalidApiKey = false;
+
+ public MinionHelperApiLoader(MinionHelperManager manager) {
+ this.manager = manager;
+ }
+
+ @SubscribeEvent
+ public void onWorldLoad(WorldEvent.Load event) {
+ if (ignoreWorldSwitches) return;
+
+ setDirty();
+ }
+
+ @SubscribeEvent
+ public void onTick(TickEvent.ClientTickEvent event) {
+ if (Minecraft.getMinecraft().thePlayer == null) return;
+ if (!NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) return;
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return;
+ ticks++;
+
+ if (ticks % 20 != 0) return;
+
+ if (dirty) {
+ load();
+ } else {
+ if (System.currentTimeMillis() > lastLoaded + 60_000 * 3) {
+ dirty = true;
+ }
+ }
+ }
+
+ private void load() {
+ lastLoaded = System.currentTimeMillis();
+
+ dirty = false;
+ String uuid = getUuid();
+ if (uuid == null) return;
+
+ NotEnoughUpdates.INSTANCE.manager.apiUtils.updateProfileData(uuid);
+ }
+
+ private String getUuid() {
+ EntityPlayerSP thePlayer = Minecraft.getMinecraft().thePlayer;
+ if (thePlayer == null) return null;
+
+ String debugPlayerUuid = manager.getDebugPlayerUuid();
+ if (debugPlayerUuid != null) return debugPlayerUuid;
+
+ return thePlayer.getUniqueID().toString().replace("-", "");
+ }
+
+ @SubscribeEvent
+ public void onApiDataLoaded(ProfileDataLoadedEvent event) {
+ JsonObject data = event.getData();
+ if (data == null) {
+ invalidApiKey = true;
+ return;
+ }
+ invalidApiKey = false;
+
+ if (!data.has("success") || !data.get("success").getAsBoolean()) return;
+ JsonArray profiles = data.getAsJsonArray("profiles");
+ for (JsonElement element : profiles) {
+ JsonObject profile = element.getAsJsonObject();
+ String profileName = profile.get("cute_name").getAsString();
+ JsonObject members = profile.getAsJsonObject("members");
+ JsonObject player = members.getAsJsonObject(getUuid());
+
+ String debugProfileName = manager.getDebugProfileName();
+ String currentProfile = debugProfileName != null ? debugProfileName : SBInfo.getInstance().currentProfile;
+
+ if (profileName.equals(currentProfile)) {
+ readData(player, members);
+ return;
+ }
+ }
+ }
+
+ private void readData(JsonObject player, JsonObject members) {
+ int magesReputation = 0;
+ int barbariansReputation = 0;
+ if (player.has("nether_island_player_data")) {
+ JsonObject netherData = player.getAsJsonObject("nether_island_player_data");
+ if (netherData.has("mages_reputation")) {
+ magesReputation = netherData.get("mages_reputation").getAsInt();
+ }
+ if (netherData.has("barbarians_reputation")) {
+ barbariansReputation = netherData.get("barbarians_reputation").getAsInt();
+ }
+ }
+
+ apiData = new ApiData(
+ getCollections(player),
+ getSlayers(player),
+ magesReputation,
+ barbariansReputation,
+ !collectionApiEnabled,
+ loadCraftedMinions(members),
+ loadPeltCount(player)
+ );
+
+ manager.reloadData();
+ readyToUse = true;
+ }
+
+ private int loadPeltCount(JsonObject player) {
+ int localPelts = manager.getLocalPelts();
+ if (localPelts != -1) return localPelts;
+
+ int peltCount = 0;
+ if (player.has("trapper_quest")) {
+ JsonObject jsonObject = player.getAsJsonObject("trapper_quest");
+ if (jsonObject.has("pelt_count")) {
+ peltCount = jsonObject.get("pelt_count").getAsInt();
+ }
+ }
+ return peltCount;
+ }
+
+ private Map<String, Integer> getSlayers(JsonObject player) {
+ JsonObject slayerLeveling = Constants.LEVELING.getAsJsonObject("slayer_xp");
+
+ Map<String, Integer> slayerTier = new HashMap<>();
+ if (player.has("slayer_bosses")) {
+ JsonObject slayerBosses = player.getAsJsonObject("slayer_bosses");
+ for (Map.Entry<String, JsonElement> entry : slayerBosses.entrySet()) {
+ String name = entry.getKey();
+ JsonObject slayerEntry = entry.getValue().getAsJsonObject();
+ if (slayerEntry.has("xp")) {
+ long xp = slayerEntry.get("xp").getAsLong();
+
+ int tier = 0;
+ for (JsonElement element : slayerLeveling.getAsJsonArray(name)) {
+ int needForLevel = element.getAsInt();
+ if (xp >= needForLevel) {
+ tier++;
+ } else {
+ break;
+ }
+ }
+ slayerTier.put(name, tier);
+ }
+ }
+ }
+ return slayerTier;
+ }
+
+ private Map<String, Integer> getCollections(JsonObject player) {
+ Map<String, Integer> highestCollectionTier = new HashMap<>();
+ if (player.has("unlocked_coll_tiers")) {
+ for (JsonElement element : player.get("unlocked_coll_tiers").getAsJsonArray()) {
+ String text = element.getAsString();
+ String[] split = text.split("_");
+ int level = Integer.parseInt(split[split.length - 1]);
+ String name = StringUtils.removeLastWord(text, "_");
+
+ //Because skyblock is good in naming things
+ LinkedHashMap<String, ItemStack> collectionMap = ProfileViewer.getCollectionToCollectionDisplayMap();
+ if (collectionMap.containsKey(name)) {
+ ItemStack itemStack = collectionMap.get(name);
+ String displayName = itemStack.getDisplayName();
+ name = Utils.cleanColour(displayName);
+ name = manager.formatInternalName(name);
+ } else {
+ //Doing this since there is no space in the profile viewer gui for more entries in collectionToCollectionDisplayMap
+ if (name.equals("SAND:1")) name = "RED_SAND";
+ if (name.equals("MYCEL")) name = "MYCELIUM";
+ }
+
+ level = Math.max(highestCollectionTier.getOrDefault(name, 0), level);
+ highestCollectionTier.put(name, level);
+ }
+ if (!collectionApiEnabled) {
+ Utils.addChatMessage("§e[NEU] Collection API detected!");
+ }
+ collectionApiEnabled = true;
+ } else {
+ if (collectionApiEnabled) {
+ notifyNoCollectionApi = true;
+ }
+ collectionApiEnabled = false;
+ }
+ return highestCollectionTier;
+ }
+
+ private List<String> loadCraftedMinions(JsonObject members) {
+ List<String> craftedMinions = new ArrayList<>();
+ for (Map.Entry<String, JsonElement> entry : members.entrySet()) {
+ JsonObject value = entry.getValue().getAsJsonObject();
+ if (value.has("crafted_generators")) {
+ for (JsonElement e : value.get("crafted_generators").getAsJsonArray()) {
+ String rawGenerator = e.getAsString();
+ String[] split = rawGenerator.split("_");
+ String tier = split[split.length - 1];
+ String name = rawGenerator.substring(0, rawGenerator.length() - tier.length() - 1);
+ String internalName = name + "_GENERATOR_" + tier;
+ craftedMinions.add(internalName);
+ }
+ }
+ }
+
+ return craftedMinions;
+ }
+
+ public void setDirty() {
+ dirty = true;
+ readyToUse = false;
+ }
+
+ public void prepareProfileSwitch() {
+ ignoreWorldSwitches = true;
+ readyToUse = false;
+ }
+
+ public void onProfileSwitch() {
+ apiData = null;
+ setDirty();
+ ignoreWorldSwitches = false;
+ collectionApiEnabled = true;
+ }
+
+ public boolean isReadyToUse() {
+ return readyToUse;
+ }
+
+ public ApiData getApiData() {
+ return apiData;
+ }
+
+ public boolean isCollectionApiDisabled() {
+ return apiData != null && apiData.isCollectionApiDisabled();
+ }
+
+ public void resetData() {
+ apiData = null;
+ }
+
+ public void setNotifyNoCollectionApi(boolean notifyNoCollectionApi) {
+ this.notifyNoCollectionApi = notifyNoCollectionApi;
+ }
+
+ public boolean isNotifyNoCollectionApi() {
+ return notifyNoCollectionApi;
+ }
+
+ public boolean isInvalidApiKey() {
+ return invalidApiKey;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperChatLoader.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperChatLoader.java
new file mode 100644
index 00000000..bef633d2
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperChatLoader.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders;
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class MinionHelperChatLoader {
+
+ private final MinionHelperManager manager;
+
+ //§aYou crafted a §eTier I Redstone Minion§a! That's a new one!
+ // §aCraft §e7 §amore unique Minions to unlock your §e9th Minion slot§a!
+ private final Pattern PATTERN_OWN_MINION = Pattern.compile(
+ "§r§aYou crafted a §eTier (\\S+) (.+) Minion§a! That's a new one!(\\r\\n|\\r|\\n)(.*)");
+ //§aYou crafted a §eTier VI Enderman Minion§a! That's a new one!
+
+ //§b[MVP§3+§b] Eisengolem§f §acrafted a §eTier I Birch Minion§a!
+ private final Pattern PATTERN_COOP_MINION = Pattern.compile(
+ "(.+)§f §acrafted a §eTier (\\S+) (.+) Minion§a!(§r)?(\\r\\n|\\r|\\n)?(.*)?");
+
+ public MinionHelperChatLoader(MinionHelperManager manager) {
+ this.manager = manager;
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true)
+ public void onChat(ClientChatReceivedEvent event) {
+ if (event.type != 0) return;
+ String message = event.message.getFormattedText();
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return;
+
+ try {
+ Matcher ownMatcher = PATTERN_OWN_MINION.matcher(message);
+ if (ownMatcher.matches()) {
+ String rawTier = ownMatcher.group(1);
+ int tier = Utils.parseRomanNumeral(rawTier);
+ String name = ownMatcher.group(2) + " Minion";
+
+ setCrafted(manager.getMinionByName(name, tier));
+ }
+
+ Matcher coopMatcher = PATTERN_COOP_MINION.matcher(message);
+ if (coopMatcher.matches()) {
+ String rawTier = coopMatcher.group(2);
+ int tier = Utils.parseRomanNumeral(rawTier);
+ String name = coopMatcher.group(3) + " Minion";
+
+ setCrafted(manager.getMinionByName(name, tier));
+ manager.getOverlay().resetCache();
+ }
+
+ if (message.startsWith("§r§7Switching to profile ")) {
+ manager.getApi().prepareProfileSwitch();
+ }
+
+ } catch (Exception e) {
+ Utils.addChatMessage(
+ "[NEU] §cMinion Helper failed reading the minion upgrade message. See the logs for more info!");
+ e.printStackTrace();
+ }
+ }
+
+ private void setCrafted(Minion minion) {
+ manager.setCrafted(minion);
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperInventoryLoader.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperInventoryLoader.java
new file mode 100644
index 00000000..7fc703e6
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperInventoryLoader.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders;
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.util.ItemUtils;
+import io.github.moulberry.notenoughupdates.util.TabListUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.ContainerChest;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.event.world.WorldEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class MinionHelperInventoryLoader {
+ private final MinionHelperManager manager;
+ private final List<String> pagesSeenAlready = new ArrayList<>();
+ private boolean dirty = true;
+
+ private int ticks = 0;
+
+ //§7Craft §b5 §7more §aunique §7minions
+ private final Pattern PATTERN_MINIONS_NEEDED = Pattern.compile("§7Craft §b(\\d+) §7more §aunique( §7minions)?");
+
+ //§r §r§fPelts: §r§59§r
+ private final Pattern PATTERN_PELTS = Pattern.compile("§r §r§fPelts: §r§5(\\d+)§r");
+
+ public MinionHelperInventoryLoader(MinionHelperManager manager) {
+ this.manager = manager;
+ }
+
+ @SubscribeEvent
+ public void onWorldLoad(WorldEvent.Load event) {
+ manager.setLocalPelts(-1);
+ }
+
+ @SubscribeEvent
+ public void onTick(TickEvent.ClientTickEvent event) {
+ if (!NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) return;
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return;
+ if (manager.notReady()) return;
+ ticks++;
+
+ if (ticks % 5 != 0) return;
+
+ if (manager.inCraftedMinionsInventory()) {
+ checkInventory();
+ } else {
+ pagesSeenAlready.clear();
+ dirty = true;
+ }
+ }
+
+ private void checkInventory() {
+ Container openContainer = Minecraft.getMinecraft().thePlayer.openContainer;
+ if (openContainer instanceof ContainerChest) {
+ if (dirty) {
+ dirty = false;
+ checkNextSlot(openContainer);
+ checkLocalPelts();
+ }
+ if (manager.getDebugPlayerUuid() == null) {
+ loadMinionData(openContainer);
+ }
+ }
+ }
+
+ private void checkLocalPelts() {
+ int pelts = -1;
+ for (String name : TabListUtils.getTabList()) {
+ Matcher matcher = PATTERN_PELTS.matcher(name);
+ if (matcher.matches()) {
+ pelts = Integer.parseInt(matcher.group(1));
+ break;
+ }
+ }
+
+ manager.setLocalPelts(pelts);
+ }
+
+ private void checkNextSlot(Container openContainer) {
+ Slot informationSlot = openContainer.inventorySlots.get(50);
+ if (informationSlot.getHasStack()) {
+ ItemStack informationStack = informationSlot.getStack();
+ for (String line : ItemUtils.getLore(informationStack)) {
+ Matcher matcher = PATTERN_MINIONS_NEEDED.matcher(line);
+ if (matcher.matches()) {
+ int debugNeedForNextSlot = manager.getDebugNeedForNextSlot();
+ int needForNextSlot = debugNeedForNextSlot != -1 ? debugNeedForNextSlot : Integer.parseInt(matcher.group(1));
+
+ manager.setNeedForNextSlot(needForNextSlot);
+ return;
+ }
+ }
+ }
+ }
+
+ private void loadMinionData(Container openContainer) {
+ Slot firstSlot = openContainer.inventorySlots.get(10);
+ boolean shouldLoad = false;
+ if (firstSlot != null) {
+ if (firstSlot.getHasStack()) {
+ ItemStack stack = firstSlot.getStack();
+ String displayName = stack.getDisplayName();
+ if (!pagesSeenAlready.contains(displayName)) {
+ pagesSeenAlready.add(displayName);
+ shouldLoad = true;
+ }
+ }
+ }
+
+ if (!shouldLoad) return;
+
+ int crafted = 0;
+ for (Slot slot : openContainer.inventorySlots) {
+ if (!slot.getHasStack()) continue;
+ ItemStack stack = slot.getStack();
+ if (stack == null) continue;
+ if (slot.slotNumber != slot.getSlotIndex()) continue;
+
+ String displayName = stack.getDisplayName();
+ if (!displayName.contains(" Minion")) continue;
+
+ displayName = StringUtils.cleanColour(displayName);
+ int index = 0;
+ for (String line : ItemUtils.getLore(stack)) {
+ index++;
+ if (!line.contains("Tier")) {
+ continue;
+ }
+ if (line.contains("§a")) {
+ Minion minion = manager.getMinionByName(displayName, index);
+ if (!minion.isCrafted()) {
+ minion.setCrafted(true);
+ crafted++;
+ }
+ }
+ }
+ }
+ if (crafted > 0) {
+ manager.getOverlay().resetCache();
+ }
+ }
+
+ public void onProfileSwitch() {
+ dirty = true;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/MinionHelperRepoLoader.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/MinionHelperRepoLoader.java
new file mode 100644
index 00000000..49dac537
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/MinionHelperRepoLoader.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders.repo;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.events.RepositoryReloadEvent;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.CustomRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.CustomSource;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.NpcSource;
+import io.github.moulberry.notenoughupdates.util.Constants;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraft.client.Minecraft;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class MinionHelperRepoLoader {
+ private final MinionHelperManager manager;
+ private boolean dirty = true;
+ private int ticks = 0;
+ private boolean readyToUse = false;
+ private final MinionHelperRepoMinionLoader minionLoader;
+ boolean errorWhileLoading = false;
+
+ public MinionHelperRepoLoader(MinionHelperManager manager) {
+ this.manager = manager;
+ minionLoader = new MinionHelperRepoMinionLoader(this, manager);
+ }
+
+ /**
+ * This adds support for the /neureloadrepo command
+ */
+ @SubscribeEvent(priority = EventPriority.LOWEST)
+ public void onRepoReload(RepositoryReloadEvent event) {
+ setDirty();
+ }
+
+ @SubscribeEvent
+ public void onTick(TickEvent.ClientTickEvent event) {
+ if (Minecraft.getMinecraft().thePlayer == null) return;
+ if (!NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) return;
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return;
+ ticks++;
+
+ if (ticks % 5 != 0) return;
+
+ if (dirty) {
+ dirty = false;
+ load();
+ }
+ }
+
+ void load() {
+ errorWhileLoading = false;
+
+ createMinions();
+
+ loadNpcData();
+ minionLoader.loadMinionData();
+ loadCustomSources();
+
+ testForMissingData();
+
+ manager.reloadData();
+ readyToUse = true;
+
+ if (errorWhileLoading) {
+ Utils.showOutdatedRepoNotification();
+ }
+ }
+
+ private void loadCustomSources() {
+ Map<String, String> customSource = new HashMap<>();
+
+ customSource.put("SNOW_GENERATOR_1", "Gifts");
+
+ customSource.put("FLOWER_GENERATOR_1", "Dark Auction");
+
+ customSource.put("REVENANT_GENERATOR_1", "Zombie Slayer");
+ customSource.put("TARANTULA_GENERATOR_1", "Spider Slayer");
+
+ for (Map.Entry<String, String> entry : customSource.entrySet()) {
+ String internalName = entry.getKey();
+ String sourceName = entry.getValue();
+ Minion minion = manager.getMinionById(internalName);
+ if (minion == null) continue;
+ manager.setCustomSource(minion, new CustomSource(sourceName));
+ }
+
+ manager.getMinionById("FLOWER_GENERATOR_1").getRequirements().add(new CustomRequirement(
+ "Buy a §cFlower Minion 1 §7from Dark Auction"));
+ manager.getMinionById("SNOW_GENERATOR_1").getRequirements().add(new CustomRequirement(
+ "Get a §cSnow Minion 1 §7from opening gifts"));
+
+ }
+
+ private void loadNpcData() {
+ TreeMap<String, JsonObject> itemInformation = NotEnoughUpdates.INSTANCE.manager.getItemInformation();
+ for (Map.Entry<String, JsonObject> entry : itemInformation.entrySet()) {
+ String internalName = entry.getKey();
+ if (!internalName.endsWith("_NPC")) continue;
+ JsonObject jsonObject = entry.getValue();
+ if (!jsonObject.has("recipes")) continue;
+
+ if (!jsonObject.has("displayname")) continue;
+ String npcName = jsonObject.get("displayname").getAsString();
+ npcName = StringUtils.cleanColour(npcName);
+ if (npcName.contains(" (")) {
+ npcName = npcName.split(" \\(")[0];
+ }
+
+ for (JsonElement element : jsonObject.get("recipes").getAsJsonArray()) {
+ JsonObject object = element.getAsJsonObject();
+ if (!object.has("type")) continue;
+ if (!object.get("type").getAsString().equals("npc_shop")) continue;
+ if (!object.has("result")) continue;
+
+ String result = object.get("result").getAsString();
+ if (!result.contains("_GENERATOR_")) continue;
+ Minion minion = manager.getMinionById(result);
+ if (!object.has("cost")) continue;
+
+ RecipeBuilder builder = new RecipeBuilder(manager);
+
+ for (JsonElement costEntry : object.get("cost").getAsJsonArray()) {
+ String price = costEntry.getAsString();
+ builder.addLine(minion, price);
+ }
+
+ ArrayListMultimap<String, Integer> map = builder.getItems();
+ int coins = 0;
+ if (map.containsKey("SKYBLOCK_COIN")) {
+ coins = map.get("SKYBLOCK_COIN").get(0);
+ map.removeAll("SKYBLOCK_COIN");
+ }
+
+ minion.setMinionSource(new NpcSource(npcName, coins, builder.getItems()));
+ minion.setParent(builder.getParent());
+ }
+ }
+ }
+
+ private void createMinions() {
+ for (Map.Entry<String, JsonElement> entry : Constants.MISC.get("minions").getAsJsonObject().entrySet()) {
+ String internalName = entry.getKey();
+ int maxTier = entry.getValue().getAsInt();
+ for (int i = 0; i < maxTier; i++) {
+ int tier = i + 1;
+ manager.createMinion(internalName + "_" + tier, tier);
+ }
+ }
+ }
+
+ private void testForMissingData() {
+ for (Minion minion : manager.getAllMinions().values()) {
+ if (minion.getMinionSource() == null) {
+ errorWhileLoading = true;
+ if (NotEnoughUpdates.INSTANCE.config.hidden.dev) {
+ Utils.addChatMessage("§c[NEU] The Minion '" + minion.getInternalName() + " has no source!");
+ }
+ }
+ if (minion.getDisplayName() == null) {
+ errorWhileLoading = true;
+ if (NotEnoughUpdates.INSTANCE.config.hidden.dev) {
+ Utils.addChatMessage("§c[NEU] The Minion '" + minion.getInternalName() + " has no display name!");
+ }
+ }
+ if (manager.getRequirementsManager().getRequirements(minion).isEmpty()) {
+ errorWhileLoading = true;
+ if (NotEnoughUpdates.INSTANCE.config.hidden.dev) {
+ Utils.addChatMessage("§c[NEU] The Minion '" + minion.getInternalName() + " has no requirements!");
+ }
+ }
+ if (minion.getTier() > 1 && minion.getParent() == null) {
+ errorWhileLoading = true;
+ if (NotEnoughUpdates.INSTANCE.config.hidden.dev) {
+ Utils.addChatMessage("§c[NEU] The Minion '" + minion.getInternalName() + " has parent!");
+ }
+ }
+ }
+ }
+
+ public void setDirty() {
+ dirty = true;
+ readyToUse = false;
+ }
+
+ public boolean isReadyToUse() {
+ return readyToUse;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/MinionHelperRepoMinionLoader.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/MinionHelperRepoMinionLoader.java
new file mode 100644
index 00000000..6abc5c56
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/MinionHelperRepoMinionLoader.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders.repo;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.CollectionRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.ReputationRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.SlayerRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.CraftingSource;
+import io.github.moulberry.notenoughupdates.util.Utils;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+public class MinionHelperRepoMinionLoader {
+
+ private final MinionHelperRepoLoader repoLoader;
+ private final MinionHelperManager manager;
+
+ public MinionHelperRepoMinionLoader(MinionHelperRepoLoader repoLoader, MinionHelperManager manager) {
+ this.repoLoader = repoLoader;
+ this.manager = manager;
+ }
+
+ void loadMinionData() {
+ TreeMap<String, JsonObject> itemInformation = NotEnoughUpdates.INSTANCE.manager.getItemInformation();
+
+ for (Map.Entry<String, Minion> entry : manager.getAllMinions().entrySet()) {
+ String internalName = entry.getKey();
+ if (!itemInformation.containsKey(internalName)) continue;
+ Minion minion = entry.getValue();
+
+ JsonObject jsonObject = itemInformation.get(internalName);
+ if (jsonObject.has("displayname")) {
+ String displayName = jsonObject.get("displayname").getAsString();
+ displayName = StringUtils.cleanColour(displayName);
+ displayName = StringUtils.removeLastWord(displayName, " ");
+ minion.setDisplayName(displayName);
+ }
+
+ if (jsonObject.has("recipe")) {
+ loadRecipes(minion, jsonObject);
+ }
+
+ loadRequirements(minion, jsonObject);
+ }
+ }
+
+ private void loadRequirements(Minion minion, JsonObject jsonObject) {
+ for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
+ String name = entry.getKey();
+ if (name.endsWith("_req") || name.equals("crafttext")) {
+ String value = entry.getValue().getAsString();
+
+ try {
+ switch (name) {
+ case "reputation_req": {
+ String[] split = value.split(":");
+ String reputationType = split[0];
+ int reputation = Integer.parseInt(split[1]);
+ minion.getRequirements().add(new ReputationRequirement(reputationType, reputation));
+ break;
+ }
+ case "crafttext": {
+ if (minion.getTier() != 1) break;
+ if (value.isEmpty()) break;
+
+ String rawCollection = value.split(Pattern.quote(": "))[1];
+ String cleanCollection = StringUtils.removeLastWord(rawCollection, " ");
+ String rawTier = rawCollection.substring(cleanCollection.length() + 1);
+ int tier = Utils.parseRomanNumeral(rawTier);
+ minion.getRequirements().add(new CollectionRequirement(cleanCollection, tier));
+ break;
+ }
+ case "slayer_req": {
+ String[] split = value.split("_");
+ String slayerType = split[0].toLowerCase();
+ int tier = Integer.parseInt(split[1]);
+ minion.getRequirements().add(new SlayerRequirement(slayerType, tier));
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
+ repoLoader.errorWhileLoading = true;
+ if (NotEnoughUpdates.INSTANCE.config.hidden.dev) {
+ Utils.addChatMessage(
+ "§c[NEU] Error in MinionHelperRepoLoader while loading repo entry " + minion.getDisplayName() + " " +
+ minion.getTier() + ": " +
+ e.getClass().getSimpleName() + ": " + e.getMessage());
+ }
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void loadRecipes(Minion minion, JsonObject jsonObject) {
+ JsonObject recipes = jsonObject.get("recipe").getAsJsonObject();
+ RecipeBuilder builder = new RecipeBuilder(manager);
+ for (Map.Entry<String, JsonElement> entry : recipes.entrySet()) {
+ String rawString = entry.getValue().getAsString();
+
+ builder.addLine(minion, rawString);
+ }
+
+ minion.setMinionSource(new CraftingSource(builder.getItems()));
+ minion.setParent(builder.getParent());
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/RecipeBuilder.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/RecipeBuilder.java
new file mode 100644
index 00000000..e7738954
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/repo/RecipeBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.loaders.repo;
+
+import com.google.common.collect.ArrayListMultimap;
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.util.Utils;
+
+public class RecipeBuilder {
+ private final MinionHelperManager manager;
+ private Minion parent = null;
+ private final ArrayListMultimap<String, Integer> items = ArrayListMultimap.create();
+
+ public RecipeBuilder(MinionHelperManager manager) {
+ this.manager = manager;
+ }
+
+ public Minion getParent() {
+ return parent;
+ }
+
+ public ArrayListMultimap<String, Integer> getItems() {
+ return items;
+ }
+
+ public void addLine(Minion minion, String rawString) {
+ String[] split = rawString.split(":");
+ String itemName = split[0];
+
+ boolean isParent = false;
+ if (itemName.contains("_GENERATOR_")) {
+ String minionInternalName = minion.getInternalName();
+ boolean same = StringUtils.removeLastWord(itemName, "_").equals(StringUtils.removeLastWord(
+ minionInternalName,
+ "_"
+ ));
+ if (same) {
+ Minion recipeMinion = manager.getMinionById(itemName);
+ if (recipeMinion.getTier() == minion.getTier() - 1) {
+ parent = recipeMinion;
+ if (parent == null) {
+ if (NotEnoughUpdates.INSTANCE.config.hidden.dev) {
+ Utils.addChatMessage("Parent is null for minion " + minionInternalName);
+ }
+ }
+ isParent = true;
+ }
+ }
+ }
+ if (!isParent) {
+ int amount = Integer.parseInt(split[1]);
+ items.put(itemName, amount);
+ }
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperOverlay.java
new file mode 100644
index 00000000..38f1c9e1
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperOverlay.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.render;
+
+import com.google.common.collect.Lists;
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.core.util.ArrowPagesUtils;
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.miscgui.TrophyRewardOverlay;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.renderables.OverviewLine;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.renderables.OverviewText;
+import io.github.moulberry.notenoughupdates.mixins.AccessorGuiContainer;
+import io.github.moulberry.notenoughupdates.util.NotificationHandler;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.gui.inventory.GuiChest;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.client.event.GuiOpenEvent;
+import net.minecraftforge.client.event.GuiScreenEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class MinionHelperOverlay {
+
+ private final ResourceLocation minionOverlayImage = new ResourceLocation("notenoughupdates:minion_overlay.png");
+ private final ResourceLocation greenCheckImage = new ResourceLocation("notenoughupdates:dungeon_map/green_check.png");
+ private final ResourceLocation whiteCheckImage = new ResourceLocation("notenoughupdates:dungeon_map/white_check.png");
+
+ private final MinionHelperManager manager;
+ private final MinionHelperOverlayHover hover;
+ private int[] topLeft = new int[]{237, 110};
+
+ private LinkedHashMap<String, OverviewLine> cacheRenderMap = null;
+ private int cacheTotalPages = -1;
+
+ private boolean filterEnabled = true;
+
+ private int maxPerPage = 8;
+ private int currentPage = 0;
+
+ public MinionHelperOverlay(MinionHelperManager manager) {
+ this.manager = manager;
+ hover = new MinionHelperOverlayHover(this, manager);
+ }
+
+ @SubscribeEvent
+ public void onGuiOpen(GuiOpenEvent event) {
+ resetCache();
+ }
+
+ public void resetCache() {
+ cacheRenderMap = null;
+ cacheTotalPages = -1;
+ }
+
+ @SubscribeEvent
+ public void onDrawBackground(GuiScreenEvent.BackgroundDrawnEvent event) {
+ if (!manager.inCraftedMinionsInventory()) return;
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return;
+ if (manager.isInvalidApiKey()) {
+ LinkedHashMap<String, OverviewLine> map = new LinkedHashMap<>();
+ map.put("§cInvalid API Key!", new OverviewText(Collections.emptyList(), () -> {}));
+ render(map);
+ return;
+ }
+ if (manager.notReady()) {
+ LinkedHashMap<String, OverviewLine> map = new LinkedHashMap<>();
+ map.put("§cLoading...", new OverviewText(Collections.emptyList(), () -> {}));
+ render(map);
+ return;
+ }
+
+ if (manager.getApi().isNotifyNoCollectionApi()) {
+ NotificationHandler.displayNotification(Lists.newArrayList(
+ "",
+ "§cCollection API is disabled!",
+ "§cMinion Helper will not filter minions that",
+ "§cdo not meet the collection requirements!"
+ ), false, true);
+ manager.getApi().setNotifyNoCollectionApi(false);
+ }
+
+ LinkedHashMap<String, OverviewLine> renderMap = getRenderMap();
+
+ hover.renderHover(renderMap);
+ render(renderMap);
+
+ renderArrows();
+ }
+
+ private void renderArrows() {
+ GuiScreen gui = Minecraft.getMinecraft().currentScreen;
+ if (gui instanceof AccessorGuiContainer) {
+ AccessorGuiContainer container = (AccessorGuiContainer) gui;
+ int guiLeft = container.getGuiLeft();
+ int guiTop = container.getGuiTop();
+ int totalPages = getTotalPages();
+ ArrowPagesUtils.onDraw(guiLeft, guiTop, topLeft, currentPage, totalPages);
+ }
+ }
+
+ @SubscribeEvent
+ public void onMouseClick(GuiScreenEvent.MouseInputEvent.Pre event) {
+ if (!manager.inCraftedMinionsInventory()) return;
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return;
+ if (manager.notReady()) return;
+ if (!Mouse.getEventButtonState()) return;
+
+ OverviewLine overviewLine = getObjectOverMouse(getRenderMap());
+ if (overviewLine != null) {
+ overviewLine.onClick();
+ event.setCanceled(true);
+ }
+
+ int totalPages = getTotalPages();
+ if (event.gui instanceof AccessorGuiContainer) {
+ int guiLeft = ((AccessorGuiContainer) event.gui).getGuiLeft();
+ int guiTop = ((AccessorGuiContainer) event.gui).getGuiTop();
+ if (ArrowPagesUtils.onPageSwitchMouse(guiLeft, guiTop, topLeft, currentPage, totalPages, pageChange -> {
+ currentPage = pageChange;
+ resetCache();
+ })) {
+ event.setCanceled(true);
+ }
+ }
+ checkToggleClick();
+ }
+
+ private void checkToggleClick() {
+ GuiScreen gui = Minecraft.getMinecraft().currentScreen;
+ if (!(gui instanceof GuiChest)) return;
+
+ int xSize = ((AccessorGuiContainer) gui).getXSize();
+ int guiLeft = ((AccessorGuiContainer) gui).getGuiLeft();
+ int guiTop = ((AccessorGuiContainer) gui).getGuiTop();
+
+ int x = guiLeft + xSize + 4 + 149 - 3;
+ int y = guiTop + 109 - 3;
+
+ final ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ final int scaledWidth = scaledresolution.getScaledWidth();
+ final int scaledHeight = scaledresolution.getScaledHeight();
+ int mouseX = Mouse.getX() * scaledWidth / Minecraft.getMinecraft().displayWidth;
+ int mouseY = scaledHeight - Mouse.getY() * scaledHeight / Minecraft.getMinecraft().displayHeight - 1;
+
+ if (mouseX > x && mouseX < x + 16 &&
+ mouseY > y && mouseY < y + 16) {
+ toggleShowAvailable();
+ }
+ }
+
+ @SubscribeEvent
+ public void onMouseClick(GuiScreenEvent.KeyboardInputEvent.Pre event) {
+ if (!manager.inCraftedMinionsInventory()) return;
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return;
+ if (manager.notReady()) return;
+
+ int totalPages = getTotalPages();
+ if (ArrowPagesUtils.onPageSwitchKey(currentPage, totalPages, pageChange -> {
+ currentPage = pageChange;
+ resetCache();
+ })) {
+ event.setCanceled(true);
+ }
+ }
+
+ private Map<Minion, Double> getMissing() {
+ Map<Minion, Double> prices = new HashMap<>();
+ for (Minion minion : manager.getAllMinions().values()) {
+
+ if (!minion.doesMeetRequirements() && filterEnabled) continue;
+ if (!minion.isCrafted()) {
+ double price = manager.getPriceCalculation().calculateUpgradeCosts(minion, true);
+ prices.put(minion, price);
+ }
+ }
+ return prices;
+ }
+
+ private void render(Map<String, OverviewLine> renderMap) {
+ Minecraft minecraft = Minecraft.getMinecraft();
+ Gui gui = Minecraft.getMinecraft().currentScreen;
+ if (!(gui instanceof GuiChest)) return;
+ int xSize = ((AccessorGuiContainer) gui).getXSize();
+ int guiLeft = ((AccessorGuiContainer) gui).getGuiLeft();
+ int guiTop = ((AccessorGuiContainer) gui).getGuiTop();
+ minecraft.getTextureManager().bindTexture(minionOverlayImage);
+ GL11.glColor4f(1, 1, 1, 1);
+ GlStateManager.disableLighting();
+ Utils.drawTexturedRect(guiLeft + xSize + 4, guiTop, 168, 128, 0, 1f, 0, 1f, GL11.GL_NEAREST);
+ if (filterEnabled) {
+ minecraft.getTextureManager().bindTexture(greenCheckImage);
+ } else {
+ minecraft.getTextureManager().bindTexture(whiteCheckImage);
+ }
+ Utils.drawTexturedRect(guiLeft + xSize + 4 + 149, guiTop + 109, 10, 10, 0, 1f, 0, 1f, GL11.GL_NEAREST);
+
+ int x = guiLeft + xSize + 10;
+ int i = 0;
+ int y = guiTop + 6;
+ FontRenderer fontRendererObj = minecraft.fontRendererObj;
+ for (Map.Entry<String, OverviewLine> entry : renderMap.entrySet()) {
+ String line = entry.getKey();
+
+ if (line.contains("§6")) {
+ String[] split = line.split("§6");
+ line = split[0];
+ String price = "§6§l" + split[1];
+ int lineLen = Minecraft.getMinecraft().fontRendererObj.getStringWidth(line);
+ fontRendererObj.drawString(price, x + lineLen, y, -1, true);
+ }
+
+ fontRendererObj.drawString(line, x, y, -1, false);
+ i++;
+ if (i == 2) {
+ y += 15;
+ } else {
+ y += 10;
+ }
+ }
+ }
+
+ private LinkedHashMap<String, OverviewLine> getRenderMap() {
+ if (cacheRenderMap != null) return cacheRenderMap;
+
+ Map<Minion, Double> prices = getMissing();
+ LinkedHashMap<String, OverviewLine> renderMap = new LinkedHashMap<>();
+
+ addTitle(prices, renderMap);
+ addNeedToNextSlot(prices, renderMap);
+
+ if (!prices.isEmpty()) {
+ addMinions(prices, renderMap);
+ }
+
+ cacheRenderMap = renderMap;
+ return renderMap;
+ }
+
+ private void addNeedToNextSlot(
+ Map<Minion, Double> prices,
+ LinkedHashMap<String, OverviewLine> renderMap
+ ) {
+ int neededForNextSlot = manager.getNeedForNextSlot();
+ String color = "§8";
+ if (neededForNextSlot == -1) {
+ renderMap.put(color + "Next slot: ?", new OverviewText(Collections.emptyList(), () -> {}));
+ return;
+ }
+
+ double priceNeeded = 0;
+ int index = 0;
+ for (Double price : TrophyRewardOverlay.sortByValue(prices).values()) {
+ priceNeeded += price;
+ index++;
+ if (index == neededForNextSlot) break;
+ }
+ String format = manager.getPriceCalculation().formatCoins(priceNeeded);
+ format = format.replace(" coins", "");
+ String text = color + "Next slot: §3" + neededForNextSlot + " minions §8- " + format;
+ renderMap.put(text, new OverviewText(Collections.emptyList(), () -> {}));
+ }
+
+ private void addTitle(Map<Minion, Double> prices, LinkedHashMap<String, OverviewLine> renderMap) {
+ String name = "§8" + prices.size() + " " + (filterEnabled ? "craftable" : "total") + " minions";
+ renderMap.put(name, new OverviewText(Collections.emptyList(), () -> {}));
+ }
+
+ private void addMinions(Map<Minion, Double> prices, LinkedHashMap<String, OverviewLine> renderMap) {
+ int skipPreviousPages = currentPage * maxPerPage;
+ int i = 0;
+ Map<Minion, Double> sort = TrophyRewardOverlay.sortByValue(prices);
+ for (Minion minion : sort.keySet()) {
+ if (i >= skipPreviousPages) {
+ String displayName = minion.getDisplayName();
+ if (displayName == null) {
+ if (NotEnoughUpdates.INSTANCE.config.hidden.dev) {
+ Utils.addChatMessage("§cDisplayname is null for " + minion.getInternalName());
+ }
+ continue;
+ }
+
+ displayName = displayName.replace(" Minion", "");
+ String format = manager.getPriceCalculation().calculateUpgradeCostsFormat(minion, true);
+ format = format.replace(" coins", "");
+ String requirementFormat = minion.doesMeetRequirements() ? "§9" : "§c";
+ renderMap.put(
+ requirementFormat + displayName + " " + minion.getTier() + " §8- " + format,
+ minion
+ );
+ }
+
+ i++;
+ if (i == ((currentPage + 1) * maxPerPage)) break;
+ }
+ }
+
+ private int getTotalPages() {
+ if (cacheTotalPages != -1) return cacheTotalPages;
+
+ Map<Minion, Double> prices = getMissing();
+ int totalPages = (int) ((double) prices.size() / maxPerPage);
+ if (prices.size() % maxPerPage != 0) {
+ totalPages++;
+ }
+
+ cacheTotalPages = totalPages;
+ return totalPages;
+ }
+
+ private void toggleShowAvailable() {
+ filterEnabled = !filterEnabled;
+ currentPage = 0;
+ resetCache();
+ }
+
+ OverviewLine getObjectOverMouse(LinkedHashMap<String, OverviewLine> renderMap) {
+ GuiScreen gui = Minecraft.getMinecraft().currentScreen;
+ if (!(gui instanceof GuiChest)) return null;
+
+ int xSize = ((AccessorGuiContainer) gui).getXSize();
+ int guiLeft = ((AccessorGuiContainer) gui).getGuiLeft();
+ int guiTop = ((AccessorGuiContainer) gui).getGuiTop();
+
+ int x = guiLeft + xSize + 9;
+ int y = guiTop + 5;
+
+ final ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ final int scaledWidth = scaledresolution.getScaledWidth();
+ final int scaledHeight = scaledresolution.getScaledHeight();
+ int mouseX = Mouse.getX() * scaledWidth / Minecraft.getMinecraft().displayWidth;
+ int mouseY = scaledHeight - Mouse.getY() * scaledHeight / Minecraft.getMinecraft().displayHeight - 1;
+
+ int i = 0;
+ FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
+ for (Map.Entry<String, OverviewLine> entry : renderMap.entrySet()) {
+ String text = entry.getKey();
+ int width = fontRenderer.getStringWidth(StringUtils.cleanColour(text));
+ if (mouseX > x && mouseX < x + width + 4 &&
+ mouseY > y && mouseY < y + 11) {
+ return entry.getValue();
+ }
+ i++;
+ if (i == 2) {
+ y += 15;
+ } else {
+ y += 10;
+ }
+ }
+
+ return null;
+ }
+
+ public void onProfileSwitch() {
+ currentPage = 0;
+ filterEnabled = true;
+ }
+
+ public void setMaxPerPage(int maxPerPage) {
+ this.maxPerPage = maxPerPage;
+ }
+
+ public void setTopLeft(int[] topLeft) {
+ this.topLeft = topLeft;
+ }
+
+ public boolean isFilterEnabled() {
+ return filterEnabled;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperOverlayHover.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperOverlayHover.java
new file mode 100644
index 00000000..ce0757a6
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperOverlayHover.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.render;
+
+import com.google.common.collect.ArrayListMultimap;
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.ApiData;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.renderables.OverviewLine;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.renderables.OverviewText;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.CollectionRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.MinionRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.ReputationRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.CraftingSource;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.MinionSource;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.NpcSource;
+import io.github.moulberry.notenoughupdates.mixins.AccessorGuiContainer;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.gui.inventory.GuiChest;
+import org.lwjgl.input.Mouse;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MinionHelperOverlayHover {
+
+ private final MinionHelperOverlay overlay;
+ private final MinionHelperManager manager;
+
+ public MinionHelperOverlayHover(MinionHelperOverlay overlay, MinionHelperManager manager) {
+ this.overlay = overlay;
+ this.manager = manager;
+ }
+
+ void renderHover(LinkedHashMap<String, OverviewLine> renderMap) {
+ if (!(Minecraft.getMinecraft().currentScreen instanceof GuiChest)) return;
+
+ final ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ final int scaledWidth = scaledresolution.getScaledWidth();
+ final int scaledHeight = scaledresolution.getScaledHeight();
+ int mouseX = Mouse.getX() * scaledWidth / Minecraft.getMinecraft().displayWidth;
+ int mouseY = scaledHeight - Mouse.getY() * scaledHeight / Minecraft.getMinecraft().displayHeight - 1;
+
+ OverviewLine mouseObject = overlay.getObjectOverMouse(renderMap);
+ if (mouseObject != null) {
+ Utils.drawHoveringText(getTooltip(mouseObject), mouseX, mouseY,
+ scaledWidth, scaledHeight, -1, Minecraft.getMinecraft().fontRendererObj
+ );
+ }
+
+ renderToggleButton();
+ }
+
+ private void renderToggleButton() {
+ GuiScreen gui = Minecraft.getMinecraft().currentScreen;
+ if (!(gui instanceof GuiChest)) return;
+
+ int xSize = ((AccessorGuiContainer) gui).getXSize();
+ int guiLeft = ((AccessorGuiContainer) gui).getGuiLeft();
+ int guiTop = ((AccessorGuiContainer) gui).getGuiTop();
+
+ int x = guiLeft + xSize + 4 + 149 - 3;
+ int y = guiTop + 109 - 3;
+
+ final ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft());
+ final int scaledWidth = scaledresolution.getScaledWidth();
+ final int scaledHeight = scaledresolution.getScaledHeight();
+ int mouseX = Mouse.getX() * scaledWidth / Minecraft.getMinecraft().displayWidth;
+ int mouseY = scaledHeight - Mouse.getY() * scaledHeight / Minecraft.getMinecraft().displayHeight - 1;
+
+ List<String> list = new ArrayList<>();
+
+ if (overlay.isFilterEnabled()) {
+ list.add("§aFilter enabled!");
+ list.add("§7Only show minions that can be");
+ list.add("§7crafted and meet requirements.");
+ } else {
+ list.add("§cFilter disabled!");
+ list.add("§7Show all minions. §cRed ones §7have");
+ list.add("§7missing requirements.");
+ }
+
+ list.add("");
+ list.add("§eClick to toggle!");
+
+ if (mouseX > x && mouseX < x + 16 &&
+ mouseY > y && mouseY < y + 16) {
+
+ Utils.drawHoveringText(list, mouseX, mouseY, scaledWidth, scaledHeight, -1,
+ Minecraft.getMinecraft().fontRendererObj);
+ }
+ }
+
+ private List<String> getTooltip(OverviewLine overviewLine) {
+ List<String> lines = new ArrayList<>();
+
+ if (overviewLine instanceof OverviewText) {
+ OverviewText overviewText = (OverviewText) overviewLine;
+ lines.addAll(overviewText.getLines());
+ } else if (overviewLine instanceof Minion) {
+
+ Minion minion = (Minion) overviewLine;
+ MinionSource minionSource = minion.getMinionSource();
+ if (minion.getCustomSource() != null) {
+ minionSource = minion.getCustomSource();
+ }
+ String displayName = minion.getDisplayName();
+ lines.add("§9" + displayName + " " + minion.getTier());
+ List<MinionRequirement> requirements = manager.getRequirementsManager().getRequirements(minion);
+ if (!requirements.isEmpty()) {
+ for (MinionRequirement requirement : requirements) {
+ String result = getRequirementDescription(minion, requirement);
+ if (result == null) continue;
+ lines.add(result);
+ }
+ } else {
+ lines.add("§cNo requirements loaded!");
+ }
+
+ if (minionSource instanceof CraftingSource) {
+ CraftingSource craftingSource = (CraftingSource) minionSource;
+ lines.add("");
+ String format = manager.getPriceCalculation().calculateUpgradeCostsFormat(minion, true);
+ if (minion.getTier() == 1) {
+ lines.add("§7Full crafting cost: " + format);
+ } else {
+ lines.add("§7Upgrade cost: " + format);
+ }
+ formatItems(lines, grabAllItems(craftingSource.getItems()));
+
+ } else if (minionSource instanceof NpcSource) {
+ NpcSource npcSource = (NpcSource) minionSource;
+ String npcName = npcSource.getNpcName();
+ lines.add("");
+ lines.add("§7Buy from: §9" + npcName + " (NPC)");
+ lines.add("");
+ lines.add("§7Cost: " + manager.getPriceCalculation().calculateUpgradeCostsFormat(minion, true));
+ int coins = npcSource.getCoins();
+ if (coins != 0) {
+ lines.add(" §8- " + manager.getPriceCalculation().formatCoins(coins));
+ }
+ formatItems(lines, grabAllItems(npcSource.getItems()));
+ }
+
+ lines.add("");
+ lines.add("§eClick to view recipe!");
+ }
+ return lines;
+ }
+
+ private String getRequirementDescription(Minion minion, MinionRequirement requirement) {
+ boolean meetRequirement = manager.getRequirementsManager().meetRequirement(minion, requirement);
+ String color = meetRequirement ? "§a" : "§c";
+
+ String description = requirement.printDescription(color);
+ if (requirement instanceof CollectionRequirement && manager.getApi().isCollectionApiDisabled()) {
+ description += " §cAPI DISABLED! §7";
+ }
+
+ if (!meetRequirement) {
+ if (requirement instanceof ReputationRequirement) {
+ ReputationRequirement reputationRequirement = (ReputationRequirement) requirement;
+ String reputationType = reputationRequirement.getReputationType();
+ ApiData apiData = manager.getApi().getApiData();
+ int having;
+ if (reputationType.equals("BARBARIAN")) {
+ having = apiData.getBarbariansReputation();
+ } else if (reputationType.equals("MAGE")) {
+ having = apiData.getMagesReputation();
+ } else {
+ Utils.addChatMessage("§c[NEU] Minion Helper: Unknown reputation type: '" + reputationType + "'");
+ return null;
+ }
+ int need = reputationRequirement.getReputation();
+ if (having < 0) having = 0;
+
+ String reputationName = StringUtils.firstUpperLetter(reputationType.toLowerCase());
+ String havingFormat = Utils.formatNumberWithDots(having);
+ String needFormat = Utils.formatNumberWithDots(need);
+ description = "Reputation: §c" + havingFormat + "§8/§c" + needFormat + " §7" + reputationName + " Reputation";
+ }
+ }
+
+ return " §8- §7" + description;
+ }
+
+ private void formatItems(List<String> lines, Map<String, Integer> allItems) {
+ for (Map.Entry<String, Integer> entry : allItems.entrySet()) {
+ String internalName = entry.getKey();
+ int amount = entry.getValue();
+ if (internalName.equals("SKYBLOCK_PELT")) {
+ int peltCount = manager.getApi().getApiData().getPeltCount();
+
+ lines.add(" §8- §5" + peltCount + "§8/§5" + amount + " Pelts");
+ continue;
+ }
+
+ String name = NotEnoughUpdates.INSTANCE.manager.getDisplayName(internalName);
+ double price = manager.getPriceCalculation().getPrice(internalName);
+ String priceFormat = manager.getPriceCalculation().formatCoins(price * amount);
+ lines.add(" §8- §a" + amount + "§7x §f" + name + " " + priceFormat);
+ }
+ }
+
+ private Map<String, Integer> grabAllItems(ArrayListMultimap<String, Integer> multimap) {
+ Map<String, Integer> allItems = new HashMap<>();
+ for (Map.Entry<String, Integer> entry : multimap.entries()) {
+ String name = entry.getKey();
+ int amount = entry.getValue();
+ amount = allItems.getOrDefault(name, 0) + amount;
+ allItems.put(name, amount);
+ }
+ return allItems;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperTooltips.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperTooltips.java
new file mode 100644
index 00000000..ef243a6f
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/MinionHelperTooltips.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.render;
+
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.MinionSource;
+import io.github.moulberry.notenoughupdates.util.ItemUtils;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.event.entity.player.ItemTooltipEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import org.lwjgl.input.Keyboard;
+
+import java.util.List;
+
+public class MinionHelperTooltips {
+ private final MinionHelperManager manager;
+ private boolean pressedShiftLast = false;
+ private boolean showFullCost = false;
+
+ public MinionHelperTooltips(MinionHelperManager manager) {
+ this.manager = manager;
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOW)
+ public void onItemTooltip(ItemTooltipEvent event) {
+ if (!manager.inCraftedMinionsInventory()) return;
+ if (!NotEnoughUpdates.INSTANCE.config.minionHelper.tooltip) return;
+ if (manager.notReady()) return;
+
+ boolean shift = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
+ if (!pressedShiftLast && shift) {
+ showFullCost = !showFullCost;
+ }
+ pressedShiftLast = shift;
+
+ ItemStack itemStack = event.itemStack;
+ if (itemStack == null) return;
+ String displayName = itemStack.getDisplayName();
+ if (!displayName.endsWith(" Minion")) return;
+ displayName = StringUtils.cleanColour(displayName);
+
+ List<String> lore = ItemUtils.getLore(itemStack);
+ if (lore.get(0).equals("§7You haven't crafted this minion.")) return;
+
+ int index = 0;
+ for (String line : lore) {
+ index++;
+ if (!line.contains("Tier")) continue;
+
+ Minion minion = manager.getMinionByName(displayName, index);
+ if (minion == null) {
+ System.err.println("minion is null for displayName '" + displayName + "' and tier " + index);
+ continue;
+ }
+ MinionSource minionSource = minion.getMinionSource();
+ if (minionSource == null) {
+ System.err.println("minionSource is null for " + minion.getInternalName());
+ continue;
+ }
+ String format = manager.getPriceCalculation().calculateUpgradeCostsFormat(minion, !showFullCost);
+ event.toolTip.set(index, line + " §8- " + format);
+ }
+
+ if (showFullCost) {
+ event.toolTip.add("§8[Press SHIFT to show upgrade cost]");
+ } else {
+ event.toolTip.add("§8[Press SHIFT to show full cost]");
+ }
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/renderables/OverviewLine.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/renderables/OverviewLine.java
new file mode 100644
index 00000000..85d682b3
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/renderables/OverviewLine.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.renderables;
+
+public abstract class OverviewLine {
+
+ public abstract void onClick();
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/renderables/OverviewText.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/renderables/OverviewText.java
new file mode 100644
index 00000000..499ca758
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/render/renderables/OverviewText.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.render.renderables;
+
+import java.util.List;
+
+public class OverviewText extends OverviewLine {
+
+ private final Runnable clickRunnable;
+ private final List<String> lines;
+
+ public OverviewText(List<String> line, Runnable clickRunnable) {
+ this.lines = line;
+ this.clickRunnable = clickRunnable;
+ }
+
+ public List<String> getLines() {
+ return lines;
+ }
+
+ @Override
+ public void onClick() {
+ clickRunnable.run();
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/CollectionRequirement.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/CollectionRequirement.java
new file mode 100644
index 00000000..c01386e1
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/CollectionRequirement.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements;
+
+public class CollectionRequirement extends MinionRequirement {
+
+ private final String collection;
+ private final int level;
+
+ public CollectionRequirement(String collection, int level) {
+ this.collection = collection;
+ this.level = level;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public String getCollection() {
+ return collection;
+ }
+
+ @Override
+ public String printDescription(String color) {
+ return "Collection: " + color + collection + " level " + level;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/CustomRequirement.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/CustomRequirement.java
new file mode 100644
index 00000000..93174f03
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/CustomRequirement.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements;
+
+public class CustomRequirement extends MinionRequirement {
+
+ private final String text;
+
+ public CustomRequirement(String text) {
+ this.text = text;
+ }
+ @Override
+ public String printDescription(String color) {
+ return text;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/MinionRequirement.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/MinionRequirement.java
new file mode 100644
index 00000000..9dd3fe09
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/MinionRequirement.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements;
+
+public abstract class MinionRequirement {
+ public abstract String printDescription(String color);
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/ReputationRequirement.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/ReputationRequirement.java
new file mode 100644
index 00000000..70d1e5fc
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/ReputationRequirement.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements;
+
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+import io.github.moulberry.notenoughupdates.util.Utils;
+
+public class ReputationRequirement extends MinionRequirement {
+
+ private final String reputationType;
+ private final int reputation;
+ private final String description;
+
+ public ReputationRequirement(String reputationType, int reputation) {
+ this.reputationType = reputationType;
+ this.reputation = reputation;
+
+ String reputationName = StringUtils.firstUpperLetter(reputationType.toLowerCase());
+ description = Utils.formatNumberWithDots(reputation) + " §7" + reputationName + " Reputation";
+ }
+
+ public int getReputation() {
+ return reputation;
+ }
+
+ public String getReputationType() {
+ return reputationType;
+ }
+
+ @Override
+ public String printDescription(String color) {
+ return "Reputation: " + color + description;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/SlayerRequirement.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/SlayerRequirement.java
new file mode 100644
index 00000000..78c588b1
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/requirements/SlayerRequirement.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements;
+
+import io.github.moulberry.notenoughupdates.core.util.StringUtils;
+
+public class SlayerRequirement extends MinionRequirement {
+
+ private final String slayer;
+ private final int level;
+
+ public SlayerRequirement(String slayer, int level) {
+ this.slayer = slayer;
+ this.level = level;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public String getSlayer() {
+ return slayer;
+ }
+
+ @Override
+ public String printDescription(String color) {
+ return "Slayer: " + color +StringUtils.firstUpperLetter(slayer)+ " level " + level;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/CraftingSource.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/CraftingSource.java
new file mode 100644
index 00000000..eca689d1
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/CraftingSource.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources;
+
+import com.google.common.collect.ArrayListMultimap;
+
+public class CraftingSource extends MinionSource {
+ //name -> amount
+ private final ArrayListMultimap<String, Integer> items;
+
+ public CraftingSource(ArrayListMultimap<String, Integer> items) {
+ this.items = items;
+ }
+
+ public ArrayListMultimap<String, Integer> getItems() {
+ return items;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/CustomSource.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/CustomSource.java
new file mode 100644
index 00000000..cf260a88
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/CustomSource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources;
+
+public class CustomSource extends MinionSource {
+
+ private final String sourceName;
+
+ public CustomSource(String sourceName) {
+ this.sourceName = sourceName;
+ }
+
+ public String getSourceName() {
+ return sourceName;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/MinionSource.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/MinionSource.java
new file mode 100644
index 00000000..f54f0845
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/MinionSource.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources;
+
+public abstract class MinionSource {
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/NpcSource.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/NpcSource.java
new file mode 100644
index 00000000..941890fa
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/sources/NpcSource.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources;
+
+import com.google.common.collect.ArrayListMultimap;
+
+public class NpcSource extends MinionSource {
+ private final String npcName;
+ // name -> amount
+ private final ArrayListMultimap<String, Integer> items;
+ private final int coins;
+
+ public NpcSource(String npcName, int coins, ArrayListMultimap<String, Integer> items) {
+ this.npcName = npcName;
+ this.coins = coins;
+ this.items = items;
+ }
+
+ public String getNpcName() {
+ return npcName;
+ }
+
+ public ArrayListMultimap<String, Integer> getItems() {
+ return items;
+ }
+
+ public int getCoins() {
+ return coins;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/util/MinionHelperPriceCalculation.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/util/MinionHelperPriceCalculation.java
new file mode 100644
index 00000000..495aeaac
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/util/MinionHelperPriceCalculation.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.util;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.gson.JsonObject;
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.CraftingSource;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.MinionSource;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.sources.NpcSource;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraftforge.client.event.GuiOpenEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MinionHelperPriceCalculation {
+
+ private final MinionHelperManager manager;
+ private final Map<String, String> upgradeCostFormatCache = new HashMap<>();
+ private final Map<String, String> fullCostFormatCache = new HashMap<>();
+
+ public MinionHelperPriceCalculation(MinionHelperManager manager) {
+ this.manager = manager;
+ }
+
+ @SubscribeEvent
+ public void onGuiOpen(GuiOpenEvent event) {
+ upgradeCostFormatCache.clear();
+ fullCostFormatCache.clear();
+ }
+
+ public String calculateUpgradeCostsFormat(Minion minion, boolean upgradeOnly) {
+ MinionSource source = minion.getMinionSource();
+
+ if (source == null) return "§c?";
+ String internalName = minion.getInternalName();
+ if (upgradeOnly) {
+ if (upgradeCostFormatCache.containsKey(internalName)) {
+ return upgradeCostFormatCache.get(internalName);
+ }
+ } else {
+ if (fullCostFormatCache.containsKey(internalName)) {
+ return fullCostFormatCache.get(internalName);
+ }
+ }
+
+ if (upgradeOnly) {
+ if (minion.getCustomSource() != null) {
+ return (minion.getCustomSource()).getSourceName();
+ }
+ }
+
+ double costs = calculateUpgradeCosts(minion, upgradeOnly);
+ String result = formatCoins(costs, !upgradeOnly ? "§o" : "");
+
+ if (source instanceof NpcSource) {
+ ArrayListMultimap<String, Integer> items = ((NpcSource) source).getItems();
+ if (items.containsKey("SKYBLOCK_PELT")) {
+ int amount = items.get("SKYBLOCK_PELT").get(0);
+ result += " §7+ §5" + amount + " Pelts";
+ }
+ }
+
+ if (upgradeOnly) {
+ upgradeCostFormatCache.put(internalName, result);
+ } else {
+ fullCostFormatCache.put(internalName, result);
+ }
+
+ return result;
+ }
+
+ public double calculateUpgradeCosts(Minion minion, boolean upgradeOnly) {
+ MinionSource source = minion.getMinionSource();
+
+ if (upgradeOnly) {
+ if (minion.getCustomSource() != null) {
+ return 0;
+ }
+ }
+
+ if (source instanceof CraftingSource) {
+ CraftingSource craftingSource = (CraftingSource) source;
+ return getCosts(minion, upgradeOnly, craftingSource.getItems());
+
+ } else if (source instanceof NpcSource) {
+ NpcSource npcSource = (NpcSource) source;
+ double upgradeCost = getCosts(minion, upgradeOnly, npcSource.getItems());
+ long coins = npcSource.getCoins();
+ upgradeCost += coins;
+
+ return upgradeCost;
+ }
+
+ return 0;
+ }
+
+ private double getCosts(Minion minion, boolean upgradeOnly, ArrayListMultimap<String, Integer> items) {
+ double upgradeCost = 0;
+ for (Map.Entry<String, Integer> entry : items.entries()) {
+ String internalName = entry.getKey();
+ if (internalName.equals("SKYBLOCK_PELT")) continue;
+ double price = getPrice(internalName);
+ int amount = entry.getValue();
+ upgradeCost += price * amount;
+ }
+ if (!upgradeOnly) {
+ Minion parent = minion.getParent();
+ if (parent != null) {
+ upgradeCost += calculateUpgradeCosts(parent, false);
+ }
+ }
+ return upgradeCost;
+ }
+
+ public double getPrice(String internalName) {
+ //Is minion
+ if (internalName.contains("_GENERATOR_")) {
+ return calculateUpgradeCosts(manager.getMinionById(internalName), false);
+ }
+
+ //Is bazaar item
+ JsonObject bazaarInfo = NotEnoughUpdates.INSTANCE.manager.auctionManager.getBazaarInfo(internalName);
+ if (bazaarInfo != null) {
+ if (!bazaarInfo.has("curr_sell")) {
+ System.err.println("curr_sell does not exist for '" + internalName + "'");
+ return 0;
+ }
+ return bazaarInfo.get("curr_sell").getAsDouble();
+ }
+
+ //is ah bin
+ double avgBinPrice = NotEnoughUpdates.INSTANCE.manager.auctionManager.getItemAvgBin(internalName);
+ if (avgBinPrice >= 1) return avgBinPrice;
+
+ //is ah without bin
+ JsonObject auctionInfo = NotEnoughUpdates.INSTANCE.manager.auctionManager.getItemAuctionInfo(internalName);
+ if (auctionInfo == null) {
+ //only wood axe and similar useless items
+ return 1;
+ }
+ return (auctionInfo.get("price").getAsFloat() / auctionInfo.get("count").getAsFloat());
+ }
+
+ public String formatCoins(double coins) {
+ return formatCoins(coins, "");
+ }
+
+ public String formatCoins(double coins, String extraFormat) {
+ int i = coins < 3 ? 1 : 0;
+ String format = Utils.shortNumberFormat(coins, i);
+ return "§6" + extraFormat + format + " coins";
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/util/MinionHelperRequirementsManager.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/util/MinionHelperRequirementsManager.java
new file mode 100644
index 00000000..4a1b96b5
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/util/MinionHelperRequirementsManager.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.miscgui.minionhelper.util;
+
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.ApiData;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.Minion;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.CollectionRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.CustomRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.MinionRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.ReputationRequirement;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.requirements.SlayerRequirement;
+import io.github.moulberry.notenoughupdates.util.Utils;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class MinionHelperRequirementsManager {
+
+ private final MinionHelperManager manager;
+
+ public MinionHelperRequirementsManager(MinionHelperManager manager) {
+ this.manager = manager;
+ }
+
+ public List<MinionRequirement> getRequirements(Minion minion) {
+ if (!minion.getRequirements().isEmpty()) {
+ return minion.getRequirements();
+ }
+
+ Minion parent = minion.getParent();
+ if (parent != null) {
+ return getRequirements(parent);
+ }
+
+ return Collections.emptyList();
+ }
+
+ public boolean meetAllRequirements(Minion minion) {
+ List<MinionRequirement> list = getRequirements(minion);
+ for (MinionRequirement requirement : list) {
+ if (!meetRequirement(minion, requirement)) {
+ return false;
+ }
+ }
+
+ Minion parent = minion.getParent();
+ if (parent != null) {
+ return meetAllRequirements(parent);
+ }
+
+ return true;
+ }
+
+ public boolean meetRequirement(Minion minion, MinionRequirement requirement) {
+ ApiData apiData = manager.getApi().getApiData();
+ if (apiData == null) return false;
+
+ if (requirement instanceof CollectionRequirement) {
+ if (apiData.isCollectionApiDisabled()) return true;
+
+ CollectionRequirement collectionRequirement = (CollectionRequirement) requirement;
+ String collection = collectionRequirement.getCollection();
+ String internalName = manager.formatInternalName(collection);
+
+ int need = collectionRequirement.getLevel();
+ Map<String, Integer> highestCollectionTier = apiData.getHighestCollectionTier();
+ if (highestCollectionTier.containsKey(internalName)) {
+ int has = highestCollectionTier.get(internalName);
+
+ return has >= need;
+ }
+
+ } else if (requirement instanceof SlayerRequirement) {
+ SlayerRequirement slayerRequirement = (SlayerRequirement) requirement;
+ String slayer = slayerRequirement.getSlayer();
+ //Because the neu-repo uses 'eman' and the hypixel api is using 'enderman'
+ if (slayer.equals("eman")) {
+ slayer = "enderman";
+ }
+ int need = slayerRequirement.getLevel();
+ Map<String, Integer> slayerTiers = apiData.getSlayerTiers();
+ if (slayerTiers.containsKey(slayer)) {
+ return slayerTiers.get(slayer) >= need;
+ }
+
+ } else if (requirement instanceof ReputationRequirement) {
+ ReputationRequirement reputationRequirement = (ReputationRequirement) requirement;
+ int need = reputationRequirement.getReputation();
+ String reputationType = reputationRequirement.getReputationType();
+ if (reputationType.equals("BARBARIAN")) {
+ return apiData.getBarbariansReputation() >= need;
+ } else if (reputationType.equals("MAGE")) {
+ return apiData.getMagesReputation() >= need;
+ } else {
+ Utils.addChatMessage("§c[NEU] Minion Helper: Unknown reputation type: '" + reputationType + "'");
+ return false;
+ }
+ } else if (requirement instanceof CustomRequirement) {
+ return minion.doesMeetRequirements();
+ }
+
+ return false;
+ }
+
+ public void reloadRequirements() {
+ for (Minion minion : manager.getAllMinions().values()) {
+ minion.setMeetRequirements(meetAllRequirements(minion));
+ }
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
index 4d04bfbc..808de00b 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
@@ -50,6 +50,7 @@ import io.github.moulberry.notenoughupdates.options.seperateSections.ItemOverlay
import io.github.moulberry.notenoughupdates.options.seperateSections.Itemlist;
import io.github.moulberry.notenoughupdates.options.seperateSections.LocationEdit;
import io.github.moulberry.notenoughupdates.options.seperateSections.Mining;
+import io.github.moulberry.notenoughupdates.options.seperateSections.MinionHelper;
import io.github.moulberry.notenoughupdates.options.seperateSections.Misc;
import io.github.moulberry.notenoughupdates.options.seperateSections.MiscOverlays;
import io.github.moulberry.notenoughupdates.options.seperateSections.NeuAuctionHouse;
@@ -400,6 +401,13 @@ public class NEUConfig extends Config {
@Expose
@Category(
+ name = "Minion Helper",
+ desc = "Minion Helper"
+ )
+ public MinionHelper minionHelper = new MinionHelper();
+
+ @Expose
+ @Category(
name = "Apis",
desc = "Api Data"
)
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MinionHelper.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MinionHelper.java
new file mode 100644
index 00000000..089b2ae7
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/MinionHelper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 NotEnoughUpdates contributors
+ *
+ * This file is part of NotEnoughUpdates.
+ *
+ * NotEnoughUpdates is free software: you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * NotEnoughUpdates is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package io.github.moulberry.notenoughupdates.options.seperateSections;
+
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigEditorBoolean;
+import io.github.moulberry.notenoughupdates.core.config.annotations.ConfigOption;
+
+//TODO jani rename message format
+public class MinionHelper {
+ @Expose
+ @ConfigOption(
+ name = "Enable gui",
+ desc =
+ "Shows a list in the crafted minions inventory of every available to craft or all missing minion, in multiple pages " +
+ "that you need to get the next minion slot, sorted by upgrade cost"
+ )
+
+ @ConfigEditorBoolean
+ public boolean gui = true;
+ @Expose
+ @ConfigOption(
+ name = "Enable tooltip",
+ desc = "Shows the price per minion at the crafted minions "
+ )
+ @ConfigEditorBoolean
+ public boolean tooltip = true;
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java
index 58adb307..4b31d022 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java
@@ -24,6 +24,7 @@ import com.google.gson.JsonObject;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.listener.ScoreboardLocationChangeListener;
import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent;
+import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager;
import io.github.moulberry.notenoughupdates.overlays.SlayerOverlay;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiChest;
@@ -440,7 +441,8 @@ public class SBInfo {
public void setCurrentProfile(String newProfile) {
if (!newProfile.equals(currentProfile)) {
- currentProfile = newProfile; // TODO @hannibal2: maybe event
+ currentProfile = newProfile;
+ MinionHelperManager.getInstance().onProfileSwitch();
}
}
}