DEADSOFTWARE

Fix furnace bugs
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / GameItemsHolder.kt
index 73da0c2cf9ba63ef0540f5af7def55dfe5c73ba4..050ae6fe442acacc4d3c20211d94b7200738182c 100644 (file)
@@ -3,17 +3,23 @@ package ru.deadsoftware.cavedroid.game
 import com.badlogic.gdx.Gdx
 import kotlinx.serialization.json.Json
 import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.craft.CraftingRecipe
+import ru.deadsoftware.cavedroid.game.model.craft.CraftingResult
 import ru.deadsoftware.cavedroid.game.model.dto.BlockDto
+import ru.deadsoftware.cavedroid.game.model.dto.CraftingDto
 import ru.deadsoftware.cavedroid.game.model.dto.GameItemsDto
 import ru.deadsoftware.cavedroid.game.model.dto.ItemDto
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
 import ru.deadsoftware.cavedroid.game.model.item.Item
 import ru.deadsoftware.cavedroid.game.model.mapper.BlockMapper
 import ru.deadsoftware.cavedroid.game.model.mapper.ItemMapper
 import ru.deadsoftware.cavedroid.misc.utils.AssetLoader
+import java.util.LinkedList
 import javax.inject.Inject
 
 @GameScope
 class GameItemsHolder @Inject constructor(
+    private val assetLoader: AssetLoader,
     private val blockMapper: BlockMapper,
     private val itemMapper: ItemMapper,
 ) {
@@ -22,15 +28,21 @@ class GameItemsHolder @Inject constructor(
 
     private val blocksMap = LinkedHashMap<String, Block>()
     private val itemsMap = LinkedHashMap<String, Item>()
+    private val craftingRecipes = LinkedList<CraftingRecipe>()
 
-    private lateinit var fallbackBlock: Block
-    private lateinit var fallbackItem: Item
-
+    lateinit var fallbackBlock: Block
+        private set
+    lateinit var fallbackItem: Item
+        private set
 
+    init {
+        initialize()
+    }
 
     private fun loadBlocks(dtoMap: Map<String, BlockDto>) {
         dtoMap.forEach { (key, dto) ->
             blocksMap[key] = blockMapper.map(key, dto)
+                .apply(Block::initialize)
         }
 
         fallbackBlock = blocksMap[FALLBACK_BLOCK_KEY]
@@ -43,29 +55,62 @@ class GameItemsHolder @Inject constructor(
         }
 
         dtoMap.forEach { (key, dto) ->
-            itemsMap[key] = itemMapper.map(key, dto, blocksMap[key])
+            try {
+                itemsMap[key] = itemMapper.map(
+                    key = key,
+                    dto = dto,
+                    block = blocksMap[key],
+                    slabTopBlock = blocksMap[dto.topSlabBlock] as? Block.Slab,
+                    slabBottomBlock = blocksMap[dto.bottomSlabBlock] as? Block.Slab
+                )
+            } catch (e: Exception) {
+                Gdx.app.error(TAG, "Failed to map item $key. Reason: ${e.message}")
+                e.printStackTrace()
+            }
         }
 
         fallbackItem = itemsMap[FALLBACK_ITEM_KEY]
             ?: throw IllegalArgumentException("Fallback item key '$FALLBACK_ITEM_KEY' not found")
     }
 
-    fun initialize(assetLoader: AssetLoader) {
+    private fun loadCraftingRecipes() {
+        val jsonString = assetLoader.getAssetHandle("json/crafting.json").readString()
+        val jsonMap = JsonFormat.decodeFromString<Map<String, CraftingDto>>(jsonString)
+
+        if (jsonMap.isNotEmpty() && itemsMap.isEmpty()) {
+            throw IllegalStateException("items should be loaded before crafting")
+        }
+
+        jsonMap.forEach { (key, value) ->
+            craftingRecipes += CraftingRecipe(
+                input = value.input.map(::Regex),
+                output = CraftingResult(getItem(key), value.count)
+            )
+        }
+    }
+
+    fun initialize() {
         if (_initialized) {
             Gdx.app.debug(TAG, "Attempted to init when already initialized")
             return
         }
 
         val jsonString = assetLoader.getAssetHandle("json/game_items.json").readString()
-        val gameItemsDto = JsonFormat.decodeFromString(GameItemsDto.GameItemsDtoJsonSerializer, jsonString)
+        val gameItemsDto = JsonFormat.decodeFromString<GameItemsDto>(jsonString)
 
         loadBlocks(gameItemsDto.blocks)
         loadItems(gameItemsDto.items)
 
         _initialized = true
+
+        loadCraftingRecipes()
     }
 
     private fun <T> Map<String, T>.getOrFallback(key: String, fallback: T, lazyErrorMessage: () -> String): T {
+        if (!_initialized) {
+            throw IllegalStateException("GameItemsHolder was not initialized before use")
+        }
+
         val t = this[key] ?: run {
             Gdx.app.error(TAG, lazyErrorMessage.invoke())
             return fallback
@@ -85,6 +130,44 @@ class GameItemsHolder @Inject constructor(
         }
     }
 
+    fun craftItem(input: List<Item>): InventoryItem? {
+        val startIndex = input.indexOfFirst { !it.isNone() }.takeIf { it >= 0 } ?: return null
+
+        return  try {
+            craftingRecipes.first { rec ->
+                for (i in rec.input.indices) {
+                    if (startIndex + i >= input.size) {
+                        return@first rec.input.subList(i, rec.input.size).all { it.matches("none") }
+                    }
+                    if (!input[startIndex + i].params.key.matches(rec.input[i])) {
+                        return@first false
+                    }
+                }
+                return@first true
+            }.output.toInventoryItem()
+        } catch (e: NoSuchElementException) {
+            null
+        }
+    }
+
+    fun getAllItems(): Collection<Item> {
+        return itemsMap.values
+    }
+
+    fun getItemFromCreativeInventory(position: Int): Item {
+        return if (position in itemsMap.values.indices) {
+            itemsMap.values.elementAt(position)
+        } else {
+            fallbackItem
+        }
+    }
+
+    fun getMaxCreativeScrollAmount(): Int = itemsMap.size / 8
+
+    fun <T : Block> getBlocksByType(type: Class<T>): List<T> {
+        return blocksMap.values.filterIsInstance(type)
+    }
+
     companion object {
         private const val TAG = "GameItemsHolder"