diff options
author | hannibal2 <24389977+hannibal002@users.noreply.github.com> | 2024-06-20 23:25:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-20 23:25:00 +0200 |
commit | 1a32e5e1a6e338e87043d39d04b9a6b817544469 (patch) | |
tree | 9920cf33407378435b1aac5b34074be617fdc3b5 /src/main/java/at/hannibal2/skyhanni/features | |
parent | 9b1894c738a16ec6ce2d59a29f7b85a9b9df9381 (diff) | |
download | skyhanni-1a32e5e1a6e338e87043d39d04b9a6b817544469.tar.gz skyhanni-1a32e5e1a6e338e87043d39d04b9a6b817544469.tar.bz2 skyhanni-1a32e5e1a6e338e87043d39d04b9a6b817544469.zip |
Feature: Craftable Item List (#1334)
Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com>
Co-authored-by: Thunderblade73 <gaidermarkus@gmail.com>
Co-authored-by: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com>
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features')
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt | 173 |
1 files changed, 173 insertions, 0 deletions
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 +} |