DEADSOFTWARE

Update README
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / GameItemsHolder.kt
1 package ru.deadsoftware.cavedroid.game
3 import com.badlogic.gdx.Gdx
4 import kotlinx.serialization.json.Json
5 import ru.deadsoftware.cavedroid.game.model.block.Block
6 import ru.deadsoftware.cavedroid.game.model.craft.CraftingRecipe
7 import ru.deadsoftware.cavedroid.game.model.craft.CraftingResult
8 import ru.deadsoftware.cavedroid.game.model.dto.BlockDto
9 import ru.deadsoftware.cavedroid.game.model.dto.CraftingDto
10 import ru.deadsoftware.cavedroid.game.model.dto.GameItemsDto
11 import ru.deadsoftware.cavedroid.game.model.dto.ItemDto
12 import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
13 import ru.deadsoftware.cavedroid.game.model.item.Item
14 import ru.deadsoftware.cavedroid.game.model.mapper.BlockMapper
15 import ru.deadsoftware.cavedroid.game.model.mapper.ItemMapper
16 import ru.deadsoftware.cavedroid.misc.utils.AssetLoader
17 import java.util.LinkedList
18 import javax.inject.Inject
20 @GameScope
21 class GameItemsHolder @Inject constructor(
22 private val assetLoader: AssetLoader,
23 private val blockMapper: BlockMapper,
24 private val itemMapper: ItemMapper,
25 ) {
27 private var _initialized: Boolean = false
29 private val blocksMap = LinkedHashMap<String, Block>()
30 private val itemsMap = LinkedHashMap<String, Item>()
31 private val craftingRecipes = LinkedList<CraftingRecipe>()
33 lateinit var fallbackBlock: Block
34 private set
35 lateinit var fallbackItem: Item
36 private set
38 init {
39 initialize()
40 }
42 private fun loadBlocks(dtoMap: Map<String, BlockDto>) {
43 dtoMap.forEach { (key, dto) ->
44 blocksMap[key] = blockMapper.map(key, dto)
45 .apply(Block::initialize)
46 }
48 fallbackBlock = blocksMap[FALLBACK_BLOCK_KEY]
49 ?: throw IllegalArgumentException("Fallback block key '$FALLBACK_BLOCK_KEY' not found")
50 }
52 private fun loadItems(dtoMap: Map<String, ItemDto>) {
53 if (dtoMap.isNotEmpty() && blocksMap.isEmpty()) {
54 throw IllegalStateException("items should be loaded after blocks")
55 }
57 dtoMap.forEach { (key, dto) ->
58 try {
59 itemsMap[key] = itemMapper.map(
60 key = key,
61 dto = dto,
62 block = blocksMap[key],
63 slabTopBlock = blocksMap[dto.topSlabBlock] as? Block.Slab,
64 slabBottomBlock = blocksMap[dto.bottomSlabBlock] as? Block.Slab
65 )
66 } catch (e: Exception) {
67 Gdx.app.error(TAG, "Failed to map item $key. Reason: ${e.message}")
68 e.printStackTrace()
69 }
70 }
72 fallbackItem = itemsMap[FALLBACK_ITEM_KEY]
73 ?: throw IllegalArgumentException("Fallback item key '$FALLBACK_ITEM_KEY' not found")
74 }
76 private fun loadCraftingRecipes() {
77 val jsonString = assetLoader.getAssetHandle("json/crafting.json").readString()
78 val jsonMap = JsonFormat.decodeFromString<Map<String, CraftingDto>>(jsonString)
80 if (jsonMap.isNotEmpty() && itemsMap.isEmpty()) {
81 throw IllegalStateException("items should be loaded before crafting")
82 }
84 jsonMap.forEach { (key, value) ->
85 craftingRecipes += CraftingRecipe(
86 input = value.input.map(::getItem),
87 output = CraftingResult(getItem(key), value.count)
88 )
89 }
90 }
92 fun initialize() {
93 if (_initialized) {
94 Gdx.app.debug(TAG, "Attempted to init when already initialized")
95 return
96 }
98 val jsonString = assetLoader.getAssetHandle("json/game_items.json").readString()
99 val gameItemsDto = JsonFormat.decodeFromString<GameItemsDto>(jsonString)
101 loadBlocks(gameItemsDto.blocks)
102 loadItems(gameItemsDto.items)
104 _initialized = true
106 loadCraftingRecipes()
109 private fun <T> Map<String, T>.getOrFallback(key: String, fallback: T, lazyErrorMessage: () -> String): T {
110 if (!_initialized) {
111 throw IllegalStateException("GameItemsHolder was not initialized before use")
114 val t = this[key] ?: run {
115 Gdx.app.error(TAG, lazyErrorMessage.invoke())
116 return fallback
118 return t
121 fun getBlock(key: String): Block {
122 return blocksMap.getOrFallback(key, fallbackBlock) {
123 "No block with key '$key' found. Returning $FALLBACK_BLOCK_KEY"
127 fun getItem(key: String): Item {
128 return itemsMap.getOrFallback(key, fallbackItem) {
129 "No item with key '$key' found. Returning $FALLBACK_BLOCK_KEY"
133 fun craftItem(input: List<Item>): InventoryItem? {
134 return try {
135 craftingRecipes.first { rec -> rec.input == input}.output.toInventoryItem()
136 } catch (e: NoSuchElementException) {
137 null
141 fun getAllItems(): Collection<Item> {
142 return itemsMap.values
145 fun getItemFromCreativeInventory(position: Int): Item {
146 return if (position in itemsMap.values.indices) {
147 itemsMap.values.elementAt(position)
148 } else {
149 fallbackItem
153 fun getMaxCreativeScrollAmount(): Int = itemsMap.size / 8
155 fun <T : Block> getBlocksByType(type: Class<T>): List<T> {
156 return blocksMap.values.filterIsInstance(type)
159 companion object {
160 private const val TAG = "GameItemsHolder"
162 private val JsonFormat = Json { ignoreUnknownKeys = true }
164 const val FALLBACK_BLOCK_KEY = "none"
165 const val FALLBACK_ITEM_KEY = "none"