aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/hysky')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java31
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/ChestValue.java230
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java170
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/HotbarSlotLock.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/HotbarSlotLock.java)2
10 files changed, 282 insertions, 177 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index cbe82667..ac19542a 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -98,7 +98,7 @@ public class SkyblockerMod implements ClientModInitializer {
DungeonMap.init();
DungeonSecrets.init();
DungeonBlaze.init();
- DungeonChestProfit.init();
+ ChestValue.init();
TheRift.init();
TitleContainer.init();
ScreenMaster.init();
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index 1c6bb394..3c8e6739 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -207,6 +207,9 @@ public class SkyblockerConfig {
public WikiLookup wikiLookup = new WikiLookup();
@SerialEntry
+ public ChestValue chestValue = new ChestValue();
+
+ @SerialEntry
public SpecialEffects specialEffects = new SpecialEffects();
@SerialEntry
@@ -547,6 +550,17 @@ public class SkyblockerConfig {
public boolean officialWiki = false;
}
+ public static class ChestValue {
+ @SerialEntry
+ public boolean enableChestValue = true;
+
+ @SerialEntry
+ public Formatting color = Formatting.DARK_GREEN;
+
+ @SerialEntry
+ public Formatting incompleteColor = Formatting.BLUE;
+ }
+
public static class SpecialEffects {
@SerialEntry
public boolean rareDungeonDropEffects = true;
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
index 7bb6414c..37f268b0 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
@@ -204,8 +204,8 @@ public class DungeonsCategory {
.controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING))
.build())
.option(Option.<Formatting>createBuilder()
- .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor"))
- .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor.@Tooltip")))
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue.incompleteColor"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue.incompleteColor.@Tooltip")))
.binding(defaults.locations.dungeons.dungeonChestProfit.incompleteColor,
() -> config.locations.dungeons.dungeonChestProfit.incompleteColor,
newValue -> config.locations.dungeons.dungeonChestProfit.incompleteColor = newValue)
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
index be5f0665..8d8c6f46 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
@@ -2,6 +2,7 @@ package de.hysky.skyblocker.config.categories;
import de.hysky.skyblocker.config.ConfigUtils;
import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.config.controllers.EnumDropdownControllerBuilder;
import de.hysky.skyblocker.skyblock.shortcut.ShortcutsConfigScreen;
import de.hysky.skyblocker.utils.render.title.TitleContainerConfigScreen;
import de.hysky.skyblocker.utils.waypoint.Waypoint;
@@ -11,6 +12,7 @@ import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder;
import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
public class GeneralCategory {
@@ -455,6 +457,35 @@ public class GeneralCategory {
.build())
.build())
+ //Chest Value
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue"))
+ .collapsed(true)
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue.enableChestValue"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue.enableChestValue.@Tooltip")))
+ .binding(defaults.general.chestValue.enableChestValue,
+ () -> config.general.chestValue.enableChestValue,
+ newValue -> config.general.chestValue.enableChestValue = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Formatting>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue.color"))
+ .binding(defaults.general.chestValue.color,
+ () -> config.general.chestValue.color,
+ newValue -> config.general.chestValue.color = newValue)
+ .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING))
+ .build())
+ .option(Option.<Formatting>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue.incompleteColor"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue.incompleteColor.@Tooltip")))
+ .binding(defaults.general.chestValue.incompleteColor,
+ () -> config.general.chestValue.incompleteColor,
+ newValue -> config.general.chestValue.incompleteColor = newValue)
+ .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING))
+ .build())
+ .build())
+
//Special Effects
.group(OptionGroup.createBuilder()
.name(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects"))
diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
index 37ae92e8..6813d654 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
@@ -3,7 +3,7 @@ package de.hysky.skyblocker.mixin;
import com.mojang.authlib.GameProfile;
import dev.cbyrne.betterinject.annotations.Inject;
-import de.hysky.skyblocker.skyblock.HotbarSlotLock;
+import de.hysky.skyblocker.skyblock.item.HotbarSlotLock;
import de.hysky.skyblocker.skyblock.item.ItemProtection;
import de.hysky.skyblocker.skyblock.rift.HealingMelonIndicator;
import de.hysky.skyblocker.utils.Utils;
diff --git a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java
index 1b6d62d4..0ee7b528 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java
@@ -5,7 +5,7 @@ import com.llamalad7.mixinextras.sugar.Local;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.skyblock.FancyStatusBars;
-import de.hysky.skyblocker.skyblock.HotbarSlotLock;
+import de.hysky.skyblocker.skyblock.item.HotbarSlotLock;
import de.hysky.skyblocker.skyblock.item.ItemCooldowns;
import de.hysky.skyblocker.skyblock.dungeon.DungeonMap;
import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds;
diff --git a/src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java b/src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java
index 066490d5..29889c28 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java
@@ -1,6 +1,6 @@
package de.hysky.skyblocker.mixin;
-import de.hysky.skyblocker.skyblock.HotbarSlotLock;
+import de.hysky.skyblocker.skyblock.item.HotbarSlotLock;
import de.hysky.skyblocker.utils.Utils;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/ChestValue.java b/src/main/java/de/hysky/skyblocker/skyblock/ChestValue.java
new file mode 100644
index 00000000..8833a8d9
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/ChestValue.java
@@ -0,0 +1,230 @@
+package de.hysky.skyblocker.skyblock;
+
+import com.google.gson.JsonObject;
+import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.mixin.accessor.HandledScreenAccessor;
+import de.hysky.skyblocker.mixin.accessor.ScreenAccessor;
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import de.hysky.skyblocker.utils.Utils;
+import it.unimi.dsi.fastutil.ints.IntBooleanPair;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.fabricmc.fabric.api.client.screen.v1.Screens;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.client.gui.tooltip.Tooltip;
+import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.item.TooltipContext;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.GenericContainerScreenHandler;
+import net.minecraft.screen.slot.Slot;
+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.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ChestValue {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChestValue.class);
+ private static final Set<String> DUNGEON_CHESTS = Set.of("Wood Chest", "Gold Chest", "Diamond Chest", "Emerald Chest", "Obsidian Chest", "Bedrock Chest");
+ private static final Pattern ESSENCE_PATTERN = Pattern.compile("(?<type>[A-Za-z]+) Essence x(?<amount>[0-9]+)");
+ private static final DecimalFormat FORMATTER = new DecimalFormat("#,###");
+
+ public static void init() {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen) {
+ Text title = screen.getTitle();
+ String titleString = title.getString();
+ if (DUNGEON_CHESTS.contains(titleString)) {
+ if (SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator) {
+ ScreenEvents.afterTick(screen).register(screen_ ->
+ ((ScreenAccessor) screen).setTitle(getDungeonChestProfit(genericContainerScreen.getScreenHandler(), title, titleString, client))
+ );
+ }
+ } else if (SkyblockerConfigManager.get().general.chestValue.enableChestValue && !titleString.equals("SkyBlock Menu")) {
+ Screens.getButtons(screen).add(ButtonWidget
+ .builder(Text.literal("$"), buttonWidget -> {
+ Screens.getButtons(screen).remove(buttonWidget);
+ ScreenEvents.afterTick(screen).register(screen_ ->
+ ((ScreenAccessor) screen).setTitle(getChestValue(genericContainerScreen.getScreenHandler(), title, titleString))
+ );
+ })
+ .dimensions(((HandledScreenAccessor) genericContainerScreen).getX() + ((HandledScreenAccessor) genericContainerScreen).getBackgroundWidth() - 16, ((HandledScreenAccessor) genericContainerScreen).getY() + 4, 12, 12)
+ .tooltip(Tooltip.of(Text.translatable("text.autoconfig.skyblocker.option.general.chestValue.@Tooltip")))
+ .build()
+ );
+ }
+ }
+ });
+ }
+
+ private static Text getDungeonChestProfit(GenericContainerScreenHandler handler, Text title, String titleString, MinecraftClient client) {
+ try {
+ 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()) {
+ continue;
+ }
+
+ String name = stack.getName().getString();
+ String id = ItemTooltip.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() * stack.getCount();
+
+ continue;
+ }
+
+ //Essence price
+ if (name.contains("Essence") && SkyblockerConfigManager.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 (SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.includeKismet && usedKismet) {
+ IntBooleanPair kismetPriceData = getItemPrice("KISMET_FEATHER");
+
+ if (!kismetPriceData.rightBoolean()) hasIncompleteData = true;
+
+ profit -= kismetPriceData.leftInt();
+ }
+
+ return Text.literal(titleString).append(getProfitText(profit, hasIncompleteData));
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Profit Calculator] Failed to calculate dungeon chest profit! ", e);
+ }
+
+ return title;
+ }
+
+ private static Text getChestValue(GenericContainerScreenHandler handler, Text title, String titleString) {
+ try {
+ int value = 0;
+ boolean hasIncompleteData = false;
+ List<Slot> slots = handler.slots.subList(0, handler.getRows() * 9);
+
+ for (Slot slot : slots) {
+ ItemStack stack = slot.getStack();
+ if (stack.isEmpty()) {
+ continue;
+ }
+
+ String id = ItemTooltip.getInternalNameFromNBT(stack, false);
+
+ if (id != null) {
+ IntBooleanPair priceData = getItemPrice(id);
+
+ if (!priceData.rightBoolean()) hasIncompleteData = true;
+
+ value += priceData.leftInt() * stack.getCount();
+ }
+ }
+
+ return Text.literal(titleString).append(getValueText(value, hasIncompleteData));
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Value Calculator] Failed to calculate dungeon chest value! ", 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 = TooltipInfoType.BAZAAR.getData();
+ JsonObject lbinPrices = TooltipInfoType.LOWEST_BINS.getData();
+
+ 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 Text getProfitText(int profit, boolean hasIncompleteData) {
+ SkyblockerConfig.DungeonChestProfit config = SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit;
+ return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor);
+ }
+
+ static Text getProfitText(int profit, boolean hasIncompleteData, int neutralThreshold, Formatting neutralColor, Formatting profitColor, Formatting lossColor, Formatting incompleteColor) {
+ return Text.literal((profit > 0 ? " +" : ' ') + FORMATTER.format(profit) + " Coins").formatted(hasIncompleteData ? incompleteColor : (Math.abs(profit) < neutralThreshold) ? neutralColor : (profit > 0) ? profitColor : lossColor);
+ }
+
+ private static Text getValueText(int value, boolean hasIncompleteData) {
+ SkyblockerConfig.ChestValue config = SkyblockerConfigManager.get().general.chestValue;
+ return getValueText(value, hasIncompleteData, config.color, config.incompleteColor);
+ }
+
+ static Text getValueText(int value, boolean hasIncompleteData, Formatting color, Formatting incompleteColor) {
+ return Text.literal(' ' + FORMATTER.format(value) + " Coins").formatted(hasIncompleteData ? incompleteColor : color);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java
deleted file mode 100644
index 1e889af3..00000000
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java
+++ /dev/null
@@ -1,170 +0,0 @@
-package de.hysky.skyblocker.skyblock.dungeon;
-
-import com.google.gson.JsonObject;
-import de.hysky.skyblocker.config.SkyblockerConfig;
-import de.hysky.skyblocker.config.SkyblockerConfigManager;
-import de.hysky.skyblocker.mixin.accessor.ScreenAccessor;
-import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
-import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
-import de.hysky.skyblocker.utils.Utils;
-import it.unimi.dsi.fastutil.ints.IntBooleanPair;
-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.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("#,###");
-
- 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 (SkyblockerConfigManager.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 = ItemTooltip.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") && SkyblockerConfigManager.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 (SkyblockerConfigManager.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 = TooltipInfoType.BAZAAR.getData();
- JsonObject lbinPrices = TooltipInfoType.LOWEST_BINS.getData();
-
- 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) {
- SkyblockerConfig.DungeonChestProfit config = SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit;
- return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor);
- }
-
- static Text getProfitText(int profit, boolean hasIncompleteData, int neutralThreshold, Formatting neutralColor, Formatting profitColor, Formatting lossColor, Formatting incompleteColor) {
- return Text.literal((profit > 0 ? " +" : " ") + FORMATTER.format(profit)).formatted(hasIncompleteData ? incompleteColor : (Math.abs(profit) < neutralThreshold) ? neutralColor : (profit > 0) ? profitColor : lossColor);
- }
-}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HotbarSlotLock.java b/src/main/java/de/hysky/skyblocker/skyblock/item/HotbarSlotLock.java
index 13f09ec6..069a030d 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/HotbarSlotLock.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/HotbarSlotLock.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock;
+package de.hysky.skyblocker.skyblock.item;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;