diff options
Diffstat (limited to 'src/main/java/at/hannibal2')
4 files changed, 221 insertions, 3 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/CraftableItemListConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/CraftableItemListConfig.java new file mode 100644 index 000000000..398a3e59e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/CraftableItemListConfig.java @@ -0,0 +1,39 @@ +package at.hannibal2.skyhanni.config.features.inventory; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +public class CraftableItemListConfig { + + @Expose + @ConfigOption( + name = "Enabled", + desc = "Shows a list of items that can be crafted with the items in inventory when inside the crafting menu. " + + "Click on the item to open §e/recipe§7." + ) + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigOption( + name = "Include Sacks", + desc = "Include items from inside the sacks.") + @ConfigEditorBoolean + public boolean includeSacks = false; + + @Expose + @ConfigOption( + name = "Exclude Vanilla Items", + desc = "Hide vanilla items from the craftable item list.") + @ConfigEditorBoolean + public boolean excludeVanillaItems = true; + + @Expose + @ConfigLink(owner = CraftableItemListConfig.class, field = "enabled") + public Position position = new Position(144, 139, false, true); +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java index 978d97ead..fcdccfab7 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java @@ -57,7 +57,12 @@ public class InventoryConfig { @Expose @Category(name = "Chocolate Factory", desc = "Features to help you master the Chocolate Factory idle game.") public ChocolateFactoryConfig chocolateFactory = new ChocolateFactoryConfig(); - + + @Expose + @Category(name = "Craftable Item List", desc = "") + @Accordion + public CraftableItemListConfig craftableItemList = new CraftableItemListConfig(); + @Expose @ConfigOption(name = "Not Clickable Items", desc = "Better not click that item.") @Accordion diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt new file mode 100644 index 000000000..741021d87 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt @@ -0,0 +1,173 @@ +package at.hannibal2.skyhanni.features.inventory.craft + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.SackAPI.getAmountInSacks +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryOpenEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut +import at.hannibal2.skyhanni.utils.CollectionUtils.sortedDesc +import at.hannibal2.skyhanni.utils.HypixelCommands +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.ItemUtils.itemName +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import at.hannibal2.skyhanni.utils.NEUItems +import at.hannibal2.skyhanni.utils.NEUItems.getPrice +import at.hannibal2.skyhanni.utils.NEUItems.isVanillaItem +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.PrimitiveItemStack.Companion.toPrimitiveStackOrNull +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables +import at.hannibal2.skyhanni.utils.StringUtils +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import io.github.moulberry.notenoughupdates.recipes.CraftingRecipe +import io.github.moulberry.notenoughupdates.recipes.NeuRecipe +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.math.floor + +@SkyHanniModule +object CraftableItemList { + private val config get() = SkyHanniMod.feature.inventory.craftableItemList + + private var display = listOf<Renderable>() + private var inInventory = false + private val craftItemPattern by RepoPattern.pattern( + "craftableitemlist.craftitem", + "Craft Item", + ) + + @SubscribeEvent + fun onInventoryOpen(event: InventoryOpenEvent) { + if (!isEnabled()) return + if (!craftItemPattern.matches(event.inventoryName)) return + inInventory = true + + val pricePer = mutableMapOf<NEUInternalName, Double>() + val lines = mutableMapOf<NEUInternalName, Renderable>() + loadItems(pricePer, lines) + + display = if (lines.isEmpty()) { + listOf(Renderable.string("§7No Items to craft")) + } else { + val items = pricePer.sortedDesc().keys.map { lines[it] ?: error("impossible") } + listOf( + Renderable.string("§eCraftable items (${items.size})"), + Renderable.scrollList(items, height = 250, velocity = 20.0), + ) + } + } + + private fun loadItems( + pricePer: MutableMap<NEUInternalName, Double>, + lines: MutableMap<NEUInternalName, Renderable>, + ) { + val availableMaterial = readItems() + for (internalName in NEUItems.allInternalNames) { + if (config.excludeVanillaItems && internalName.isVanillaItem()) continue + + val recipes = NEUItems.getRecipes(internalName) + for (recipe in recipes) { + if (recipe !is CraftingRecipe) continue + val renderable = createItemRenderable(recipe, availableMaterial, pricePer, internalName) ?: continue + lines[internalName] = renderable + } + } + } + + private fun createItemRenderable( + recipe: CraftingRecipe, + availableMaterial: Map<NEUInternalName, Long>, + pricePer: MutableMap<NEUInternalName, Double>, + internalName: NEUInternalName, + ): Renderable? { + val neededItems = neededItems(recipe) + // Just a fail save, should not happen normally + if (neededItems.isEmpty()) return null + + val canCraftAmount = canCraftAmount(neededItems, availableMaterial) + if (canCraftAmount <= 0) return null + + val amountFormat = canCraftAmount.addSeparators() + val totalPrice = pricePer(neededItems) + pricePer[internalName] = totalPrice + val tooltip = buildList<String> { + add(internalName.itemName) + add("") + add("§7Craft cost: §6${totalPrice.shortFormat()}") + for ((item, amount) in neededItems) { + val name = item.itemName + val price = item.getPrice() * amount + add(" §8x${amount.addSeparators()} $name §7(§6${price.shortFormat()}§7)") + } + add("") + add("§7You have enough materials") + val timeName = StringUtils.pluralize(canCraftAmount, "time", "times") + add("§7to craft this item $amountFormat $timeName!") + add("") + add("§eClick to craft!") + } + return Renderable.clickAndHover( + "§8x$amountFormat ${internalName.itemName}", + tips = tooltip, + onClick = { + HypixelCommands.viewRecipe(internalName.asString()) + }, + ) + } + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + inInventory = false + } + + private fun pricePer(neededItems: Map<NEUInternalName, Int>): Double = neededItems.map { it.key.getPrice() * it.value }.sum() + + private fun canCraftAmount( + need: Map<NEUInternalName, Int>, + available: Map<NEUInternalName, Long>, + ): Int { + val canCraftTotal = mutableListOf<Int>() + for ((name, neededAmount) in need) { + val inventory = available[name] ?: 0 + val sacks = if (config.includeSacks) name.getAmountInSacks() else 0 + val having = inventory + sacks + val canCraft = floor(having.toDouble() / neededAmount).toInt() + canCraftTotal.add(canCraft) + } + return canCraftTotal.min() + } + + private fun neededItems(recipe: NeuRecipe): MutableMap<NEUInternalName, Int> { + val neededItems = mutableMapOf<NEUInternalName, Int>() + for (ingredient in recipe.ingredients) { + val material = ingredient.internalItemId.asInternalName() + val amount = ingredient.count.toInt() + neededItems.addOrPut(material, amount) + } + return neededItems + } + + private fun readItems(): Map<NEUInternalName, Long> { + val materials = mutableMapOf<NEUInternalName, Long>() + for (stack in InventoryUtils.getItemsInOwnInventory()) { + val item = stack.toPrimitiveStackOrNull() ?: continue + materials.addOrPut(item.internalName, item.amount.toLong()) + } + return materials + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { + if (!isEnabled()) return + if (!inInventory) return + + config.position.renderRenderables(display, posLabel = "Craft Materials From Bazaar") + } + + fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt index 262b768a4..bb2d3fd3e 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt @@ -215,8 +215,9 @@ object NEUItems { fallbackItem } - fun isVanillaItem(item: ItemStack): Boolean = - manager.auctionManager.isVanillaItem(item.getInternalName().asString()) + fun isVanillaItem(item: ItemStack): Boolean = item.getInternalName().isVanillaItem() + + fun NEUInternalName.isVanillaItem(): Boolean = manager.auctionManager.isVanillaItem(this.asString()) const val itemFontSize = 2.0 / 3.0 |