package net.torvald.terrarum.itemproperties import com.badlogic.gdx.utils.JsonValue import net.torvald.terrarum.INGAME import net.torvald.terrarum.Terrarum import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.isBlock import net.torvald.terrarum.gameitems.isWall import net.torvald.terrarum.modulebasegame.gameactors.ActorInventory import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory import net.torvald.terrarum.utils.forEachSiblings import net.torvald.terrarum.utils.forEachSiblingsIndexed /** * Created by minjaesong on 2022-06-24. */ class CraftingCodex { /** * Key: final product of the given recipes. Equal to the recipe.product * Value: the recipes */ @Transient internal val props = HashMap>() // the key ItemID and value.product must be equal fun addRecipe(recipe: CraftingRecipe) { val product = recipe.product if (props.containsKey(product)) { props[product]?.add(recipe) } else { props[product] = arrayListOf(recipe) } } fun addFromJson(json: JsonValue, moduleName: String, fileName:String) { if (moduleName.filter { it.code in 33..127 } .length < 5) throw IllegalStateException("Invalid module name: ${moduleName}") json.forEachSiblings { itemName, details -> val workbenchStr = details["workbench"].asString() val recipes = ArrayList() details["ingredients"].forEachSiblingsIndexed { ingredientIndex, _, ingredientRecord -> var moq = -1L val qtys = ArrayList() val itemsStr = ArrayList() ingredientRecord.forEachIndexed { i, elem -> if (i == 0) { moq = elem.asLong() } else { if (i % 2 == 1) qtys.add(elem.asLong()) else itemsStr.add(elem.asString()) } } // sanity check if (moq !in 1..1000) throw IllegalStateException("Recipe #${ingredientIndex+1} for item '$itemName' has invalid moq of ${moq}") else if (qtys.size != itemsStr.size) throw IllegalStateException("Mismatched item name and count for recipe #${ingredientIndex+1} for item '$itemName'") val ingredients = ArrayList() itemsStr.forEachIndexed { i, itemStr -> ingredients.add(CraftingIngredients( if (itemStr.startsWith("$")) itemStr.substring(1).replace('$',',') else itemStr , if (itemStr.startsWith("$")) CraftingItemKeyMode.TAG else CraftingItemKeyMode.VERBATIM , qtys[i] )) } recipes.add(CraftingRecipe(workbenchStr, ingredients.toTypedArray(), moq, itemName, "$moduleName/$fileName")) } // register to the main props if (props[itemName] == null) props[itemName] = ArrayList() props[itemName]!!.addAll(recipes) } } /** * Returns list of all possible recipes for the item; null if there is no recipe * * Even if the props for the specified item is happens to exist but has no element (usually caused by bad mod behaviour), * this function is guaranteed to return null. */ fun getRecipesFor(itemID: ItemID): List? = props[itemID]?.toList()?.let { return it.ifEmpty { null } } /** * Returns list of items that uses the given `item`. * * @return list of itemIDs and corresponding recipes */ fun getCraftableRecipesUsingTheseItems(item: ItemID): List { return props.values.flatten().filter { recipe -> recipe.ingredients.any { ingredient -> when (ingredient.keyMode) { CraftingItemKeyMode.TAG -> if (item.isBlock() || item.isWall()) Terrarum.blockCodex[item].hasTag(ingredient.key) else Terrarum.itemCodex[item]!!.hasTag(ingredient.key) CraftingItemKeyMode.VERBATIM -> (item == ingredient.key) } } } } /** * @return list of itemIDs and corresponding recipes */ fun getRecipesForIngredients(ingredients: List>): List { TODO() } data class CraftingRecipe(val workbench: String, val ingredients: Array, val moq: Long, val product: ItemID, val addedBy: String) data class CraftingIngredients(val key: String, val keyMode: CraftingItemKeyMode, val qty: Long) { override fun toString() = "$qty ${if (keyMode == CraftingItemKeyMode.TAG) "\$$key" else "$key"}" } enum class CraftingItemKeyMode { VERBATIM, TAG } }