diff options
6 files changed, 293 insertions, 2 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index 78458291..f8bd61d4 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -684,6 +684,9 @@ public class SkyblockerConfig { public boolean croesusHelper = true; @SerialEntry + public boolean croesusProfit = true; + + @SerialEntry public boolean enableMap = true; @SerialEntry 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 3ebd5d76..f22d9584 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -328,6 +328,14 @@ public class DungeonsCategory { .controller(ConfigUtils::createBooleanController) .build()) .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.croesusProfit")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.croesusProfit.@Tooltip"))) + .binding(defaults.locations.dungeons.croesusProfit, + () -> config.locations.dungeons.croesusProfit, + newValue -> config.locations.dungeons.croesusProfit = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.enableMap")) .binding(defaults.locations.dungeons.enableMap, () -> config.locations.dungeons.enableMap, diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java index e95b47c9..01422770 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java @@ -25,8 +25,12 @@ public class CroesusHelper extends ContainerSolver { List<ColorHighlight> highlights = new ArrayList<>(); for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) { ItemStack stack = entry.getValue(); - if (stack != null && stack.getNbt() != null && (stack.getNbt().toString().contains("No more Chests to open!") || stack.getNbt().toString().contains("Opened Chest:"))) { - highlights.add(ColorHighlight.gray(entry.getKey())); + if (stack != null && stack.getNbt() != null) { + if (stack.getNbt().toString().contains("Opened Chest:")) { + highlights.add(ColorHighlight.gray(entry.getKey())); + } else if (stack.getNbt().toString().contains("No more Chests to open!")) { + highlights.add(ColorHighlight.red(entry.getKey())); + } } } return highlights; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusProfit.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusProfit.java new file mode 100644 index 00000000..a2a82aee --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusProfit.java @@ -0,0 +1,272 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import com.google.gson.JsonObject; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; +import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import it.unimi.dsi.fastutil.longs.LongBooleanPair; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CroesusProfit extends ContainerSolver { + private static final Logger LOGGER = LogManager.getLogger(); + private static final Pattern ESSENCE_PATTERN = Pattern.compile("(?<type>[A-Za-z]+) Essence x(?<amount>[0-9]+)"); + private static final Pattern CHEST_PATTERN = Pattern.compile("literal\\{(.*?) Chest}"); + public CroesusProfit() { + super(".*Catacombs - Floor.*"); + } + + @Override + protected boolean isEnabled() { + return SkyblockerConfigManager.get().locations.dungeons.croesusProfit; + } + + @Override + protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) { + List<ColorHighlight> highlights = new ArrayList<>(); + ItemStack bestChest = null, secondBestChest = null; + long bestValue = 0, secondBestValue = 0; // If negative value of chest - it is out of the question + long dungeonKeyPriceData = getItemPrice("DUNGEON_CHEST_KEY")*2; // lesser ones don't worth the hassle + + for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) { + ItemStack stack = entry.getValue(); + if (stack != null && stack.getNbt() != null && stack.getName().toString().contains("Chest")) { + long value = valueChest(stack); + if (value > bestValue) { + secondBestChest = bestChest; + secondBestValue = bestValue; + bestChest = stack; + bestValue = value; + } else if (value > secondBestValue) { + secondBestChest = stack; + secondBestValue = value; + } + } + } + + for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) { + ItemStack stack = entry.getValue(); + if (stack != null && stack.getNbt() != null) { + if (stack.equals(bestChest)) { + highlights.add(ColorHighlight.green(entry.getKey())); + } else if (stack.equals(secondBestChest) && secondBestValue > dungeonKeyPriceData) { + highlights.add(ColorHighlight.yellow(entry.getKey())); + } + } + } + return highlights; + } + + + private long valueChest(@NotNull ItemStack chest) { + long chestValue = 0; + int chestPrice = 0; + List<String> chestItems = new ArrayList<>(); + + Matcher matcher = CHEST_PATTERN.matcher(chest.getName().toString()); + String chestType = (matcher.find() ? matcher.group(1) : "Error"); // we are doing it for sole reason of saving us headache of a spirit pet rarity + + boolean processingContents = false; + for (Text line : chest.getTooltip(null, TooltipContext.BASIC)) { + String lineString = line.getString(); + if (lineString.contains("Contents")) { + processingContents = true; + continue; + } else if (lineString.isEmpty()) { + processingContents = false; + } else if (lineString.contains("Coins") && !processingContents) { + chestPrice = Integer.parseInt(lineString.replaceAll(",", "").replaceAll("\\D", "")); + } + + if (processingContents) { + if (lineString.contains("Essence")) { + matcher = ESSENCE_PATTERN.matcher(lineString); + if (matcher.matches()) { // add to chest value result of multiplying price of essence on it's amount + chestValue += getItemPrice(("ESSENCE_" + matcher.group("type")).toUpperCase()) * Integer.parseInt(matcher.group("amount")); + } + } else { + if (lineString.contains("Spirit")) { // I hate pets in chests. Speaking of which, this is only part of code I'm really not sure of + if (Objects.equals(chestType, "Diamond")) { + chestValue += getItemPrice("Spirit Epic"); // Diamond chest containts epic spirit + } else { + chestValue += getItemPrice(lineString); + } + } else { + chestItems.add(lineString); + } + } + } + } + for (String item : chestItems){ + chestValue += getItemPrice(item); + } + return chestValue-chestPrice; + } + + private long getItemPrice(String itemDisplayName) { + JsonObject bazaarPrices = TooltipInfoType.BAZAAR.getData(); + JsonObject lbinPrices = TooltipInfoType.LOWEST_BINS.getData(); + long itemValue = 0; + String id = dungeonDropsNameToId.get(itemDisplayName); + + if (bazaarPrices == null || lbinPrices == null) return 0; + + if (bazaarPrices.has(id)) { + JsonObject item = bazaarPrices.get(id).getAsJsonObject(); + boolean isPriceNull = item.get("sellPrice").isJsonNull(); + return (isPriceNull ? 0L : item.get("sellPrice").getAsLong()); + } else if (lbinPrices.has(id)) { + return lbinPrices.get(id).getAsLong(); + } + return itemValue; + } + + private String getChestType(String name) { + Matcher matcher = CHEST_PATTERN.matcher(name); + if (matcher.find()) { // add to chest value result of multiplying price of essence on it's amount + return matcher.group(); + } + return name; + } + + // I did a thing :( + final Map<String, String> dungeonDropsNameToId = new HashMap<>() {{ + put("Enchanted Book (Ultimate Jerry I)", "ENCHANTMENT_ULTIMATE_JERRY_1"); // ultimate books start + put("Enchanted Book (Ultimate Jerry II)", "ENCHANTMENT_ULTIMATE_JERRY_2"); + put("Enchanted Book (Ultimate Jerry III)", "ENCHANTMENT_ULTIMATE_JERRY_3"); + put("Enchanted Book (Bank I)", "ENCHANTMENT_ULTIMATE_BANK_1"); + put("Enchanted Book (Bank II)", "ENCHANTMENT_ULTIMATE_BANK_2"); + put("Enchanted Book (Bank III)", "ENCHANTMENT_ULTIMATE_BANK_3"); + put("Enchanted Book (Combo I)", "ENCHANTMENT_ULTIMATE_COMBO_1"); + put("Enchanted Book (Combo II)", "ENCHANTMENT_ULTIMATE_COMBO_2"); + put("Enchanted Book (No Pain No Gain I)", "ENCHANTMENT_ULTIMATE_NO_PAIN_NO_GAIN_1"); + put("Enchanted Book (No Pain No Gain II)", "ENCHANTMENT_ULTIMATE_NO_PAIN_NO_GAIN_2"); + put("Enchanted Book (Ultimate Wise I)", "ENCHANTMENT_ULTIMATE_WISE_1"); + put("Enchanted Book (Ultimate Wise II)", "ENCHANTMENT_ULTIMATE_WISE_2"); + put("Enchanted Book (Wisdom I)", "ENCHANTMENT_ULTIMATE_WISDOM_1"); + put("Enchanted Book (Wisdom II)", "ENCHANTMENT_ULTIMATE_WISDOM_2"); + put("Enchanted Book (Last Stand I)", "ENCHANTMENT_ULTIMATE_LAST_STAND_1"); + put("Enchanted Book (Last Stand II)", "ENCHANTMENT_ULTIMATE_LAST_STAND_2"); + put("Enchanted Book (Rend I)", "ENCHANTMENT_ULTIMATE_REND_1"); + put("Enchanted Book (Rend II)", "ENCHANTMENT_ULTIMATE_REND_2"); + put("Enchanted Book (Legion I)", "ENCHANTMENT_ULTIMATE_LEGION_1"); + put("Enchanted Book (Swarm I)", "ENCHANTMENT_ULTIMATE_SWARM_1"); + put("Enchanted Book (One For All I)", "ENCHANTMENT_ULTIMATE_ONE_FOR_ALL_1"); + put("Enchanted Book (Soul Eater I)", "ENCHANTMENT_ULTIMATE_SOUL_EATER_1"); // ultimate books end + put("Enchanted Book (Infinite Quiver VI)", "ENCHANTMENT_INFINITE_QUIVER_6"); // enchanted books start + put("Enchanted Book (Infinite Quiver VII)", "ENCHANTMENT_INFINITE_QUIVER_7"); + put("Enchanted Book (Feather Falling VI)", "ENCHANTMENT_FEATHER_FALLING_6"); + put("Enchanted Book (Feather Falling VII)", "ENCHANTMENT_FEATHER_FALLING_7"); + put("Enchanted Book (Rejuvenate I)", "ENCHANTMENT_REJUVENATE_1"); + put("Enchanted Book (Rejuvenate II)", "ENCHANTMENT_REJUVENATE_2"); + put("Enchanted Book (Rejuvenate III)", "ENCHANTMENT_REJUVENATE_3"); + put("Enchanted Book (Overload)", "ENCHANTMENT_OVERLOAD_1"); + put("Enchanted Book (Lethality VI)", "ENCHANTMENT_LETHALITY_6"); + put("Enchanted Book (Thunderlord VII)", "ENCHANTMENT_THUNDERLORD_7"); // enchanted books end + + put("Hot Potato Book", "HOT_POTATO_BOOK"); // HPB, FPB, Recomb (universal drops) + put("Fuming Potato Book", "FUMING_POTATO_BOOK"); + put("Recombobulator 3000", "RECOMBOBULATOR_3000"); + put("Necromancer's Brooch", "NECROMANCER_BROOCH"); + put("ESSENCE_WITHER","ESSENCE_WITHER"); // Essences. Really stupid way of doing this + put("ESSENCE_UNDEAD", "ESSENCE_UNDEAD"); + put("ESSENCE_DRAGON", "ESSENCE_DRAGON"); + put("ESSENCE_SPIDER", "ESSENCE_SPIDER"); + put("ESSENCE_ICE", "ESSENCE_ICE"); + put("ESSENCE_DIAMOND", "ESSENCE_DIAMOND"); + put("ESSENCE_GOLD", "ESSENCE_GOLD"); + put("ESSENCE_CRIMSON", "ESSENCE_CRIMSON"); + put("DUNGEON_CHEST_KEY", "DUNGEON_CHEST_KEY"); + + put("Bonzo's Staff", "BONZO_STAFF"); // F1 M1 + put("Master Skull - Tier 1", "MASTER_SKULL_TIER_1"); + put("Bonzo's Mask", "BONZO_MASK"); + put("Balloon Snake", "BALLOON_SNAKE"); + put("Red Nose", "RED_NOSE"); + + put("Red Scarf", "RED_SCARF"); // F2 M2 + put("Adaptive Blade", "STONE_BLADE"); + put("Master Skull - Tier 2", "MASTER_SKULL_TIER_2"); + put("Adaptive Belt", "ADAPTIVE_BELT"); + put("Scarf's Studies", "SCARF_STUDIES"); + + put("First Master Star", "FIRST_MASTER_STAR"); // F3 M3 + put("Adaptive Helmet", "ADAPTIVE_HELMET"); + put("Adaptive Chestplate", "ADAPTIVE_CHESTPLATE"); + put("Adaptive Leggings", "ADAPTIVE_LEGGINGS"); + put("Adaptive Boots", "ADAPTIVE_BOOTS"); + put("Master Skull - Tier 3", "MASTER_SKULL_TIER_3"); + put("Suspicious Vial", "SUSPICIOUS_VIAL"); + + put("Spirit Sword", "SPIRIT_SWORD"); // F4 M4 + put("Spirit Shortbow", "ITEM_SPIRIT_BOW"); + put("Spirit Boots", "THORNS_BOOTS"); + put("Spirit", "SPIRIT;4"); // Spirit pet (Legendary) + put("Spirit Epic", "SPIRIT;3"); + + put("Second Master Star", "SECOND_MASTER_STAR"); + put("Spirit Wing", "SPIRIT_WING"); + put("Spirit Bone", "SPIRIT_BONE"); + put("Spirit Stone", "SPIRIT_DECOY"); + + put("Shadow Fury", "SHADOW_FURY"); // F5 M5 + put("Last Breath", "LAST_BREATH"); + put("Third Master Star", "THIRD_MASTER_STAR"); + put("Warped Stone", "AOTE_STONE"); + put("Livid Dagger", "LIVID_DAGGER"); + put("Shadow Assassin Helmet", "SHADOW_ASSASSIN_HELMET"); + put("Shadow Assassin Chestplate", "SHADOW_ASSASSIN_CHESTPLATE"); + put("Shadow Assassin Leggings", "SHADOW_ASSASSIN_LEGGINGS"); + put("Shadow Assassin Boots", "SHADOW_ASSASSIN_BOOTS"); + put("Shadow Assassin Cloak", "SHADOW_ASSASSIN_CLOAK"); + put("Master Skull - Tier 4", "MASTER_SKULL_TIER_4"); + put("Dark Orb", "DARK_ORB"); + + put("Precursor Eye", "PRECURSOR_EYE"); // F6 M6 + put("Giant's Sword", "GIANTS_SWORD"); + put("Necromancer Lord Helmet", "NECROMANCER_LORD_HELMET"); + put("Necromancer Lord Chestplate", "NECROMANCER_LORD_CHESTPLATE"); + put("Necromancer Lord Leggings", "NECROMANCER_LORD_LEGGINGS"); + put("Necromancer Lord Boots", "NECROMANCER_LORD_BOOTS"); + put("Fourth Master Star", "FOURTH_MASTER_STAR"); + put("Summoning Ring", "SUMMONING_RING"); + put("Fel Skull", "FEL_SKULL"); + put("Necromancer Sword", "NECROMANCER_SWORD"); + put("Soulweaver Gloves", "SOULWEAVER_GLOVES"); + put("Sadan's Brooch", "SADAN_BROOCH"); + put("Giant Tooth", "GIANT_TOOTH"); + + put("Precursor Gear", "PRECURSOR_GEAR"); // F7 M7 + put("Necron Dye", "DYE_NECRON"); + put("Storm the Fish", "STORM_THE_FISH"); + put("Maxor the Fish", "MAXOR_THE_FISH"); + put("Goldor the Fish", "GOLDOR_THE_FISH"); + put("Dark Claymore", "DARK_CLAYMORE"); + put("Necron's Handle", "NECRON_HANDLE"); + put("Master Skull - Tier 5", "MASTER_SKULL_TIER_5"); + put("Shadow Warp", "SHADOW_WARP_SCROLL"); + put("Wither Shield", "WITHER_SHIELD_SCROLL"); + put("Implosion", "IMPLOSION_SCROLL"); + put("Fifth Master Star", "FIFTH_MASTER_STAR"); + put("Auto Recombobulator", "AUTO_RECOMBOBULATOR"); + put("Wither Helmet", "WITHER_HELMET"); + put("Wither Chestplate", "WITHER_CHESTPLATE"); + put("Wither Leggings", "WITHER_LEGGINGS"); + put("Wither Boots", "WITHER_BOOTS"); + put("Wither Catalyst", "WITHER_CATALYST"); + put("Wither Cloak Sword", "WITHER_CLOAK"); + put("Wither Blood", "WITHER_BLOOD"); + }}; +} + diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java index 60e2b4e4..a81c25f7 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java @@ -3,6 +3,7 @@ package de.hysky.skyblocker.utils.render.gui; import com.mojang.blaze3d.systems.RenderSystem; import de.hysky.skyblocker.mixin.accessor.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.dungeon.CroesusHelper; +import de.hysky.skyblocker.skyblock.dungeon.CroesusProfit; import de.hysky.skyblocker.skyblock.dungeon.terminal.ColorTerminal; import de.hysky.skyblocker.skyblock.dungeon.terminal.OrderTerminal; import de.hysky.skyblocker.skyblock.dungeon.terminal.StartsWithTerminal; @@ -40,6 +41,7 @@ public class ContainerSolverManager { new OrderTerminal(), new StartsWithTerminal(), new CroesusHelper(), + new CroesusProfit(), new ChronomatronSolver(), new SuperpairsSolver(), new UltrasequencerSolver() diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 60631ef1..c8ac97c5 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -252,6 +252,8 @@ "text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.lossColor": "Loss Color", "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.croesusProfit": "Croesus Chest Profit Calculator", + "text.autoconfig.skyblocker.option.locations.dungeons.croesusProfit.@Tooltip": "Highlight chests with best profit at Croesus NPC", "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Enable Map", "text.autoconfig.skyblocker.option.locations.dungeons.mapScreen": "Dungeon Map & Score Placement Config...", "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "Map Scaling", |