crafting recipe loader wip

This commit is contained in:
minjaesong
2022-06-25 03:28:02 +09:00
parent 828a485395
commit 4569546bdd
11 changed files with 162 additions and 23 deletions

View File

@@ -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.
Therefore, the single record has at least three elements and always has odd number of them.

View File

@@ -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<String, FileHandle>()
val retexableCallbacks = HashMap<String, () -> 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 {
// <other modname>-<hopefully a number>.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<URL>) : URLClassLoader(urls) {

View File

@@ -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

View File

@@ -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<String, Any>(); internal set

View File

@@ -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<ItemID, ArrayList<CraftingRecipe>>()
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<CraftingRecipe>()
details["ingredients"].forEachSiblingsIndexed { ingredientIndex, _, ingredientRecord ->
var moq = -1L
val qtys = ArrayList<Long>()
val itemsStr = ArrayList<String>()
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<CraftingIngredients>()
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<CraftingRecipe>? = props[itemID]?.toList()?.let {
return if (it.isNotEmpty()) it else null
}
/**
* @return list of itemIDs and corresponding recipes
*/
fun getRecipesUsingTheseItems(items: List<ItemID>): List<Pair<ItemID, CraftingRecipe>> {
TODO()
}
/**
* @return list of itemIDs and corresponding recipes
*/
fun getRecipesForIngredients(ingredients: List<Pair<ItemID, Long>>): List<Pair<ItemID, CraftingRecipe>> {
TODO()
}
data class CraftingRecipe(val workbench: String, val ingredients: Array<CraftingIngredients>, val moq: Long, val addedBy: String)
data class CraftingIngredients(val key: String, val keyMode: CraftingItemKeyMode, val qty: Long)
enum class CraftingItemKeyMode { VERBATIM, TAG }
}

View File

@@ -87,7 +87,7 @@ object Lang {
* "<<STRING ID>>" = "<<LOCALISED TEXT>>"
*/
//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")

View File

@@ -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<String>()
if (!value.isArray && !value.isObject) {

View File

@@ -120,7 +120,7 @@ object Common {
override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): HashArray<*> {
val hashMap = HashArray<Any>()
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

View File

@@ -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

View File

@@ -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

View File

@@ -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)