From 4185b2cc025bf9cc55bd4fcd71650415dc234d2c Mon Sep 17 00:00:00 2001 From: Fluboxer <36457056+Fluboxer@users.noreply.github.com> Date: Sat, 28 Dec 2024 02:06:12 +0300 Subject: Bits Helper (#939) * just saving those before I do something silly * made adding to tooltip work * at this point it is usable * admins why * ABICASE_MODEL * now it should highlight two groups (all and quick sell) not finished yet * refactor * a bit of renaming * removed extra line. That's it * a little bit of refactor. NOT FINISHED * okay it finally works * requested changes. Currently in testing to see if anything broke * rebased + few little changes * Update src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BitsHelper.java Co-authored-by: Kevin <92656833+kevinthegreat1@users.noreply.github.com> * :clown: * Fix TooltipInfoType * Reformat BitsHelper * Clean up fields * More cleanup * Cleanup * Try to clean up fields and local variables --------- Co-authored-by: Kevin <92656833+kevinthegreat1@users.noreply.github.com> --- .../config/categories/HelperCategory.java | 11 + .../skyblocker/config/configs/HelperConfig.java | 3 + .../skyblock/item/tooltip/TooltipManager.java | 1 + .../skyblock/item/tooltip/adders/BitsHelper.java | 396 +++++++++++++++++++++ .../item/tooltip/info/TooltipInfoType.java | 4 +- .../utils/container/ContainerSolverManager.java | 4 +- 6 files changed, 416 insertions(+), 3 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BitsHelper.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java index 80d96483..94f0fc0c 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java @@ -26,6 +26,17 @@ public class HelperCategory { .controller(ConfigUtils::createBooleanController) .build()) + // Bits Helper + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.helpers.enableBitsHelper")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.helpers.enableBitsHelper.@Tooltip"))) + .binding(defaults.helpers.enableBitsTooltip, + () -> config.helpers.enableBitsTooltip, + newValue -> config.helpers.enableBitsTooltip = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + + // Wardrobe Helper .option(Option.createBuilder() .name(Text.translatable("skyblocker.config.helpers.enableWardrobeHelper")) .description(OptionDescription.of(Text.translatable("skyblocker.config.helpers.enableWardrobeHelper.@Tooltip"))) diff --git a/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java index b4b15009..2b1944ae 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java @@ -8,6 +8,9 @@ public class HelperConfig { @SerialEntry public boolean enableNewYearCakesHelper = true; + @SerialEntry + public boolean enableBitsTooltip = true; + @SerialEntry public boolean enableWardrobeHelper = true; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java index 684588ea..2566ea13 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java @@ -28,6 +28,7 @@ public class TooltipManager { new TrueHexDyeScreenDisplay(), new SupercraftReminder(), ChocolateFactorySolver.INSTANCE, + BitsHelper.INSTANCE, new ReorderHelper(), new StackingEnchantProgressTooltip(0), //Would be best to have after the lore but the tech doesn't exist for that new NpcPriceTooltip(1), diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BitsHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BitsHelper.java new file mode 100644 index 00000000..d3de1cc8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BitsHelper.java @@ -0,0 +1,396 @@ +package de.hysky.skyblocker.skyblock.item.tooltip.adders; + +import com.mojang.logging.LogUtils; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.container.SimpleContainerSolver; +import de.hysky.skyblocker.utils.container.TooltipAdder; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.*; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.Slot; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Util; +import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; + +import java.text.NumberFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BitsHelper extends SimpleContainerSolver implements TooltipAdder { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Pattern BITS_PATTERN = Pattern.compile("Cost (?[\\d,]+) Bits"); + private static final Pattern CATEGORY_PATTERN = Pattern.compile("Click to browse!"); + @Language("RegExp") + private static final String TITLE_PATTERN = ".*(?:Community Shop|Bits Shop).*"; + private static final NumberFormat DECIMAL_FORMAT = NumberFormat.getInstance(Locale.US); + private static final String LOGS_PREFIX = "[Skyblocker Bits Helper] "; + + //region Constants + /** + * List of items that sell well. No, it is not automated, but that stuff is unlikely to change unless some update + * Definition of "good selling item": 300+ daily sales, 12+ hourly sales + * I would personally also demand good price per item as filling up all ah slots with catalyst would suck + * But newer players would find a good use of it so ima keep my preferences away + * Actual on 14.08.2024 + */ + private static final List GOOD_SELLING_ITEMS = List.of( + "KAT_FLOWER", + "KAT_BOUQUET", + "HEAT_CORE", + "ULTIMATE_CARROT_CANDY_UPGRADE", + "TALISMAN_ENRICHMENT_SWAPPER", + "GOD_POTION_2", + "KISMET_FEATHER", + "MATRIARCH_PARFUM", + "AUTOPET_RULES_2" // it barely made it here + ); + + /** + * SKYBLOCK_ID, Bits Price + * Yes I just hardcoded it. Why work smart when you can work hard? + * Those are only from submenus (categories) as normal item is easy to parse + * It probably could've been extracted from neu repo or something + * Actual on 14.08.2024 + */ + private static final Object2IntMap CAT_KAT = Util.make(new Object2IntOpenHashMap<>(), map -> { + map.put("KAT_FLOWER", 500); + map.put("KAT_BOUQUET", 2500); + }); + + private static final Object2IntMap CAT_UPGRADE_COMPONENTS = Util.make(new Object2IntOpenHashMap<>(), map -> { + map.put("HEAT_CORE", 3000); + map.put("HYPER_CATALYST_UPGRADE", 300); + map.put("ULTIMATE_CARROT_CANDY_UPGRADE", 8000); + map.put("COLOSSAL_EXP_BOTTLE_UPGRADE", 1200); + map.put("JUMBO_BACKPACK_UPGRADE", 4000); + map.put("MINION_STORAGE_EXPANDER", 1500); + }); + + private static final Object2IntMap CAT_SACKS = Util.make(new Object2IntOpenHashMap<>(), map -> { + map.put("POCKET_SACK_IN_A_SACK", 8000); + map.put("LARGE_DUNGEON_SACK", 14000); + map.put("RUNE_SACK", 14000); + map.put("FLOWER_SACK", 14000); + map.put("DWARVEN_MINES_SACK", 14000); + map.put("CRYSTAL_HOLLOWS_SACK", 14000); + }); + + private static final Object2IntMap CAT_ABIPHONE = Util.make(new Object2IntOpenHashMap<>(), map -> { + map.put("TRIO_CONTACTS_ADDON", 6450); + map.put("ABICASE_SUMSUNG_1", 15000); + map.put("ABICASE_SUMSUNG_2", 25000); + map.put("ABICASE_REZAR", 26000); + map.put("ABICASE_BLUE_AQUA", 17000); + map.put("ABICASE_BLUE_BLUE", 17000); + map.put("ABICASE_BLUE_GREEN", 17000); + map.put("ABICASE_BLUE_RED", 17000); + map.put("ABICASE_BLUE_YELLOW", 17000); + }); + + private static final Object2IntMap CAT_DYES = Util.make(new Object2IntOpenHashMap<>(), map -> { + map.put("DYE_PURE_WHITE", 250000); + map.put("DYE_PURE_BLACK", 250000); + }); + + private static final Object2IntMap CAT_ENCHANTS = Util.make(new Object2IntOpenHashMap<>(), map -> { + map.put("ENCHANTMENT_HECATOMB_1", 6000); + map.put("ENCHANTMENT_EXPERTISE_1", 4000); + map.put("ENCHANTMENT_COMPACT_1", 4000); + map.put("ENCHANTMENT_CULTIVATING_1", 4000); + map.put("ENCHANTMENT_CHAMPION_1", 4000); + map.put("ENCHANTMENT_TOXOPHILITE_1", 4000); + }); + + private static final Object2IntMap CAT_ENRICHMENTS = Util.make(new Object2IntOpenHashMap<>(), map -> { + map.put("TALISMAN_ENRICHMENT_SWAPPER", 200); + map.put("TALISMAN_ENRICHMENT_WALK_SPEED", 5000); + map.put("TALISMAN_ENRICHMENT_INTELLIGENCE", 5000); + map.put("TALISMAN_ENRICHMENT_CRITICAL_DAMAGE", 5000); + map.put("TALISMAN_ENRICHMENT_CRITICAL_CHANCE", 5000); + map.put("TALISMAN_ENRICHMENT_STRENGTH", 5000); + map.put("TALISMAN_ENRICHMENT_DEFENSE", 5000); + map.put("TALISMAN_ENRICHMENT_HEALTH", 5000); + map.put("TALISMAN_ENRICHMENT_MAGIC_FIND", 5000); + map.put("TALISMAN_ENRICHMENT_FEROCITY", 5000); + map.put("TALISMAN_ENRICHMENT_SEA_CREATURE_CHANCE", 5000); + map.put("TALISMAN_ENRICHMENT_ATTACK_SPEED", 5000); + }); + + private static final Map> CATEGORIES = Map.of( + "Kat Items", CAT_KAT, + "Upgrade Components", CAT_UPGRADE_COMPONENTS, + "Sacks", CAT_SACKS, + "Abiphone Supershop", CAT_ABIPHONE, + "Dyes", CAT_DYES, + "Stacking Enchants", CAT_ENCHANTS, + "Enrichments", CAT_ENRICHMENTS + ); + //endregion + + public static final BitsHelper INSTANCE = new BitsHelper(); + + private final Map> categoryOutput = new HashMap<>(); + private int bestSlotIndexSelling = -1; // index of slot that has the best item from "white list" of good selling items (= items that sell quick) + private int bestSlotIndexAll = -1; // index of slot that has the best item overall. May has same result as bestSlotIndexSelling - it is intended + private boolean megamindInLogs = false; // comedy + + private BitsHelper() { + super(TITLE_PATTERN); + } + + @Override + public List getColors(Int2ObjectMap slots) { + List highlights = new ArrayList<>(); + BestItemsResult bestItemsResult = calculateBestItems(slots); + + bestSlotIndexSelling = bestItemsResult.bestSlotIndexSelling; + bestSlotIndexAll = bestItemsResult.bestSlotIndexAll; + + // If best selling = best overall, only green will appear. This is intended + if (bestSlotIndexSelling != -1 && bestSlotIndexSelling == bestSlotIndexAll) { + highlights.add(ColorHighlight.green(bestSlotIndexSelling)); + } else { + if (bestSlotIndexSelling != -1) { + highlights.add(ColorHighlight.green(bestSlotIndexSelling)); + } + if (bestSlotIndexAll != -1) { + highlights.add(ColorHighlight.yellow(bestSlotIndexAll)); + } + } + return highlights; + } + + @Override + public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List lines) { + String lore = ItemUtils.concatenateLore(lines); + Matcher bitsMatcher = BITS_PATTERN.matcher(lore); + long coinsPerBit = 0L; + String itemID = ""; + boolean isGreen = false; + if (focusedSlot == null) return; + + if (!CATEGORY_PATTERN.matcher(lore).find()) { + if (!bitsMatcher.find()) return; + + long bitsCost = Long.parseLong(bitsMatcher.group("amount").replace(",", "")); + double itemCost = ItemUtils.getItemPrice(stack).keyDouble() * stack.getCount(); + + if (itemCost == 0) return; + + coinsPerBit = Math.round(itemCost / bitsCost); + } else { + String outerKey = stack.getName().getString(); + ObjectLongPair innerMap, innerMapGreen; + + BestItemsResult bestItemsResult = calculateBestItems(getSlots()); + bestSlotIndexSelling = bestItemsResult.bestSlotIndexSelling; + bestSlotIndexAll = bestItemsResult.bestSlotIndexAll; + if (bestSlotIndexSelling != -1 && bestSlotIndexSelling == focusedSlot.getIndex()) isGreen = true; + innerMap = categoryOutput.get(outerKey); + innerMapGreen = categoryOutput.get(outerKey + "GREEN"); + + if (innerMap != null) { + if (isGreen) { // this is here so green highlighted category won't show yellow stuff + itemID = innerMapGreen.left(); + coinsPerBit = innerMapGreen.rightLong(); + } else { + itemID = innerMap.left(); + coinsPerBit = innerMap.rightLong(); + } + } + } + ItemStack foundItemStack = ItemRepository.getItemStack(itemID); + if (itemID.isEmpty()) { // a bit dirty, but basically if itemID is empty then it is normal item and NOT category + lines.add(Text.empty() + .append(Text.literal("Bits Cost: ").formatted(Formatting.AQUA)) + .append(Text.literal(DECIMAL_FORMAT.format(coinsPerBit) + " Coins per bit").formatted(Formatting.DARK_AQUA))); + } else if (foundItemStack != null && isGreen) { + lines.add(Text.empty() + .append(Text.literal("Bits Cost: ").formatted(Formatting.AQUA)) + .append(Text.literal(DECIMAL_FORMAT.format(coinsPerBit) + " Coins per bit").formatted(Formatting.DARK_AQUA))); + lines.add(Text.literal("From " + foundItemStack.getName().getString()).formatted(Formatting.GREEN)); + } else if (foundItemStack != null) { + lines.add(Text.empty() + .append(Text.literal("Bits Cost: ").formatted(Formatting.AQUA)) + .append(Text.literal(DECIMAL_FORMAT.format(coinsPerBit) + " Coins per bit").formatted(Formatting.DARK_AQUA))); + lines.add(Text.literal("From " + foundItemStack.getName().getString()).formatted(Formatting.DARK_AQUA)); + } else { // if below so it won't clog logs with that cursed enchanted book + if (!stack.getName().getString().equals("Stacking Enchants")) LOGGER.warn(LOGS_PREFIX + "ItemStack not found for {}", itemID); + lines.add(Text.empty() + .append(Text.literal("Bits Cost: ").formatted(Formatting.AQUA)) + .append(Text.literal(DECIMAL_FORMAT.format(coinsPerBit) + " Coins per bit").formatted(Formatting.DARK_AQUA))); + lines.add(Text.literal("From " + itemID).formatted(Formatting.DARK_AQUA)); + } + } + + /** + * Why there is no simpler way to get those? + * upd: there is, using `focusedSlot.inventory`, but I don't wanna touch code that already works just fine + */ + private Int2ObjectMap getSlots() { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.currentScreen instanceof HandledScreen screen) { + ScreenHandler handler = screen.getScreenHandler(); + + Int2ObjectMap slots = new Int2ObjectOpenHashMap<>(); + for (int i = 0; i < handler.slots.size(); i++) { + slots.put(i, handler.getSlot(i).getStack()); + } + if (!megamindInLogs) { + LOGGER.info(LOGS_PREFIX + """ + No slots? + ⠀⣞⢽⢪⢣⢣⢣⢫⡺⡵⣝⡮⣗⢷⢽⢽⢽⣮⡷⡽⣜⣜⢮⢺⣜⢷⢽⢝⡽⣝ + ⠸⡸⠜⠕⠕⠁⢁⢇⢏⢽⢺⣪⡳⡝⣎⣏⢯⢞⡿⣟⣷⣳⢯⡷⣽⢽⢯⣳⣫⠇ + ⠀⠀⢀⢀⢄⢬⢪⡪⡎⣆⡈⠚⠜⠕⠇⠗⠝⢕⢯⢫⣞⣯⣿⣻⡽⣏⢗⣗⠏⠀ + ⠀⠪⡪⡪⣪⢪⢺⢸⢢⢓⢆⢤⢀⠀⠀⠀⠀⠈⢊⢞⡾⣿⡯⣏⢮⠷⠁⠀⠀ + ⠀⠀⠀⠈⠊⠆⡃⠕⢕⢇⢇⢇⢇⢇⢏⢎⢎⢆⢄⠀⢑⣽⣿⢝⠲⠉⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡿⠂⠠⠀⡇⢇⠕⢈⣀⠀⠁⠡⠣⡣⡫⣂⣿⠯⢪⠰⠂⠀⠀⠀⠀ + ⠀⠀⠀⠀⡦⡙⡂⢀⢤⢣⠣⡈⣾⡃⠠⠄⠀⡄⢱⣌⣶⢏⢊⠂⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⢝⡲⣜⡮⡏⢎⢌⢂⠙⠢⠐⢀⢘⢵⣽⣿⡿⠁⠁⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠨⣺⡺⡕⡕⡱⡑⡆⡕⡅⡕⡜⡼⢽⡻⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⣼⣳⣫⣾⣵⣗⡵⡱⡡⢣⢑⢕⢜⢕⡝⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⣴⣿⣾⣿⣿⣿⡿⡽⡑⢌⠪⡢⡣⣣⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⡟⡾⣿⢿⢿⢵⣽⣾⣼⣘⢸⢸⣞⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠁⠇⠡⠩⡫⢿⣝⡻⡮⣒⢽⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀"""); + megamindInLogs = true; + } + return slots; + } + return null; + } + + private BestItemsResult calculateBestItems(Int2ObjectMap slots) { + long bestCoinsPerBitSelling = 0L; + long bestCoinsPerBitAll = 0L; + int bestSlotIndexSelling = -1; + int bestSlotIndexAll = -1; + if (slots == null || slots.isEmpty()) return new BestItemsResult(bestSlotIndexSelling, bestSlotIndexAll, bestCoinsPerBitSelling, bestCoinsPerBitAll); + + // process categories first + for (Int2ObjectMap.Entry entry : slots.int2ObjectEntrySet()) { + ItemStack stack = entry.getValue(); + if (stack == null || stack.isEmpty()) continue; + + if (CATEGORY_PATTERN.matcher(ItemUtils.concatenateLore(ItemUtils.getLore(stack))).find()) { + ObjectLongImmutablePair categoryResults = calculateBestInCategory(stack); + + String itemID = categoryResults.left(); + long coinsPerBit = categoryResults.rightLong(); + + if (GOOD_SELLING_ITEMS.contains(itemID) && (coinsPerBit > bestCoinsPerBitSelling)) { + bestCoinsPerBitSelling = coinsPerBit; + bestSlotIndexSelling = entry.getIntKey(); + } + + if (coinsPerBit > bestCoinsPerBitAll) { + bestCoinsPerBitAll = coinsPerBit; + bestSlotIndexAll = entry.getIntKey(); + } + } + } + + for (Int2ObjectMap.Entry entry : slots.int2ObjectEntrySet()) { + ItemStack stack = entry.getValue(); + if (stack == null || stack.isEmpty()) continue; + + String itemId = stack.getSkyblockApiId(); + String lore = ItemUtils.concatenateLore(ItemUtils.getLore(stack)); + Matcher bitsMatcher = BITS_PATTERN.matcher(lore); + if (!bitsMatcher.find()) continue; + + long bitsCost = Long.parseLong(bitsMatcher.group("amount").replace(",", "")); + double itemCost = ItemUtils.getItemPrice(stack).keyDouble() * stack.getCount(); + if (itemCost == 0 || bitsCost == 0) continue; + + long coinsPerBit = Math.round(itemCost / bitsCost); + + if (GOOD_SELLING_ITEMS.contains(itemId) && (coinsPerBit > bestCoinsPerBitSelling)) { + bestCoinsPerBitSelling = coinsPerBit; + bestSlotIndexSelling = entry.getIntKey(); + } + + if (coinsPerBit > bestCoinsPerBitAll) { + bestCoinsPerBitAll = coinsPerBit; + bestSlotIndexAll = entry.getIntKey(); + } + } + return new BestItemsResult(bestSlotIndexSelling, bestSlotIndexAll, bestCoinsPerBitSelling, bestCoinsPerBitAll); + } + + private ObjectLongImmutablePair calculateBestInCategory(ItemStack stack) { + long bestCoinsPerBitSelling = 0L; + long bestCoinsPerBitAll = 0L; + String catName = stack.getName().getString(); + categoryOutput.put(catName, ObjectLongImmutablePair.of("", 0L)); + categoryOutput.put(catName + "GREEN", ObjectLongImmutablePair.of("", 0L)); + Object2LongMap categoryResults = processCategory(stack); + + for (Map.Entry categoryEntry : categoryResults.object2LongEntrySet()) { + String itemID = categoryEntry.getKey(); + long coinsPerBit = categoryEntry.getValue(); + + if (GOOD_SELLING_ITEMS.contains(itemID) && (coinsPerBit > bestCoinsPerBitSelling)) { + categoryOutput.put(catName + "GREEN", ObjectLongImmutablePair.of(itemID, coinsPerBit)); + bestCoinsPerBitSelling = coinsPerBit; + } + + if (coinsPerBit > bestCoinsPerBitAll) { + categoryOutput.put(catName, ObjectLongImmutablePair.of(itemID, coinsPerBit)); + bestCoinsPerBitAll = coinsPerBit; + } + } + return categoryOutput.get(catName); + } + + private Object2LongMap processCategory(ItemStack stack) { + String categoryName = stack.getName().getString(); + + if (CATEGORIES.containsKey(categoryName)) { + Object2LongMap results = new Object2LongOpenHashMap<>(); + Map category = CATEGORIES.get(categoryName); + + for (Map.Entry entry : category.entrySet()) { + String itemID = entry.getKey(); + Integer itemBitsPrice = entry.getValue(); + double itemCost = ItemUtils.getItemPrice(itemID).keyDouble(); + long coinsPerBit = Math.round(itemCost / itemBitsPrice); + results.put(itemID, coinsPerBit); + } + return results; + } else if (categoryName.contains("Fuel Blocks")) { + String itemID = "INFERNO_FUEL_BLOCK"; // but I don't know if only 1x offer of 64x offer gets discount too + int[] itemBitsPrice = {75, 3600}; // if only 1x gets discount then it doesn't matter as x64 would be ALWAYS better even with it + double itemCost = ItemUtils.getItemPrice(itemID).keyDouble(); // TLDR: need blaze slayer 9 players to show their prices + long coinsPerBit = (long) (Math.max(itemCost / itemBitsPrice[0], itemCost * 64 / itemBitsPrice[1])); + Object2LongMap fuelBlockResult = new Object2LongOpenHashMap<>(); + fuelBlockResult.put(itemID, coinsPerBit); + return fuelBlockResult; + } else { + LOGGER.warn(LOGS_PREFIX + "Can't recognize category {} from bits shop! Consider reporting this to our discord!", categoryName); + } + return new Object2LongOpenHashMap<>(); + } + + @Override + public boolean isEnabled() { + return SkyblockerConfigManager.get().helpers.enableBitsTooltip; + } + + @Override + public int getPriority() { + return 0; // Intended to show first + } + + private record BestItemsResult(int bestSlotIndexSelling, int bestSlotIndexAll, long bestCoinsPerBitSelling, long bestCoinsPerBitAll) {} +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/info/TooltipInfoType.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/info/TooltipInfoType.java index e9e427b8..bff62731 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/info/TooltipInfoType.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/info/TooltipInfoType.java @@ -23,8 +23,8 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectMap; public interface TooltipInfoType { DataTooltipInfoType> NPC = ofData("https://hysky.de/api/npcprice", CodecUtils.object2DoubleMapCodec(Codec.STRING), true, Object2DoubleMap::containsKey, itemTooltip -> itemTooltip.enableNPCPrice); - DataTooltipInfoType> BAZAAR = ofData("https://hysky.de/api/bazaar", BazaarProduct.MAP_CODEC, false, Object2ObjectMap::containsKey, itemTooltip -> itemTooltip.enableBazaarPrice, itemTooltip -> itemTooltip.enableBazaarPrice || itemTooltip.enableCraftingCost != Craft.OFF || itemTooltip.enableEstimatedItemValue || getConfig().dungeons.dungeonChestProfit.enableProfitCalculator || getConfig().dungeons.dungeonChestProfit.croesusProfit || getConfig().uiAndVisuals.chestValue.enableChestValue || itemTooltip.showEssenceCost, EssenceShopPrice::refreshEssencePrices); - DataTooltipInfoType> LOWEST_BINS = ofData("https://hysky.de/api/auctions/lowestbins", CodecUtils.object2DoubleMapCodec(Codec.STRING), false, Object2DoubleMap::containsKey, itemTooltip -> itemTooltip.enableLowestBIN, itemTooltip -> itemTooltip.enableLowestBIN || itemTooltip.enableCraftingCost != Craft.OFF || itemTooltip.enableEstimatedItemValue || getConfig().dungeons.dungeonChestProfit.enableProfitCalculator || getConfig().dungeons.dungeonChestProfit.croesusProfit || getConfig().uiAndVisuals.chestValue.enableChestValue); + DataTooltipInfoType> BAZAAR = ofData("https://hysky.de/api/bazaar", BazaarProduct.MAP_CODEC, false, Object2ObjectMap::containsKey, itemTooltip -> itemTooltip.enableBazaarPrice, itemTooltip -> itemTooltip.enableBazaarPrice || itemTooltip.enableCraftingCost != Craft.OFF || itemTooltip.enableEstimatedItemValue || getConfig().dungeons.dungeonChestProfit.enableProfitCalculator || getConfig().dungeons.dungeonChestProfit.croesusProfit || getConfig().uiAndVisuals.chestValue.enableChestValue || SkyblockerConfigManager.get().helpers.enableBitsTooltip || itemTooltip.showEssenceCost, EssenceShopPrice::refreshEssencePrices); + DataTooltipInfoType> LOWEST_BINS = ofData("https://hysky.de/api/auctions/lowestbins", CodecUtils.object2DoubleMapCodec(Codec.STRING), false, Object2DoubleMap::containsKey, itemTooltip -> itemTooltip.enableLowestBIN, itemTooltip -> itemTooltip.enableLowestBIN || itemTooltip.enableCraftingCost != Craft.OFF || itemTooltip.enableEstimatedItemValue || getConfig().dungeons.dungeonChestProfit.enableProfitCalculator || getConfig().dungeons.dungeonChestProfit.croesusProfit || getConfig().uiAndVisuals.chestValue.enableChestValue || SkyblockerConfigManager.get().helpers.enableBitsTooltip); DataTooltipInfoType> ONE_DAY_AVERAGE = ofData("https://hysky.de/api/auctions/lowestbins/average/1day.json", CodecUtils.object2DoubleMapCodec(Codec.STRING), false, Object2DoubleMap::containsKey, itemTooltip -> itemTooltip.enableAvgBIN, itemTooltip -> itemTooltip.enableAvgBIN && itemTooltip.avg != GeneralConfig.Average.THREE_DAY); DataTooltipInfoType> THREE_DAY_AVERAGE = ofData("https://hysky.de/api/auctions/lowestbins/average/3day.json", CodecUtils.object2DoubleMapCodec(Codec.STRING), false, Object2DoubleMap::containsKey, itemTooltip -> itemTooltip.enableAvgBIN, itemTooltip -> itemTooltip.enableAvgBIN && itemTooltip.avg != GeneralConfig.Average.ONE_DAY || getConfig().uiAndVisuals.searchOverlay.enableAuctionHouse); DataTooltipInfoType> MOTES = ofData("https://hysky.de/api/motesprice", CodecUtils.object2IntMapCodec(Codec.STRING), true, Object2IntMap::containsKey, itemTooltip -> itemTooltip.enableMotesPrice, itemTooltip -> itemTooltip.enableMotesPrice && Utils.isInTheRift()); diff --git a/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolverManager.java index 668faf2c..dbe1f94f 100644 --- a/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolverManager.java +++ b/src/main/java/de/hysky/skyblocker/utils/container/ContainerSolverManager.java @@ -18,6 +18,7 @@ import de.hysky.skyblocker.skyblock.dwarven.CommissionHighlight; import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver; import de.hysky.skyblocker.skyblock.experiment.UltrasequencerSolver; +import de.hysky.skyblocker.skyblock.item.tooltip.adders.BitsHelper; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.render.gui.ColorHighlight; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -51,7 +52,8 @@ public class ContainerSolverManager { new NewYearCakeBagHelper(), NewYearCakesHelper.INSTANCE, ChocolateFactorySolver.INSTANCE, - new ReorderHelper() + new ReorderHelper(), + BitsHelper.INSTANCE }; private static ContainerSolver currentSolver = null; private static List highlights; -- cgit