aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/CraftableItemListConfig.java39
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java7
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt173
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt5
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