From 4569546bddc5d5b0b712095bc89cc56625964166 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 25 Jun 2022 03:28:02 +0900 Subject: [PATCH] crafting recipe loader wip --- assets/mods/basegame/crafting/doc.md | 10 +- src/net/torvald/terrarum/ModMgr.kt | 25 +++-- src/net/torvald/terrarum/PostProcessor.kt | 5 + src/net/torvald/terrarum/Terrarum.kt | 2 + .../terrarum/itemproperties/CraftingCodex.kt | 102 ++++++++++++++++++ src/net/torvald/terrarum/langpack/Lang.kt | 4 +- .../gameactors/InjectCreatureRaw.kt | 2 +- src/net/torvald/terrarum/serialise/Common.kt | 8 +- .../torvald/terrarum/serialise/WriteConfig.kt | 2 +- src/net/torvald/terrarum/ui/UIItem.kt | 4 +- src/net/torvald/terrarum/utils/JsonFetcher.kt | 21 +++- 11 files changed, 162 insertions(+), 23 deletions(-) create mode 100644 src/net/torvald/terrarum/itemproperties/CraftingCodex.kt diff --git a/assets/mods/basegame/crafting/doc.md b/assets/mods/basegame/crafting/doc.md index 65b94000b..9c2dc53cb 100644 --- a/assets/mods/basegame/crafting/doc.md +++ b/assets/mods/basegame/crafting/doc.md @@ -9,7 +9,7 @@ Multiple workbenches are separated by commas, and alternative workbenches are se ### Ingredient Querying -Ingredients are defined as list of records. Multiple records denotes multiple alternative recipes, whereas +Ingredients are defined as list of records. Multiple records denote multiple alternative recipes, whereas entries in a record denote multiple ingredients the recipe requires. Example: @@ -25,18 +25,18 @@ Each entry is interpreted as: ```[moq, count 1, ingredient 1, count 2, ingredient 2, ...]``` -- moq: this item combination creates this amount of items. +- moq: this combination of ingredients creates this amount of crafted item. For example: ```[2, 1, "$WOOD", 1, "$ROCK"]``` This line is interpreted as: this item requires 1 tagged-as-wood ingredient and 1 tagged-as-rock ingredient, -and returns 2 of manufactured items. +and returns 2 of crafted items. ```[20, 1, "ITEM_PLATFORM_BUILDING_KIT"]``` This line is interpreted as: this item requires 1 verbatim item "ITEM_PLATFORM_BUILDING_KIT" and returns -20 of manufactured items. +20 of crafted items. -Therefore, the single record has at least three items and always has odd number of items. \ No newline at end of file +Therefore, the single record has at least three elements and always has odd number of them. \ No newline at end of file diff --git a/src/net/torvald/terrarum/ModMgr.kt b/src/net/torvald/terrarum/ModMgr.kt index d8fc45003..11d6d9014 100644 --- a/src/net/torvald/terrarum/ModMgr.kt +++ b/src/net/torvald/terrarum/ModMgr.kt @@ -8,6 +8,7 @@ import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.blockproperties.WireCodex import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.ItemID +import net.torvald.terrarum.itemproperties.CraftingCodex import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.itemproperties.MaterialCodex import net.torvald.terrarum.langpack.Lang @@ -224,7 +225,7 @@ object ModMgr { // check for module-info.java val moduleInfoPath = cl.getResources("module-info.class").toList().filter { it.toString().contains("$moduleName/$jar!/module-info.class") && it.toString().endsWith("module-info.class")} - if (moduleInfoPath.size == 0) { + if (moduleInfoPath.isEmpty()) { throw IllegalStateException("module-info not found on $moduleName") } @@ -441,7 +442,7 @@ object ModMgr { } object GameItemLoader { - val itemPath = "items/" + const val itemPath = "items/" init { Terrarum.itemCodex = ItemCodex() @@ -488,7 +489,7 @@ object ModMgr { } object GameLanguageLoader { - val langPath = "locales/" + const val langPath = "locales/" @JvmStatic operator fun invoke(module: String) { Lang.load(getFile(module, langPath)) @@ -496,7 +497,7 @@ object ModMgr { } object GameMaterialLoader { - val matePath = "materials/" + const val matePath = "materials/" init { Terrarum.materialCodex = MaterialCodex() @@ -511,7 +512,7 @@ object ModMgr { * A sugar-library for easy texture pack creation */ object GameRetextureLoader { - val retexturesPath = "retextures/" + const val retexturesPath = "retextures/" val retexables = listOf("blocks","wires") val altFilePaths = HashMap() val retexableCallbacks = HashMap Unit>() @@ -537,8 +538,8 @@ object ModMgr { if (dir.isDirectory && dir.exists()) { dir.listFiles { it: File -> - it?.name?.contains('-') == true - }.forEach { + it.name.contains('-') + }?.forEach { // -.tga or .png val tokens = it.name.split('-') if (tokens.size > 1) { @@ -558,6 +559,16 @@ object ModMgr { } } + object GameCraftingRecipeLoader { + const val recipePath = "crafting/" + + @JvmStatic operator fun invoke(module: String) { + getFile(module, recipePath).listFiles { it: File -> it.name.lowercase().endsWith(".json") }?.forEach { jsonFile -> + Terrarum.craftingCodex.addFromJson(JsonFetcher(jsonFile), module) + } + } + } + } private class JarFileLoader(urls: Array) : URLClassLoader(urls) { diff --git a/src/net/torvald/terrarum/PostProcessor.kt b/src/net/torvald/terrarum/PostProcessor.kt index 23d44181f..5dee9afeb 100644 --- a/src/net/torvald/terrarum/PostProcessor.kt +++ b/src/net/torvald/terrarum/PostProcessor.kt @@ -100,6 +100,11 @@ object PostProcessor : Disposable { gdxClearAndSetBlend(.094f, .094f, .094f, 0f) + fbo.colorBufferTexture.setFilter( + Texture.TextureFilter.Linear, + if (App.scr.magn % 1.0 < 0.0001) Texture.TextureFilter.Nearest else Texture.TextureFilter.Linear + ) + postShader(projMat, fbo) // draw things when F keys are on diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index a6c519e76..a889d71f0 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -23,6 +23,7 @@ import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.ActorID import net.torvald.terrarum.gameactors.faction.FactionCodex import net.torvald.terrarum.gameworld.fmod +import net.torvald.terrarum.itemproperties.CraftingCodex import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.itemproperties.MaterialCodex import net.torvald.terrarum.savegame.ByteArray64Reader @@ -72,6 +73,7 @@ object Terrarum : Disposable { var wireCodex = WireCodex(); internal set var materialCodex = MaterialCodex(); internal set var factionCodex = FactionCodex(); internal set + var craftingCodex = CraftingCodex(); internal set var apocryphas = HashMap(); internal set diff --git a/src/net/torvald/terrarum/itemproperties/CraftingCodex.kt b/src/net/torvald/terrarum/itemproperties/CraftingCodex.kt new file mode 100644 index 000000000..8c1833a8f --- /dev/null +++ b/src/net/torvald/terrarum/itemproperties/CraftingCodex.kt @@ -0,0 +1,102 @@ +package net.torvald.terrarum.itemproperties + +import com.badlogic.gdx.utils.JsonValue +import net.torvald.terrarum.gameitems.ItemID +import net.torvald.terrarum.utils.forEachSiblings +import net.torvald.terrarum.utils.forEachSiblingsIndexed + +/** + * Created by minjaesong on 2022-06-24. + */ +class CraftingCodex { + + @Transient private val props = HashMap>() + + fun addFromJson(json: JsonValue, moduleName: 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 < 1) + throw IllegalStateException("Recipe #${ingredientIndex+1} for item '$itemName' has 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) + else + itemStr + , + if (itemStr.startsWith("$")) + CraftingItemKeyMode.TAG + else + CraftingItemKeyMode.VERBATIM + , + qtys[i] + )) + } + recipes.add(CraftingRecipe(workbenchStr, ingredients.toTypedArray(), moq, moduleName)) + } + + // 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 if (it.isNotEmpty()) it else null + } + + /** + * @return list of itemIDs and corresponding recipes + */ + fun getRecipesUsingTheseItems(items: List): List> { + TODO() + } + + /** + * @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 addedBy: String) + data class CraftingIngredients(val key: String, val keyMode: CraftingItemKeyMode, val qty: Long) + enum class CraftingItemKeyMode { VERBATIM, TAG } +} + diff --git a/src/net/torvald/terrarum/langpack/Lang.kt b/src/net/torvald/terrarum/langpack/Lang.kt index 76b8e92d6..1efeb813e 100644 --- a/src/net/torvald/terrarum/langpack/Lang.kt +++ b/src/net/torvald/terrarum/langpack/Lang.kt @@ -87,7 +87,7 @@ object Lang { * "<>" = "<>" */ //println(json.entrySet()) - JsonFetcher.forEach(json) { key, value -> + JsonFetcher.forEachSiblings(json) { key, value -> langpack.put("${key}_$lang", value.asString()) } @@ -112,7 +112,7 @@ object Lang { * (the array continues) * */ - JsonFetcher.forEach(json.get("resources").get("data")) { _, entry -> + JsonFetcher.forEachSiblings(json.get("resources").get("data")) { _, entry -> langpack.put( "${entry.getString("n")}_$lang", entry.getString("s") diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/InjectCreatureRaw.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/InjectCreatureRaw.kt index ef6ab50fb..efe6c6c79 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/InjectCreatureRaw.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/InjectCreatureRaw.kt @@ -27,7 +27,7 @@ object InjectCreatureRaw { val jsonObj = JsonFetcher(ModMgr.getFile(module, "creatures/$jsonFileName")) - JsonFetcher.forEach(jsonObj) { key, value -> if (!key.startsWith("_")) { + JsonFetcher.forEachSiblings(jsonObj) { key, value -> if (!key.startsWith("_")) { val diceRollers = ArrayList() if (!value.isArray && !value.isObject) { diff --git a/src/net/torvald/terrarum/serialise/Common.kt b/src/net/torvald/terrarum/serialise/Common.kt index 5987ee3c4..6582ee997 100644 --- a/src/net/torvald/terrarum/serialise/Common.kt +++ b/src/net/torvald/terrarum/serialise/Common.kt @@ -120,7 +120,7 @@ object Common { override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): HashArray<*> { val hashMap = HashArray() - JsonFetcher.forEach(jsonData) { key, obj -> + JsonFetcher.forEachSiblings(jsonData) { key, obj -> hashMap[key.toLong()] = json.readValue(null, obj) } return hashMap @@ -138,7 +138,7 @@ object Common { override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): HashedWirings { val hashMap = HashedWirings() - JsonFetcher.forEach(jsonData) { key, obj -> + JsonFetcher.forEachSiblings(jsonData) { key, obj -> hashMap[key.toLong()] = json.readValue(GameWorld.WiringNode::class.java, obj) } return hashMap @@ -156,7 +156,7 @@ object Common { override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): HashedWiringGraph { val hashMap = HashedWiringGraph() - JsonFetcher.forEach(jsonData) { key, obj -> + JsonFetcher.forEachSiblings(jsonData) { key, obj -> hashMap[key.toLong()] = json.readValue(WiringGraphMap::class.java, obj) } return hashMap @@ -174,7 +174,7 @@ object Common { override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): WiringGraphMap { val hashMap = WiringGraphMap() - JsonFetcher.forEach(jsonData) { key, obj -> + JsonFetcher.forEachSiblings(jsonData) { key, obj -> hashMap[key] = json.readValue(GameWorld.WiringSimCell::class.java, obj) } return hashMap diff --git a/src/net/torvald/terrarum/serialise/WriteConfig.kt b/src/net/torvald/terrarum/serialise/WriteConfig.kt index 54b9a5745..7608497fa 100644 --- a/src/net/torvald/terrarum/serialise/WriteConfig.kt +++ b/src/net/torvald/terrarum/serialise/WriteConfig.kt @@ -31,7 +31,7 @@ object WriteConfig { override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): KVHashMap { val map = KVHashMap() - JsonFetcher.forEach(jsonData) { key, obj -> + JsonFetcher.forEachSiblings(jsonData) { key, obj -> map[key] = json.readValue(null, obj) } return map diff --git a/src/net/torvald/terrarum/ui/UIItem.kt b/src/net/torvald/terrarum/ui/UIItem.kt index 4b3e9f290..744146736 100644 --- a/src/net/torvald/terrarum/ui/UIItem.kt +++ b/src/net/torvald/terrarum/ui/UIItem.kt @@ -79,10 +79,10 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I /** Position of mouse relative to this item */ protected val itemRelativeMouseX: Int - get() = (Terrarum.mouseScreenX - (parentUI.posX) - this.posX) + get() = (Terrarum.mouseScreenX - parentUI.posX - this.posX) /** Position of mouse relative to this item */ protected val itemRelativeMouseY: Int - get() = (Terrarum.mouseScreenY - (parentUI.posY) - this.posY) + get() = (Terrarum.mouseScreenY - parentUI.posY - this.posY) /** If mouse is hovering over it */ open val mouseUp: Boolean diff --git a/src/net/torvald/terrarum/utils/JsonFetcher.kt b/src/net/torvald/terrarum/utils/JsonFetcher.kt index b16166095..0aa4de7b7 100644 --- a/src/net/torvald/terrarum/utils/JsonFetcher.kt +++ b/src/net/torvald/terrarum/utils/JsonFetcher.kt @@ -47,7 +47,13 @@ object JsonFetcher { } - fun forEach(map: JsonValue, action: (String, JsonValue) -> Unit) { + /** + * Iterates [JsonValue] over its siblings. + * + * @param map JsonValue to iterate over + * @param action A `function(`Name of the sibling or a stringified integer if the `map` is an array`, `JsonValue representation of the sibling`)` -> `Unit` + */ + fun forEachSiblings(map: JsonValue, action: (String, JsonValue) -> Unit) { var counter = 0 var entry = map.child while (entry != null) { @@ -56,4 +62,17 @@ object JsonFetcher { counter += 1 } } + + fun forEachSiblingsIndexed(map: JsonValue, action: (Int, String, JsonValue) -> Unit) { + var counter = 0 + var entry = map.child + while (entry != null) { + action(counter, entry.name ?: "$counter", entry) + entry = entry.next + counter += 1 + } + } } + +fun JsonValue.forEachSiblings(action: (String, JsonValue) -> Unit) = JsonFetcher.forEachSiblings(this, action) +fun JsonValue.forEachSiblingsIndexed(action: (Int, String, JsonValue) -> Unit) = JsonFetcher.forEachSiblingsIndexed(this, action) \ No newline at end of file