aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorKevin <92656833+kevinthegreat1@users.noreply.github.com>2023-09-12 21:25:50 -0400
committerGitHub <noreply@github.com>2023-09-12 21:25:50 -0400
commit99c6a8a6f1f09feee4a8fb1cae5af40ec0217c7e (patch)
treea709bbf6f5081b870b6b69eaf4a6448e16c22fc0 /src/main
parent331caf4f35e9471456d0c7e1c2c8353a1ba0b5c3 (diff)
parente4757b16aca9f67621a5beaad3a55aeb15401d9c (diff)
downloadSkyblocker-99c6a8a6f1f09feee4a8fb1cae5af40ec0217c7e.tar.gz
Skyblocker-99c6a8a6f1f09feee4a8fb1cae5af40ec0217c7e.tar.bz2
Skyblocker-99c6a8a6f1f09feee4a8fb1cae5af40ec0217c7e.zip
Merge pull request #294 from AzureAaron/dungeon-chest-profit-calc
Dungeon Chest Profit Calculator!
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java6
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java11
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java7
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java14
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java168
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java12
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json7
-rw-r--r--src/main/resources/skyblocker.mixins.json1
8 files changed, 220 insertions, 6 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
index 0147938c..b630e5f5 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
@@ -4,10 +4,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.skyblock.*;
-import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonBlaze;
-import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap;
-import me.xmrvizzy.skyblocker.skyblock.dungeon.LividColor;
-import me.xmrvizzy.skyblocker.skyblock.dungeon.TicTacToe;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.*;
import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets;
import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud;
import me.xmrvizzy.skyblocker.skyblock.item.*;
@@ -89,6 +86,7 @@ public class SkyblockerMod implements ClientModInitializer {
DungeonMap.init();
DungeonSecrets.init();
DungeonBlaze.init();
+ DungeonChestProfit.init();
TheRift.init();
TitleContainer.init();
ScreenMaster.init();
diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
index 92d31660..ca3b221a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
@@ -437,6 +437,8 @@ public class SkyblockerConfig implements ConfigData {
public static class Dungeons {
@ConfigEntry.Gui.CollapsibleObject
public SecretWaypoints secretWaypoints = new SecretWaypoints();
+ @ConfigEntry.Gui.CollapsibleObject
+ public DungeonChestProfit dungeonChestProfit = new DungeonChestProfit();
@ConfigEntry.Gui.Tooltip()
public boolean croesusHelper = true;
public boolean enableMap = true;
@@ -474,6 +476,15 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Gui.Tooltip()
public boolean enableDefaultWaypoints = true;
}
+
+ public static class DungeonChestProfit {
+ @ConfigEntry.Gui.Tooltip
+ public boolean enableProfitCalculator = true;
+ @ConfigEntry.Gui.Tooltip
+ public boolean includeKismet = false;
+ @ConfigEntry.Gui.Tooltip
+ public boolean includeEssence = true;
+ }
public static class LividColor {
@ConfigEntry.Gui.Tooltip()
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
index af6f6aa7..eccd63e6 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
@@ -3,6 +3,7 @@ package me.xmrvizzy.skyblocker.mixin;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.skyblock.BackpackPreview;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonChestProfit;
import me.xmrvizzy.skyblocker.skyblock.experiment.ChronomatronSolver;
import me.xmrvizzy.skyblocker.skyblock.experiment.ExperimentSolver;
import me.xmrvizzy.skyblocker.skyblock.experiment.SuperpairsSolver;
@@ -16,10 +17,13 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.inventory.SimpleInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
+import net.minecraft.screen.GenericContainerScreenHandler;
+import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
+import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -30,6 +34,9 @@ import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+
import java.util.Map;
@Mixin(HandledScreen.class)
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java
new file mode 100644
index 00000000..6a671601
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java
@@ -0,0 +1,14 @@
+package me.xmrvizzy.skyblocker.mixin.accessor;
+
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+@Mixin(Screen.class)
+public interface ScreenAccessor {
+ @Accessor
+ @Mutable
+ void setTitle(Text title);
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java
new file mode 100644
index 00000000..beed0cd8
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java
@@ -0,0 +1,168 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import com.google.gson.JsonObject;
+import it.unimi.dsi.fastutil.ints.IntBooleanPair;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.mixin.accessor.ScreenAccessor;
+import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.client.item.TooltipContext;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.GenericContainerScreenHandler;
+import net.minecraft.screen.ScreenHandlerType;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Style;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.DecimalFormat;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DungeonChestProfit {
+ private static final Logger LOGGER = LoggerFactory.getLogger(DungeonChestProfit.class);
+ private static final Pattern ESSENCE_PATTERN = Pattern.compile("(?<type>[A-Za-z]+) Essence x(?<amount>[0-9]+)");
+ private static final DecimalFormat FORMATTER = new DecimalFormat("#,###");
+ private static final Style GRAY = Style.EMPTY.withColor(Formatting.GRAY);
+ private static final Style DARK_BLUE = Style.EMPTY.withColor(Formatting.DARK_BLUE);
+ private static final Style DARK_GREEN = Style.EMPTY.withColor(Formatting.DARK_GREEN);
+ private static final Style DARK_RED = Style.EMPTY.withColor(Formatting.DARK_RED);
+
+ public static void init() {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> ScreenEvents.afterTick(screen).register(screen1 -> {
+ if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getScreenHandler().getType() == ScreenHandlerType.GENERIC_9X6) {
+ ((ScreenAccessor) screen).setTitle(getChestProfit(genericContainerScreen.getScreenHandler(), screen.getTitle(), client));
+ }
+ }));
+ }
+
+ public static Text getChestProfit(GenericContainerScreenHandler handler, Text title, MinecraftClient client) {
+ try {
+ if (SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator && isDungeonChest(title.getString())) {
+ int profit = 0;
+ boolean hasIncompleteData = false, usedKismet = false;
+ List<Slot> slots = handler.slots.subList(0, handler.getRows() * 9);
+
+ //If the item stack for the "Open Reward Chest" button or the kismet button hasn't been sent to the client yet
+ if (slots.get(31).getStack().isEmpty() || slots.get(50).getStack().isEmpty()) return title;
+
+ for (Slot slot : slots) {
+ ItemStack stack = slot.getStack();
+
+ if (!stack.isEmpty()) {
+ String name = stack.getName().getString();
+ String id = PriceInfoTooltip.getInternalNameFromNBT(stack, false);
+
+ //Regular item price
+ if (id != null) {
+ IntBooleanPair priceData = getItemPrice(id);
+
+ if (!priceData.rightBoolean()) hasIncompleteData = true;
+
+ //Add the item price to the profit
+ profit += priceData.leftInt();
+
+ continue;
+ }
+
+ //Essence price
+ if (name.contains("Essence") && SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.includeEssence) {
+ Matcher matcher = ESSENCE_PATTERN.matcher(name);
+
+ if (matcher.matches()) {
+ String type = matcher.group("type");
+ int amount = Integer.parseInt(matcher.group("amount"));
+
+ IntBooleanPair priceData = getItemPrice(("ESSENCE_" + type).toUpperCase());
+
+ if (!priceData.rightBoolean()) hasIncompleteData = true;
+
+ //Add the price of the essence to the profit
+ profit += priceData.leftInt() * amount;
+
+ continue;
+ }
+ }
+
+ //Determine the cost of the chest
+ if (name.contains("Open Reward Chest")) {
+ String foundString = searchLoreFor(stack, client, "Coins");
+
+ //Incase we're searching the free chest
+ if (!StringUtils.isBlank(foundString)) {
+ profit -= Integer.parseInt(foundString.replaceAll("[^0-9]", ""));
+ }
+
+ continue;
+ }
+
+ //Determine if a kismet was used or not
+ if (name.contains("Reroll Chest")) {
+ usedKismet = !StringUtils.isBlank(searchLoreFor(stack, client, "You already rerolled a chest!"));
+ }
+ }
+ }
+
+ if (SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.includeKismet && usedKismet) {
+ IntBooleanPair kismetPriceData = getItemPrice("KISMET_FEATHER");
+
+ if (!kismetPriceData.rightBoolean()) hasIncompleteData = true;
+
+ profit -= kismetPriceData.leftInt();
+ }
+
+ return Text.literal(title.getString()).append(getProfitText(profit, hasIncompleteData));
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Profit Calculator] Failed to calculate dungeon chest profit! ", e);
+ }
+
+ return title;
+ }
+
+ /**
+ * @return An {@link IntBooleanPair} with the {@code left int} representing the item's price, and the {@code right boolean} indicating if the price
+ * was based on complete data.
+ */
+ private static IntBooleanPair getItemPrice(String id) {
+ JsonObject bazaarPrices = PriceInfoTooltip.getBazaarPrices();
+ JsonObject lbinPrices = PriceInfoTooltip.getLBINPrices();
+
+ if (bazaarPrices == null || lbinPrices == null) return IntBooleanPair.of(0, false);
+
+ if (bazaarPrices.has(id)) {
+ JsonObject item = bazaarPrices.get(id).getAsJsonObject();
+ boolean isPriceNull = item.get("sellPrice").isJsonNull();
+
+ return IntBooleanPair.of(isPriceNull ? 0 : (int) item.get("sellPrice").getAsDouble(), !isPriceNull);
+ }
+
+ if (lbinPrices.has(id)) {
+ return IntBooleanPair.of((int) lbinPrices.get(id).getAsDouble(), true);
+ }
+
+ return IntBooleanPair.of(0, false);
+ }
+
+ /**
+ * Searches for a specific string of characters in the name and lore of an item
+ */
+ private static String searchLoreFor(ItemStack stack, MinecraftClient client, String searchString) {
+ return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).filter(line -> line.contains(searchString)).findAny().orElse(null);
+ }
+
+ private static boolean isDungeonChest(String name) {
+ return name.equals("Wood Chest") || name.equals("Gold Chest") || name.equals("Diamond Chest") || name.equals("Emerald Chest") || name.equals("Obsidian Chest") || name.equals("Bedrock Chest");
+ }
+
+ private static Text getProfitText(int profit, boolean hasIncompleteData) {
+ return (profit == 0) ? Text.literal(" " + FORMATTER.format(profit)).setStyle(hasIncompleteData ? DARK_BLUE : GRAY) : (profit > 0) ? Text.literal(" +" + FORMATTER.format(profit)).setStyle(hasIncompleteData ? DARK_BLUE : DARK_GREEN) : Text.literal(" " + FORMATTER.format(profit)).setStyle(hasIncompleteData ? DARK_BLUE : DARK_RED);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
index b2dc900e..d20aa0e3 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
@@ -375,10 +375,10 @@ public class PriceInfoTooltip {
futureList.add(CompletableFuture.runAsync(() -> threeDayAvgPricesJson = downloadPrices("3 day avg")));
}
}
- if (SkyblockerConfig.get().general.itemTooltip.enableLowestBIN)
+ if (SkyblockerConfig.get().general.itemTooltip.enableLowestBIN || SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator)
futureList.add(CompletableFuture.runAsync(() -> lowestPricesJson = downloadPrices("lowest bins")));
- if (SkyblockerConfig.get().general.itemTooltip.enableBazaarPrice)
+ if (SkyblockerConfig.get().general.itemTooltip.enableBazaarPrice || SkyblockerConfig.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator)
futureList.add(CompletableFuture.runAsync(() -> bazaarPricesJson = downloadPrices("bazaar")));
if (SkyblockerConfig.get().general.itemTooltip.enableNPCPrice && npcPricesJson == null)
@@ -419,6 +419,14 @@ public class PriceInfoTooltip {
return null;
}
}
+
+ public static JsonObject getBazaarPrices() {
+ return bazaarPricesJson;
+ }
+
+ public static JsonObject getLBINPrices() {
+ return lowestPricesJson;
+ }
static {
apiAddresses = new HashMap<>();
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index 30af6493..ffcb4e81 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -221,6 +221,13 @@
"text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableStonkWaypoints" : "Enable Stonk Waypoints",
"text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableDefaultWaypoints" : "Enable Default Waypoints",
"text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableDefaultWaypoints.@Tooltip" : "This includes all waypoints that do not belong to a category.",
+ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit": "Dungeon Chest Profit Calculator",
+ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator": "Enable Profit Calculator",
+ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator.@Tooltip": "Displays the profit of a dungeon chest in the chest screen's title.\nGreen if there's profit.\nRed if there isn't profit.\nGray if you don't gain or lose anything.\nBlue if calculations were based on incomplete data.",
+ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet": "Include Kismet Price",
+ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet.@Tooltip": "When enabled, if you used a kismet the price of one will be subtracted from the profit",
+ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence": "Include Essence",
+ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence.@Tooltip": "Disabling is **not recommended** if you're a forgetful person.",
"text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Croesus Helper",
"text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "Gray out chests that have already been opened.",
"text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Enable Map",
diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json
index 98759aac..9d2ce7c2 100644
--- a/src/main/resources/skyblocker.mixins.json
+++ b/src/main/resources/skyblocker.mixins.json
@@ -28,6 +28,7 @@
"accessor.HandledScreenAccessor",
"accessor.PlayerListHudAccessor",
"accessor.RecipeBookWidgetAccessor",
+ "accessor.ScreenAccessor",
"accessor.WorldRendererAccessor"
],
"injectors": {