DEADSOFTWARE

Add crafting
authorfredboy <fredboy@protonmail.com>
Wed, 24 Apr 2024 15:48:49 +0000 (22:48 +0700)
committerfredboy <fredboy@protonmail.com>
Wed, 24 Apr 2024 15:48:49 +0000 (22:48 +0700)
37 files changed:
android/assets/crafting_table.png [new file with mode: 0644]
android/assets/json/crafting.json [new file with mode: 0644]
android/assets/json/game_items.json
android/assets/json/texture_regions.json
android/assets/textures/items/stick.png [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/MainConfig.java
core/src/ru/deadsoftware/cavedroid/game/GameItemsHolder.kt
core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java
core/src/ru/deadsoftware/cavedroid/game/input/KeyboardInputHandlersModule.kt
core/src/ru/deadsoftware/cavedroid/game/input/MouseInputHandlersModule.kt
core/src/ru/deadsoftware/cavedroid/game/input/action/keys/KeyboardInputActionKey.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/CloseGameWindowKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenInventoryKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CloseGameWindowMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCraftingInventoryItemMouseInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCreativeInventoryItemMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectSurvivalInventoryItemMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/mapper/KeyboardInputActionMapper.kt
core/src/ru/deadsoftware/cavedroid/game/mobs/Player.java
core/src/ru/deadsoftware/cavedroid/game/model/craft/CraftingRecipe.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/model/dto/CraftingDto.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/model/dto/ItemDto.kt
core/src/ru/deadsoftware/cavedroid/game/model/item/InventoryItem.kt
core/src/ru/deadsoftware/cavedroid/game/model/item/Item.kt
core/src/ru/deadsoftware/cavedroid/game/model/mapper/ItemMapper.kt
core/src/ru/deadsoftware/cavedroid/game/objects/DropController.java
core/src/ru/deadsoftware/cavedroid/game/render/DebugRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/WindowsRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/windows/CraftingWindowRenderer.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/render/windows/SurvivalWindowRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsConfigs.kt
core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsManager.kt
core/src/ru/deadsoftware/cavedroid/game/windows/inventory/AbstractInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CraftingInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CreativeInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/windows/inventory/SurvivalInventoryWindow.kt [new file with mode: 0644]

diff --git a/android/assets/crafting_table.png b/android/assets/crafting_table.png
new file mode 100644 (file)
index 0000000..e019e39
Binary files /dev/null and b/android/assets/crafting_table.png differ
diff --git a/android/assets/json/crafting.json b/android/assets/json/crafting.json
new file mode 100644 (file)
index 0000000..6f651bb
--- /dev/null
@@ -0,0 +1,42 @@
+{
+  "planks_oak": {
+    "input": ["log_oak", "none", "none", "none", "none", "none", "none", "none", "none"],
+    "count": 4
+  },
+  "stick": {
+    "input": ["planks_oak", "none", "none", "planks_oak", "none", "none", "none", "none", "none"],
+    "count": 4
+  },
+  "wood_pickaxe": {
+    "input": ["planks_oak", "planks_oak", "planks_oak", "none", "stick", "none", "none", "stick", "none"],
+    "count": 59
+  },
+  "wood_axe": {
+    "input": ["planks_oak", "planks_oak", "none", "planks_oak", "stick", "none", "none", "stick", "none"],
+    "count": 60
+  },
+  "wood_sword": {
+    "input": ["none", "planks_oak", "none", "none", "planks_oak", "none", "none", "stick", "none"],
+    "count": 60
+  },
+  "wood_shovel": {
+    "input": ["none", "planks_oak", "none", "none", "stick", "none", "none", "stick", "none"],
+    "count": 59
+  },
+  "stone_pickaxe": {
+    "input": ["cobblestone", "cobblestone", "cobblestone", "none", "stick", "none", "none", "stick", "none"],
+    "count": 131
+  },
+  "stone_axe": {
+    "input": ["cobblestone", "cobblestone", "none", "cobblestone", "stick", "none", "none", "stick", "none"],
+    "count": 131
+  },
+  "stone_sword": {
+    "input": ["none", "cobblestone", "none", "none", "cobblestone", "none", "none", "stick", "none"],
+    "count": 132
+  },
+  "stone_shovel": {
+    "input": ["none", "cobblestone", "none", "none", "stick", "none", "none", "stick", "none"],
+    "count": 131
+  }
+}
\ No newline at end of file
index 6db046714ee2056a90bfddb8210dc5b6f08e6750..d6c3b441a3fc0cff5672e08a10985e8705bfbcd0 100644 (file)
       "type": "block",
       "texture": "obsidian"
     },
+    "stick": {
+      "name": "Stick",
+      "texture": "stick"
+    },
     "wood_sword": {
       "name": "Wooden Sword",
       "type": "sword",
index 2b73d8f127f8f3ae0295ea44fdba01f8d5b12283..800098081fedec8efe5483a3ba2af7803c742a71 100644 (file)
       "h": 166
     }
   },
+  "crafting_table": {
+    "crafting_table": {
+      "w": 176,
+      "h": 166
+    }
+  },
   "buttons": {
     "button_0": {
       "w": 200,
diff --git a/android/assets/textures/items/stick.png b/android/assets/textures/items/stick.png
new file mode 100644 (file)
index 0000000..81c915e
Binary files /dev/null and b/android/assets/textures/items/stick.png differ
index ff077ee8a64933dc9e1346efbecf6cbccf4e23f5..6577f297254f82928b2e18d28f29911d32764750 100644 (file)
@@ -53,10 +53,6 @@ public class MainConfig {
         return mGameUiWindow == gameUiWindow;
     }
 
-    public GameUiWindow getGameUiWindow() {
-        return mGameUiWindow;
-    }
-
     public void setGameUiWindow(GameUiWindow gameUiWindow) {
         mGameUiWindow = gameUiWindow;
     }
index 1fbcd3dbfe8313c8beaccd390b10b89041c01518..832845eb0c11f60f2ae6168661d1a99961b28c37 100644 (file)
@@ -3,13 +3,18 @@ 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
@@ -23,6 +28,7 @@ class GameItemsHolder @Inject constructor(
 
     private val blocksMap = LinkedHashMap<String, Block>()
     private val itemsMap = LinkedHashMap<String, Item>()
+    private val craftingRecipes = LinkedList<CraftingRecipe>()
 
     lateinit var fallbackBlock: Block
         private set
@@ -67,6 +73,22 @@ class GameItemsHolder @Inject constructor(
             ?: throw IllegalArgumentException("Fallback item key '$FALLBACK_ITEM_KEY' not found")
     }
 
+    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(::getItem),
+                output = CraftingResult(getItem(key), value.count)
+            )
+        }
+    }
+
     fun initialize() {
         if (_initialized) {
             Gdx.app.debug(TAG, "Attempted to init when already initialized")
@@ -80,6 +102,8 @@ class GameItemsHolder @Inject constructor(
         loadItems(gameItemsDto.items)
 
         _initialized = true
+
+        loadCraftingRecipes()
     }
 
     private fun <T> Map<String, T>.getOrFallback(key: String, fallback: T, lazyErrorMessage: () -> String): T {
@@ -106,6 +130,14 @@ class GameItemsHolder @Inject constructor(
         }
     }
 
+    fun craftItem(input: List<Item>): InventoryItem? {
+        return  try {
+            craftingRecipes.first { rec -> rec.input == input}.output.toInventoryItem()
+        } catch (e: NoSuchElementException) {
+            null
+        }
+    }
+
     fun getAllItems(): Collection<Item> {
         return itemsMap.values
     }
index 8118fec05abe59756d90d568f06851b4e7243c45..e96f3af48e3936fb31ccb99ecfac36a8b67d6774 100644 (file)
@@ -138,7 +138,7 @@ public class GameRenderer extends Renderer {
     }
 
     private TouchButton getTouchedKey(float touchX, float touchY) {
-        if (mGameWindowsManager.getCurrentWindow() != GameUiWindow.NONE) {
+        if (mGameWindowsManager.getCurrentWindowType() != GameUiWindow.NONE) {
             return nullButton;
         }
         for (ObjectMap.Entry<String, TouchButton> entry : Assets.guiMap) {
index 8c5502c631374fc5baa8e732deff1b7a3b3211e6..f590398c30db185354b2ae2738c664649be7e4fa 100644 (file)
@@ -108,4 +108,11 @@ object KeyboardInputHandlersModule {
         return handler
     }
 
+    @Binds
+    @IntoSet
+    @GameScope
+    fun bindOpenCraftingKeyboardInputHandler(handler: OpenCraftingKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
+        return handler
+    }
+
 }
\ No newline at end of file
index aef4c0bca680d01f3624114a19bf6e92ee5ce3df..937b3e68d921e6d4dfd831c23dd7ca27439e26b3 100644 (file)
@@ -65,4 +65,11 @@ object MouseInputHandlersModule {
     fun bindSelectSurvivalInventoryItemMouseInputHandler(handler: SelectSurvivalInventoryItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
         return handler
     }
+
+    @Binds
+    @IntoSet
+    @GameScope
+    fun bindSelectCraftingInventoryItemMouseInputHandler(handler: SelectCraftingInventoryItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
+        return handler
+    }
 }
\ No newline at end of file
index b3006a6e13ba4fb620acd131181b0eaeaed7e74c..fb59efbf4dae71cc79b726ca51f28717ee66c655 100644 (file)
@@ -20,4 +20,5 @@ sealed interface KeyboardInputActionKey {
     data object SpawnPig : KeyboardInputActionKey
     data object SwitchGameMode : KeyboardInputActionKey
     data object ShowMap : KeyboardInputActionKey
+    data object OpenCraft : KeyboardInputActionKey
 }
\ No newline at end of file
index 0111949d495635c4ac0c5fd79483fbdbfe0a8cbb..705f9aa0e71a5ae60a64c8ac1e411c8cbf511a45 100644 (file)
@@ -5,12 +5,16 @@ import ru.deadsoftware.cavedroid.game.GameUiWindow
 import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
 import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.objects.DropController
 import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
 import javax.inject.Inject
 
 @GameScope
 class CloseGameWindowKeyboardInputHandler @Inject constructor(
-    private val gameWindowsManager: GameWindowsManager
+    private val gameWindowsManager: GameWindowsManager,
+    private val mobsController: MobsController,
+    private val dropController: DropController,
 ) : IGameInputHandler<KeyboardInputAction> {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
@@ -19,6 +23,17 @@ class CloseGameWindowKeyboardInputHandler @Inject constructor(
     }
 
     override fun handle(action: KeyboardInputAction) {
+        val selectedItem = gameWindowsManager.currentWindow?.selectedItem
+        if (selectedItem != null) {
+            for (i in 1 .. selectedItem.amount) {
+                dropController.addDrop(
+                    /* x = */ mobsController.player.x + (32f * mobsController.player.direction.basis),
+                    /* y = */ mobsController.player.y,
+                    /* item = */ selectedItem.item
+                )
+            }
+            gameWindowsManager.currentWindow?.selectedItem = null
+        }
         gameWindowsManager.closeWindow()
     }
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt
new file mode 100644 (file)
index 0000000..d2e333c
--- /dev/null
@@ -0,0 +1,26 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.objects.DropController
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import javax.inject.Inject
+
+@GameScope
+class OpenCraftingKeyboardInputHandler @Inject constructor(
+    private val gameWindowsManager: GameWindowsManager,
+) : IGameInputHandler<KeyboardInputAction> {
+
+    override fun checkConditions(action: KeyboardInputAction): Boolean {
+        return action.actionKey is KeyboardInputActionKey.OpenCraft &&
+                action.isKeyDown && gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
+    }
+
+    override fun handle(action: KeyboardInputAction) {
+        gameWindowsManager.openCrafting()
+    }
+}
\ No newline at end of file
index f04c993147d5776091b90bc23b3c506100df6b22..0e2c11bd17a0adce97b6853cbe37965891b11fb0 100644 (file)
@@ -5,12 +5,14 @@ import ru.deadsoftware.cavedroid.game.GameUiWindow
 import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
 import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.objects.DropController
 import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
 import javax.inject.Inject
 
 @GameScope
 class OpenInventoryKeyboardInputHandler @Inject constructor(
-    private val gameWindowsManager: GameWindowsManager
+    private val gameWindowsManager: GameWindowsManager,
 ) : IGameInputHandler<KeyboardInputAction> {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
index 1f76039c78fe1575c5fcf6c878e1f69d68cfb30d..e80ff476b00fdfbb02c1c7623ddb2b8969b3ee6a 100644 (file)
@@ -8,16 +8,21 @@ import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
 import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
 import ru.deadsoftware.cavedroid.game.input.isInsideWindow
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.objects.DropController
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 
 @GameScope
 class CloseGameWindowMouseInputHandler @Inject constructor(
     private val gameWindowsManager: GameWindowsManager,
+    private val mobsController: MobsController,
+    private val dropController: DropController,
 ) : IGameInputHandler<MouseInputAction> {
 
     private val creativeInventoryTexture get() = requireNotNull(Assets.textureRegions["creative"])
     private val survivalInventoryTexture get() = requireNotNull(Assets.textureRegions["survival"])
+    private val craftingInventoryTexture get() = requireNotNull(Assets.textureRegions["crafting_table"])
 
     override fun checkConditions(action: MouseInputAction): Boolean {
         return gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE &&
@@ -30,11 +35,23 @@ class CloseGameWindowMouseInputHandler @Inject constructor(
         return when (val window = gameWindowsManager.getCurrentWindow()) {
             GameUiWindow.CREATIVE_INVENTORY -> creativeInventoryTexture
             GameUiWindow.SURVIVAL_INVENTORY -> survivalInventoryTexture
+            GameUiWindow.CRAFTING_TABLE -> craftingInventoryTexture
             else -> throw UnsupportedOperationException("Cant close window ${window.name}")
         }
     }
 
     override fun handle(action: MouseInputAction) {
+        val selectedItem = gameWindowsManager.currentWindow?.selectedItem
+        if (selectedItem != null) {
+            for (i in 1 .. selectedItem.amount) {
+                dropController.addDrop(
+                    /* x = */ mobsController.player.x + (32f * mobsController.player.direction.basis),
+                    /* y = */ mobsController.player.y,
+                    /* item = */ selectedItem.item
+                )
+            }
+            gameWindowsManager.currentWindow?.selectedItem = null
+        }
         gameWindowsManager.closeWindow()
     }
 
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCraftingInventoryItemMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCraftingInventoryItemMouseInputHandler.kt
new file mode 100644 (file)
index 0000000..7005a67
--- /dev/null
@@ -0,0 +1,161 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+import ru.deadsoftware.cavedroid.game.input.isInsideWindow
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.windows.inventory.CraftingInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class SelectCraftingInventoryItemMouseInputHandler @Inject constructor(
+    private val gameWindowsManager: GameWindowsManager,
+    private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
+) : IGameInputHandler<MouseInputAction> {
+
+    private val survivalWindowTexture get() = requireNotNull(Assets.textureRegions["survival"])
+
+    override fun checkConditions(action: MouseInputAction): Boolean {
+        return gameWindowsManager.getCurrentWindow() == GameUiWindow.CRAFTING_TABLE &&
+                isInsideWindow(action, survivalWindowTexture) &&
+                (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Right || action.actionKey is MouseInputActionKey.Touch)
+                && action.actionKey.touchUp
+    }
+
+    private fun onLeftCLick(items: MutableList<InventoryItem?>, window: CraftingInventoryWindow, index: Int) {
+        val selectedItem = window.selectedItem
+        val clickedItem = items[index]
+
+        if (clickedItem != null && selectedItem != null && items[index]!!.item == selectedItem.item &&
+            items[index]!!.amount + selectedItem.amount <= selectedItem.item.params.maxStack) {
+            items[index]!!.amount += selectedItem.amount
+            window.selectedItem = null
+            return
+        }
+
+        val item = items[index]
+        items[index] = selectedItem ?: gameItemsHolder.fallbackItem.toInventoryItem()
+        window.selectedItem = item
+    }
+
+    private fun onRightClick(items: MutableList<InventoryItem?>, window: CraftingInventoryWindow, index: Int) {
+        val clickedItem = items[index]
+        val selectedItem = window.selectedItem
+            ?.takeIf { clickedItem == null || clickedItem.item.isNone() || it.item == items[index]!!.item && items[index]!!.amount + 1 < it.item.params.maxStack }
+            ?: return
+
+        val newItem = selectedItem.item.toInventoryItem((clickedItem?.takeIf { !it.item.isNone() }?.amount ?: 0) + 1)
+        items[index] = newItem
+        selectedItem.amount --
+
+        if (selectedItem.amount <= 0) {
+            window.selectedItem = null
+        }
+    }
+
+    private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
+        val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
+
+        var itemIndex = ((xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Crafting.itemsInRow))
+        itemIndex += GameWindowsConfigs.Crafting.hotbarCells
+
+        if (itemIndex >= 36) {
+            itemIndex -= 36
+        }
+
+        if (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) {
+            onLeftCLick(mobsController.player.inventory, window, itemIndex)
+        } else {
+            onRightClick(mobsController.player.inventory, window, itemIndex)
+        }
+
+        Gdx.app.debug(
+            TAG,
+            "selected item: ${window.selectedItem?.item?.params?.key ?: "null"}; index $itemIndex, grid ($xOnGrid;$yOnGrid)"
+        )
+    }
+
+    private fun handleInsideCraft(action: MouseInputAction, xOnCraft: Int, yOnCraft: Int) {
+        val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
+        val index = xOnCraft + yOnCraft * GameWindowsConfigs.Crafting.craftGridSize
+
+        if (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) {
+            onLeftCLick(window.craftingItems, window, index)
+        } else {
+            onRightClick(window.craftingItems, window, index)
+        }
+
+        window.craftResult =
+            gameItemsHolder.craftItem(window.craftingItems.map { it?.item ?: gameItemsHolder.fallbackItem })
+    }
+
+    override fun handle(action: MouseInputAction) {
+        val survivalTexture = survivalWindowTexture
+        val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
+
+        val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - survivalTexture.regionWidth / 2)
+        val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - survivalTexture.regionHeight / 2)
+
+        val xOnGrid = (xOnWindow - GameWindowsConfigs.Crafting.itemsGridMarginLeft) /
+                GameWindowsConfigs.Crafting.itemsGridColWidth
+        val yOnGrid = (yOnWindow - GameWindowsConfigs.Crafting.itemsGridMarginTop) /
+                GameWindowsConfigs.Crafting.itemsGridRowHeight
+
+        val xOnCraft = (xOnWindow - GameWindowsConfigs.Crafting.craftOffsetX) /
+                GameWindowsConfigs.Crafting.itemsGridColWidth
+        val yOnCraft = (yOnWindow - GameWindowsConfigs.Crafting.craftOffsetY) /
+                GameWindowsConfigs.Crafting.itemsGridRowHeight
+
+        val isInsideInventoryGrid = xOnGrid >= 0 && xOnGrid < GameWindowsConfigs.Crafting.itemsInRow &&
+                yOnGrid >= 0 && yOnGrid < GameWindowsConfigs.Crafting.itemsInCol
+
+        val isInsideCraftGrid = xOnCraft >= 0 && xOnCraft < GameWindowsConfigs.Crafting.craftGridSize &&
+                yOnCraft >= 0 && yOnCraft < GameWindowsConfigs.Crafting.craftGridSize
+
+        val isInsideCraftResult = xOnWindow > GameWindowsConfigs.Crafting.craftResultOffsetX &&
+                xOnWindow < GameWindowsConfigs.Crafting.craftResultOffsetX + GameWindowsConfigs.Crafting.itemsGridColWidth &&
+                yOnWindow > GameWindowsConfigs.Crafting.craftResultOffsetY &&
+                yOnWindow < GameWindowsConfigs.Crafting.craftResultOffsetY + GameWindowsConfigs.Crafting.itemsGridRowHeight
+
+        if (isInsideInventoryGrid) {
+            handleInsideInventoryGrid(action, xOnGrid.toInt(), yOnGrid.toInt())
+        } else if (isInsideCraftGrid) {
+            handleInsideCraft(action, xOnCraft.toInt(), yOnCraft.toInt())
+        } else if (isInsideCraftResult) {
+            val selectedItem = window.selectedItem
+            if (selectedItem == null || selectedItem.item.isNone() ||
+                (selectedItem.item == window.craftResult?.item && selectedItem.amount + (window.craftResult?.amount ?: 0) <= selectedItem.item.params.maxStack)) {
+                for (i in window.craftingItems.indices) {
+                    if ((window.craftingItems[i]?.amount ?: 0) > 1) {
+                        window.craftingItems[i]?.amount = window.craftingItems[i]?.amount!! - 1
+                    } else {
+                        window.craftingItems[i] = null
+                    }
+                }
+                if (selectedItem != null && !selectedItem.item.isNone()) {
+                    selectedItem.amount += (window.craftResult?.amount ?: 0)
+                } else {
+                    window.selectedItem = window.craftResult
+                }
+                window.craftResult = gameItemsHolder.craftItem(window.craftingItems
+                    .map { it?.item ?: gameItemsHolder.fallbackItem })
+            }
+        }
+
+    }
+
+    companion object {
+        private const val TAG = "SelectCraftingInventoryItemMouseInputHandler"
+
+    }
+}
\ No newline at end of file
index e6fb732c2fb118e83ab118a548e76edcf7b8c50e..7c587b67b7162415ecceeb9525ce568fcdb2831c 100644 (file)
@@ -45,16 +45,14 @@ class SelectCreativeInventoryItemMouseInputHandler @Inject constructor(
 
         val itemIndex = (gameWindowsManager.creativeScrollAmount * GameWindowsConfigs.Creative.itemsInRow +
                 (xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Creative.itemsInRow))
-
-        mobsController.player.inventory.copyInto(
-            destination = mobsController.player.inventory,
-            destinationOffset = 1,
-            startIndex = 0,
-            endIndex = mobsController.player.inventory.size - 1
-        )
-
         val item = gameItemsHolder.getItemFromCreativeInventory(itemIndex)
-        mobsController.player.inventory[0] = item.toInventoryItem(amount = item.params.maxStack)
+        mobsController.player.inventory.reverse()
+        mobsController.player.inventory.add(item.toInventoryItem(amount = item.params.maxStack))
+        mobsController.player.inventory.reverse()
+
+        if (mobsController.player.inventory.size > 36) {
+            mobsController.player.inventory.dropLast(mobsController.player.inventory.size - 36)
+        }
     }
 
 }
\ No newline at end of file
index 559ccfc79388a09157838d3238f0326709764f24..61e1b18892e8970a8f65f79d52c841eea5e9088b 100644 (file)
@@ -9,8 +9,10 @@ import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
 import ru.deadsoftware.cavedroid.game.input.isInsideWindow
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
 import ru.deadsoftware.cavedroid.game.windows.GameWindowsConfigs
 import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.windows.inventory.SurvivalInventoryWindow
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 
@@ -25,37 +27,131 @@ class SelectSurvivalInventoryItemMouseInputHandler @Inject constructor(
 
     override fun checkConditions(action: MouseInputAction): Boolean {
         return gameWindowsManager.getCurrentWindow() == GameUiWindow.SURVIVAL_INVENTORY &&
-            isInsideWindow(action, survivalWindowTexture) &&
-                (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch)
+                isInsideWindow(action, survivalWindowTexture) &&
+                (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Right || action.actionKey is MouseInputActionKey.Touch)
                 && action.actionKey.touchUp
     }
 
-    override fun handle(action: MouseInputAction) {
-        val survivalTexture = survivalWindowTexture
-        val xOnGrid = (action.screenX - (action.cameraViewport.width / 2 - survivalTexture.regionWidth / 2 +
-                GameWindowsConfigs.Survival.itemsGridMarginLeft)) /
-                GameWindowsConfigs.Survival.itemsGridColWidth
-        val yOnGrid = (action.screenY - (action.cameraViewport.height / 2 - survivalTexture.regionHeight / 2 +
-                GameWindowsConfigs.Survival.itemsGridMarginTop)) /
-                GameWindowsConfigs.Survival.itemsGridRowHeight
+    private fun onLeftCLick(items: MutableList<InventoryItem?>, window: SurvivalInventoryWindow, index: Int) {
+        val selectedItem = window.selectedItem
+        val clickedItem = items[index]
 
-        if (xOnGrid < 0 || xOnGrid >= GameWindowsConfigs.Survival.itemsInRow ||
-            yOnGrid < 0 || yOnGrid > GameWindowsConfigs.Survival.itemsInCol) {
+        if (clickedItem != null && selectedItem != null && items[index]!!.item == selectedItem.item &&
+            items[index]!!.amount + selectedItem.amount <= selectedItem.item.params.maxStack) {
+            items[index]!!.amount += selectedItem.amount
+            window.selectedItem = null
             return
         }
 
+        val item = items[index]
+        items[index] = selectedItem ?: gameItemsHolder.fallbackItem.toInventoryItem()
+        window.selectedItem = item
+    }
+
+    private fun onRightClick(items: MutableList<InventoryItem?>, window: SurvivalInventoryWindow, index: Int) {
+        val clickedItem = items[index]
+        val selectedItem = window.selectedItem
+            ?.takeIf { clickedItem == null || clickedItem.item.isNone() || it.item == items[index]!!.item && items[index]!!.amount + 1 < it.item.params.maxStack }
+            ?: return
+
+        val newItem = selectedItem.item.toInventoryItem((clickedItem?.takeIf { !it.item.isNone() }?.amount ?: 0) + 1)
+        items[index] = newItem
+        selectedItem.amount --
+
+        if (selectedItem.amount <= 0) {
+            window.selectedItem = null
+        }
+    }
+
+    private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
+        val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
+
         var itemIndex = ((xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Survival.itemsInRow))
         itemIndex += GameWindowsConfigs.Survival.hotbarCells
 
-        if (itemIndex >= mobsController.player.inventory.size) {
-            itemIndex -= mobsController.player.inventory.size
+        if (itemIndex >= 36) {
+            itemIndex -= 36
+        }
+
+        if (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) {
+            onLeftCLick(mobsController.player.inventory, window, itemIndex)
+        } else {
+            onRightClick(mobsController.player.inventory, window, itemIndex)
         }
 
-        val item = mobsController.player.inventory[itemIndex]
-        mobsController.player.inventory[itemIndex] = gameWindowsManager.selectedItem ?: gameItemsHolder.fallbackItem.toInventoryItem()
-        gameWindowsManager.selectedItem = item
+        Gdx.app.debug(
+            TAG,
+            "selected item: ${window.selectedItem?.item?.params?.key ?: "null"}; index $itemIndex, grid ($xOnGrid;$yOnGrid)"
+        )
+    }
+
+    private fun handleInsideCraft(action: MouseInputAction, xOnCraft: Int, yOnCraft: Int) {
+        val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
+        val index = xOnCraft + yOnCraft * GameWindowsConfigs.Crafting.craftGridSize // this is crafting on purpose!!
+
+        if (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) {
+            onLeftCLick(window.craftingItems, window, index)
+        } else {
+            onRightClick(window.craftingItems, window, index)
+        }
+
+        window.craftResult =
+            gameItemsHolder.craftItem(window.craftingItems.map { it?.item ?: gameItemsHolder.fallbackItem })
+    }
+
+    override fun handle(action: MouseInputAction) {
+        val survivalTexture = survivalWindowTexture
+        val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
+
+        val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - survivalTexture.regionWidth / 2)
+        val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - survivalTexture.regionHeight / 2)
+
+        val xOnGrid = (xOnWindow - GameWindowsConfigs.Survival.itemsGridMarginLeft) /
+                GameWindowsConfigs.Survival.itemsGridColWidth
+        val yOnGrid = (yOnWindow - GameWindowsConfigs.Survival.itemsGridMarginTop) /
+                GameWindowsConfigs.Survival.itemsGridRowHeight
+
+        val xOnCraft = (xOnWindow - GameWindowsConfigs.Survival.craftOffsetX) /
+                GameWindowsConfigs.Survival.itemsGridColWidth
+        val yOnCraft = (yOnWindow - GameWindowsConfigs.Survival.craftOffsetY) /
+                GameWindowsConfigs.Survival.itemsGridRowHeight
+
+        val isInsideInventoryGrid = xOnGrid >= 0 && xOnGrid < GameWindowsConfigs.Survival.itemsInRow &&
+                yOnGrid >= 0 && yOnGrid < GameWindowsConfigs.Survival.itemsInCol
+
+        val isInsideCraftGrid = xOnCraft >= 0 && xOnCraft < GameWindowsConfigs.Survival.craftGridSize &&
+                yOnCraft >= 0 && yOnCraft < GameWindowsConfigs.Survival.craftGridSize
+
+        val isInsideCraftResult = xOnWindow > GameWindowsConfigs.Survival.craftResultOffsetX &&
+                xOnWindow < GameWindowsConfigs.Survival.craftResultOffsetX + GameWindowsConfigs.Survival.itemsGridColWidth &&
+                yOnWindow > GameWindowsConfigs.Survival.craftResultOffsetY &&
+                yOnWindow < GameWindowsConfigs.Survival.craftResultOffsetY + GameWindowsConfigs.Survival.itemsGridRowHeight
+
+        if (isInsideInventoryGrid) {
+            handleInsideInventoryGrid(action, xOnGrid.toInt(), yOnGrid.toInt())
+        } else if (isInsideCraftGrid) {
+            handleInsideCraft(action, xOnCraft.toInt(), yOnCraft.toInt())
+        } else if (isInsideCraftResult) {
+            val selectedItem = window.selectedItem
+            if (selectedItem == null || selectedItem.item.isNone() ||
+                (selectedItem.item == window.craftResult?.item && selectedItem.amount + (window.craftResult?.amount ?: 0) <= selectedItem.item.params.maxStack)) {
+                for (i in window.craftingItems.indices) {
+                    if ((window.craftingItems[i]?.amount ?: 0) > 1) {
+                        window.craftingItems[i]?.amount = window.craftingItems[i]?.amount!! - 1
+                    } else {
+                        window.craftingItems[i] = null
+                    }
+                }
+                if (selectedItem != null && !selectedItem.item.isNone()) {
+                    selectedItem.amount += (window.craftResult?.amount ?: 0)
+                } else {
+                    window.selectedItem = window.craftResult
+                }
+                window.craftResult = gameItemsHolder.craftItem(window.craftingItems
+                    .map { it?.item ?: gameItemsHolder.fallbackItem })
+            }
+        }
 
-        Gdx.app.debug(TAG, "selected item: ${gameWindowsManager.selectedItem?.item?.params?.key ?: "null"}; index $itemIndex, grid ($xOnGrid;$yOnGrid)")
     }
 
     companion object {
index fefd4ad4a228c9b7f304ef689ee27163db455fdb..ca91f486951fdf0b737ede5f84d8fbd3e92df3dd 100644 (file)
@@ -25,6 +25,8 @@ class KeyboardInputActionMapper @Inject constructor() {
             Input.Keys.GRAVE -> KeyboardInputActionKey.SwitchGameMode
             Input.Keys.M -> KeyboardInputActionKey.ShowMap
 
+            Input.Keys.T -> KeyboardInputActionKey.OpenCraft
+
             else -> null
         }
 
index b369f1ccc7f37613d9b90ec0d863a5530a7a7563..0e8af6e0eed0407b1cc3a4be057ae2dc344f5b87 100644 (file)
@@ -15,6 +15,7 @@ import ru.deadsoftware.cavedroid.misc.utils.SpriteOrigin;
 import ru.deadsoftware.cavedroid.misc.utils.SpriteUtilsKt;
 
 import javax.annotation.CheckForNull;
+import java.util.ArrayList;
 
 public class Player extends Mob {
 
@@ -26,7 +27,7 @@ public class Player extends Mob {
     private float hitAnim = 0f;
     private float hitAnimDelta = ANIMATION_SPEED;
 
-    public final InventoryItem[] inventory;
+    public final ArrayList<InventoryItem> inventory;
     public int slot;
     public int gameMode;
     public boolean swim;
@@ -48,9 +49,9 @@ public class Player extends Mob {
 
     public Player(GameItemsHolder gameItemsHolder) {
         super(0, 0, 4, 30, randomDir(), Type.MOB, MAX_HEALTH);
-        inventory = new InventoryItem[36];
-        for (int i = 0; i < inventory.length; i++) {
-            inventory[i] = gameItemsHolder.getFallbackItem().toInventoryItem();
+        inventory = new ArrayList<>(36);
+        for (int i = 0; i < 36; i++) {
+            inventory.add(gameItemsHolder.getFallbackItem().toInventoryItem());
         }
         swim = false;
     }
@@ -63,7 +64,7 @@ public class Player extends Mob {
 
     @CheckForNull
     public Item inventory(int i) {
-        return inventory[i].getItem();
+        return inventory.get(i).getItem();
     }
     
     public void respawn(GameWorld gameWorld, GameItemsHolder itemsHolder) {
@@ -86,7 +87,7 @@ public class Player extends Mob {
     }
 
     public InventoryItem getCurrentItem() {
-        return inventory[slot];
+        return inventory.get(slot);
     }
 
     public void pickUpDrop(Drop drop) {
@@ -100,9 +101,9 @@ public class Player extends Mob {
             }
         }
 
-        for (int i = 0; i < inventory.length; i++) {
+        for (int i = 0; i < inventory.size(); i++) {
             if (inventory(i) == null || inventory(i).getParams().getKey().equals(GameItemsHolder.FALLBACK_ITEM_KEY)) {
-                inventory[i] = drop.getItem().toInventoryItem();
+                inventory.set(i, drop.getItem().toInventoryItem());
                 drop.setPickedUp(true);
                 break;
             }
@@ -143,7 +144,7 @@ public class Player extends Mob {
     }
 
     public void setCurrentInventorySlotItem(Item item) {
-        inventory[slot] = item.toInventoryItem();
+        inventory.set(slot, item.toInventoryItem());
     }
 
     @Override
@@ -211,7 +212,7 @@ public class Player extends Mob {
         final boolean canHitBlock = target != null;
 
         float multiplier = 1f;
-        final Item currentItem = inventory[slot].getItem();
+        final Item currentItem = inventory.get(slot).getItem();
         if (currentItem instanceof Item.Tool && canHitBlock) {
             if (target.getParams().getToolType() == currentItem.getClass()
                     && ((Item.Tool)currentItem).getLevel() >= target.getParams().getToolLevel()) {
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/craft/CraftingRecipe.kt b/core/src/ru/deadsoftware/cavedroid/game/model/craft/CraftingRecipe.kt
new file mode 100644 (file)
index 0000000..eebe13f
--- /dev/null
@@ -0,0 +1,16 @@
+package ru.deadsoftware.cavedroid.game.model.craft
+
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.model.item.Item
+
+data class CraftingRecipe(
+    val input: List<Item>,
+    val output: CraftingResult
+)
+
+data class CraftingResult(
+    val item: Item,
+    val amount: Int,
+) {
+    fun toInventoryItem() = InventoryItem(item, amount)
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/dto/CraftingDto.kt b/core/src/ru/deadsoftware/cavedroid/game/model/dto/CraftingDto.kt
new file mode 100644 (file)
index 0000000..469602a
--- /dev/null
@@ -0,0 +1,9 @@
+package ru.deadsoftware.cavedroid.game.model.dto
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class CraftingDto(
+    val input: List<String>,
+    val count: Int = 1,
+)
index 3573fa986d70718f44cbe385f5b782147060ff8d..fe28e6440adc30a52eadaab656d6bbb9306592f3 100644 (file)
@@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable
 data class ItemDto(
     @Deprecated("numeric ids will be removed") @SerialName("id") val id: Int? = null,
     @SerialName("name") val name: String,
-    @SerialName("type") val type: String,
+    @SerialName("type") val type: String = "normal",
     @SerialName("texture") val texture: String,
     @SerialName("origin_x") val originX: Float = 0f,
     @SerialName("origin_y") val origin_y: Float = 1f,
index 6cb2a9d85bf5232fb8e7b37750c336f7e0aa0f38..6c1598315cb5037813c8a385ddb06f5f062846ea 100644 (file)
@@ -42,11 +42,14 @@ class InventoryItem @JvmOverloads constructor(
         }
 
         val sprite = item.sprite
-        sprite.setOriginCenter()
-        sprite.setPosition(x, y)
-        sprite.setScale(1.25f)
-        sprite.draw(spriteBatch)
-        sprite.setScale(1f)
+        val amountString = amount.toString()
+        spriteBatch.drawSprite(sprite, x - 10f, y - 10f, rotation = 0f, width = 20f, height = 20f)
+        drawAmountText(
+            spriteBatch = spriteBatch,
+            text = amountString,
+            x = x + 10f - Assets.getStringWidth(amountString) + 1f,
+            y = y + 10f - Assets.getStringHeight(amountString) + 1f
+        )
     }
 
     fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, x: Float, y: Float) {
index 7eeb9403a88a636f843998a18b98188bbedaf2c0..1e6755ab7c367b8886301e3173c0664df038e3ec 100644 (file)
@@ -54,7 +54,12 @@ sealed class Item {
     fun toInventoryItem(amount: Int = 1): InventoryItem {
         return InventoryItem(this, amount)
     }
-    
+
+    data class Normal(
+        override val params: CommonItemParams,
+        override val sprite: Sprite
+    ) : Item()
+
     sealed class Tool : Item() {
         abstract val mobDamageMultiplier: Float
         abstract val blockDamageMultiplier: Float
index 4d6d5512276af372e5be34f0acacfd98e92dd708..b951f261e795b7937d40c0c6a6f3b08272e50d72 100644 (file)
@@ -22,6 +22,7 @@ class ItemMapper @Inject constructor(
         val params = mapCommonParams(key, dto)
 
         return when (dto.type) {
+            "normal" -> Normal(params, requireNotNull(loadSprite(dto)))
             "bucket" -> Bucket(params, requireNotNull(loadSprite(dto)), requireNotNull(dto.actionKey))
             "shovel" -> Shovel(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier, requireNotNull(dto.toolLevel))
             "sword" -> Sword(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier, requireNotNull(dto.toolLevel))
index 826fd6878fc950ed5911d95b9bdc5358a84d6ddc..798a84ce5b77415bbf88f87187c39d51e636ff04 100644 (file)
@@ -27,6 +27,9 @@ public class DropController implements Serializable {
     }
 
     public void addDrop(float x, float y, Item item) {
+        if (item.isNone()) {
+            return;
+        }
         mDrops.add(new Drop(x, y, item));
     }
 
index 8145324ee9b1ac88d8f10316645196bd845fe1d4..03f62534522264f008936a451faaa9d2139ea989 100644 (file)
@@ -89,15 +89,6 @@ class DebugRenderer @Inject constructor(
     override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
         if (mainConfig.isShowInfo) {
             drawDebugInfo(spriteBatch)
-
-            spriteBatch.end();
-            shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
-            forEachBlockInArea(mobsController.player) { x: Int, y: Int ->
-                shapeRenderer.color = Color.CYAN
-                shapeRenderer.rect(x.px - viewport.x, y.px - viewport.y, 16f, 16f)
-            }
-            shapeRenderer.end()
-            spriteBatch.begin()
         }
 
         if (mainConfig.isShowMap) {
index 10c211a5ce801577d6b3d14cc513e942200de977..2fa093f61719d8564637e5ce4e456b2e7153b201 100644 (file)
@@ -4,28 +4,31 @@ import com.badlogic.gdx.Gdx
 import com.badlogic.gdx.graphics.g2d.SpriteBatch
 import com.badlogic.gdx.graphics.glutils.ShapeRenderer
 import com.badlogic.gdx.math.Rectangle
-import ru.deadsoftware.cavedroid.MainConfig
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.render.windows.CraftingWindowRenderer
 import ru.deadsoftware.cavedroid.game.render.windows.CreativeWindowRenderer
 import ru.deadsoftware.cavedroid.game.render.windows.SurvivalWindowRenderer
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
 import javax.inject.Inject
 
 @GameScope
 class WindowsRenderer @Inject constructor(
-    private val mainConfig: MainConfig,
     private val creativeWindowRenderer: CreativeWindowRenderer,
     private val survivalWindowRenderer: SurvivalWindowRenderer,
+    private val craftingWindowRenderer: CraftingWindowRenderer,
+    private val gameWindowsManager: GameWindowsManager,
 ) : IGameRenderer {
 
     override val renderLayer get() = RENDER_LAYER
 
     override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
-        when (mainConfig.gameUiWindow) {
+        when (val windowType = gameWindowsManager.getCurrentWindow()) {
             GameUiWindow.CREATIVE_INVENTORY -> creativeWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta)
             GameUiWindow.SURVIVAL_INVENTORY -> survivalWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta)
+            GameUiWindow.CRAFTING_TABLE -> craftingWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta)
             GameUiWindow.NONE -> return
-            else -> Gdx.app.error(TAG, "Cannot draw window: ${mainConfig.gameUiWindow.name}")
+            else -> Gdx.app.error(TAG, "Cannot draw window: ${windowType.name}")
         }
     }
 
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/windows/CraftingWindowRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/windows/CraftingWindowRenderer.kt
new file mode 100644 (file)
index 0000000..3b08ed7
--- /dev/null
@@ -0,0 +1,95 @@
+package ru.deadsoftware.cavedroid.game.render.windows
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import com.badlogic.gdx.graphics.glutils.ShapeRenderer
+import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.render.IGameRenderer
+import ru.deadsoftware.cavedroid.game.render.WindowsRenderer
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.windows.inventory.CraftingInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class CraftingWindowRenderer @Inject constructor(
+    private val mainConfig: MainConfig,
+    private val mobsController: MobsController,
+    private val gameWindowsManager: GameWindowsManager,
+    private val gameItemsHolder: GameItemsHolder,
+) : AbstractWindowRenderer(), IGameRenderer {
+
+    override val renderLayer get() = WindowsRenderer.RENDER_LAYER
+
+    private val craftingWindowTexture get() = requireNotNull(Assets.textureRegions[CRAFTING_WINDOW_KEY])
+
+    override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+        val windowTexture = craftingWindowTexture
+        val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
+
+        val windowX = viewport.width / 2 - windowTexture.regionWidth / 2
+        val windowY = viewport.height / 2 - windowTexture.regionHeight / 2
+
+        spriteBatch.draw(windowTexture, windowX, windowY)
+
+        drawItemsGrid(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            gridX = windowX + GameWindowsConfigs.Crafting.itemsGridMarginLeft,
+            gridY = windowY + GameWindowsConfigs.Crafting.itemsGridMarginTop,
+            items = mobsController.player.inventory.asSequence()
+                .drop(GameWindowsConfigs.Crafting.hotbarCells)
+                .take(GameWindowsConfigs.Crafting.itemsInCol * GameWindowsConfigs.Crafting.itemsInRow)
+                .asIterable(),
+            itemsInRow = GameWindowsConfigs.Crafting.itemsInRow,
+            cellWidth = GameWindowsConfigs.Crafting.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Crafting.itemsGridRowHeight,
+        )
+
+        drawItemsGrid(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            gridX = windowX + GameWindowsConfigs.Crafting.itemsGridMarginLeft,
+            gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Crafting.hotbarOffsetFromBottom,
+            items = mobsController.player.inventory.asSequence()
+                .take(GameWindowsConfigs.Crafting.hotbarCells)
+                .asIterable(),
+            itemsInRow = GameWindowsConfigs.Crafting.hotbarCells,
+            cellWidth = GameWindowsConfigs.Crafting.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Crafting.itemsGridRowHeight,
+        )
+
+        drawItemsGrid(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            gridX = windowX + GameWindowsConfigs.Crafting.craftOffsetX,
+            gridY = windowY + GameWindowsConfigs.Crafting.craftOffsetY,
+            items = window.craftingItems.asSequence().map {  it ?: gameItemsHolder.fallbackItem.toInventoryItem()}.asIterable(),
+            itemsInRow = GameWindowsConfigs.Crafting.craftGridSize,
+            cellWidth = GameWindowsConfigs.Crafting.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Crafting.itemsGridRowHeight,
+        )
+
+        window.craftResult?.draw(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            x = windowX + GameWindowsConfigs.Crafting.craftResultOffsetX,
+            y = windowY + GameWindowsConfigs.Crafting.craftResultOffsetY
+        )
+
+        window.selectedItem?.drawSelected(
+            spriteBatch = spriteBatch,
+            x = Gdx.input.x * (viewport.width / Gdx.graphics.width),
+            y = Gdx.input.y * (viewport.height / Gdx.graphics.height)
+        )
+    }
+
+    companion object {
+        private const val CRAFTING_WINDOW_KEY = "crafting_table"
+    }
+}
\ No newline at end of file
index 39580de82cecd8a42d63a0dcf9eb25fd991ddb97..56df2cac3ad173f16bc9eeafc2808dbc0f69ce19 100644 (file)
@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer
 import com.badlogic.gdx.math.MathUtils
 import com.badlogic.gdx.math.Rectangle
 import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.mobs.Mob
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
@@ -13,6 +14,7 @@ import ru.deadsoftware.cavedroid.game.render.IGameRenderer
 import ru.deadsoftware.cavedroid.game.render.WindowsRenderer
 import ru.deadsoftware.cavedroid.game.windows.GameWindowsConfigs
 import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.windows.inventory.SurvivalInventoryWindow
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 import kotlin.math.atan
@@ -22,6 +24,7 @@ class SurvivalWindowRenderer @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
     private val gameWindowsManager: GameWindowsManager,
+    private val gameItemsHolder: GameItemsHolder,
 ) : AbstractWindowRenderer(), IGameRenderer {
 
     override val renderLayer get() = WindowsRenderer.RENDER_LAYER
@@ -60,12 +63,13 @@ class SurvivalWindowRenderer @Inject constructor(
     }
 
     override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
-        val survivalWindow = survivalWindowTexture
+        val windowTexture = survivalWindowTexture
+        val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
 
-        val windowX = viewport.width / 2 - survivalWindow.regionWidth / 2
-        val windowY = viewport.height / 2 - survivalWindow.regionHeight / 2
+        val windowX = viewport.width / 2 - windowTexture.regionWidth / 2
+        val windowY = viewport.height / 2 - windowTexture.regionHeight / 2
 
-        spriteBatch.draw(survivalWindow, windowX, windowY)
+        spriteBatch.draw(windowTexture, windowX, windowY)
 
         drawPlayerPortrait(spriteBatch, windowX, windowY, delta)
 
@@ -87,7 +91,7 @@ class SurvivalWindowRenderer @Inject constructor(
             spriteBatch = spriteBatch,
             shapeRenderer = shapeRenderer,
             gridX = windowX + GameWindowsConfigs.Survival.itemsGridMarginLeft,
-            gridY = windowY + survivalWindow.regionHeight - GameWindowsConfigs.Survival.hotbarOffsetFromBottom,
+            gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Survival.hotbarOffsetFromBottom,
             items = mobsController.player.inventory.asSequence()
                 .take(GameWindowsConfigs.Survival.hotbarCells)
                 .asIterable(),
@@ -96,7 +100,31 @@ class SurvivalWindowRenderer @Inject constructor(
             cellHeight = GameWindowsConfigs.Survival.itemsGridRowHeight,
         )
 
-        gameWindowsManager.selectedItem?.drawSelected(
+        drawItemsGrid(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            gridX = windowX + GameWindowsConfigs.Survival.craftOffsetX,
+            gridY = windowY + GameWindowsConfigs.Survival.craftOffsetY,
+            items = window.craftingItems.asSequence().mapIndexedNotNull { index, it ->
+                if (index % 3 > 1 || index / 3 > 1) {
+                    null
+                } else {
+                    it ?: gameItemsHolder.fallbackItem.toInventoryItem()
+                }
+            }.asIterable(),
+            itemsInRow = GameWindowsConfigs.Survival.craftGridSize,
+            cellWidth = GameWindowsConfigs.Survival.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Survival.itemsGridRowHeight,
+        )
+
+        window.craftResult?.draw(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            x = windowX + GameWindowsConfigs.Survival.craftResultOffsetX,
+            y = windowY + GameWindowsConfigs.Survival.craftResultOffsetY
+        )
+
+        window.selectedItem?.drawSelected(
             spriteBatch = spriteBatch,
             x = Gdx.input.x * (viewport.width / Gdx.graphics.width),
             y = Gdx.input.y * (viewport.height / Gdx.graphics.height)
index 6dde753a860c02360c47631413e310f897a6813e..a6cff63b08c049c3dc646c11ffdee28593ff05fc 100644 (file)
@@ -39,5 +39,35 @@ object GameWindowsConfigs {
         const val portraitMarginTop = 8f
         const val portraitWidth = 48f
         const val portraitHeight = 68f
+
+        const val craftGridSize = 2
+
+        const val craftOffsetX = 98f
+        const val craftOffsetY = 18f
+
+        const val craftResultOffsetX = 154f
+        const val craftResultOffsetY = 28f
+    }
+
+    data object Crafting {
+        const val itemsGridMarginLeft = 8f
+        const val itemsGridMarginTop = 84f
+
+        const val itemsGridRowHeight = 18f
+        const val itemsGridColWidth = 18f
+
+        const val itemsInRow = 9
+        const val itemsInCol = 5
+
+        const val hotbarOffsetFromBottom = 24f
+        const val hotbarCells = 9
+
+        const val craftGridSize = 3
+
+        const val craftOffsetX = 30f
+        const val craftOffsetY = 18f
+
+        const val craftResultOffsetX = 128f
+        const val craftResultOffsetY = 36f
     }
 }
\ No newline at end of file
index 302562fb9b60d086b30bc79ca6b1baf22099ef12..b07f1fd5112ea2be09521f05808319a173286b71 100644 (file)
@@ -5,6 +5,10 @@ import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.GameUiWindow
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.windows.inventory.AbstractInventoryWindow
+import ru.deadsoftware.cavedroid.game.windows.inventory.CraftingInventoryWindow
+import ru.deadsoftware.cavedroid.game.windows.inventory.CreativeInventoryWindow
+import ru.deadsoftware.cavedroid.game.windows.inventory.SurvivalInventoryWindow
 import javax.inject.Inject
 
 @GameScope
@@ -15,22 +19,28 @@ class GameWindowsManager @Inject constructor(
 
     var creativeScrollAmount = 0
     var isDragging = false
-    var selectedItem: InventoryItem? = null
 
+    var currentWindow: AbstractInventoryWindow? = null
+
+    @JvmName("getCurrentWindowType")
     fun getCurrentWindow(): GameUiWindow {
-        return mainConfig.gameUiWindow
+        return currentWindow?.type ?: GameUiWindow.NONE
     }
 
     fun openInventory() {
         if (mobsController.player.gameMode == 1) {
-            mainConfig.gameUiWindow = GameUiWindow.CREATIVE_INVENTORY
+            currentWindow = CreativeInventoryWindow(GameUiWindow.CREATIVE_INVENTORY)
         } else {
-            mainConfig.gameUiWindow = GameUiWindow.SURVIVAL_INVENTORY
+            currentWindow = SurvivalInventoryWindow(GameUiWindow.SURVIVAL_INVENTORY)
         }
     }
 
+    fun openCrafting() {
+        currentWindow = CraftingInventoryWindow(GameUiWindow.CRAFTING_TABLE)
+    }
+
     fun closeWindow() {
-        mainConfig.gameUiWindow = GameUiWindow.NONE
+        currentWindow = null
     }
 
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/AbstractInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/AbstractInventoryWindow.kt
new file mode 100644 (file)
index 0000000..0660604
--- /dev/null
@@ -0,0 +1,12 @@
+package ru.deadsoftware.cavedroid.game.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+abstract class AbstractInventoryWindow {
+
+    abstract val type: GameUiWindow
+
+    abstract var selectedItem: InventoryItem?
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CraftingInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CraftingInventoryWindow.kt
new file mode 100644 (file)
index 0000000..03808f1
--- /dev/null
@@ -0,0 +1,15 @@
+package ru.deadsoftware.cavedroid.game.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class CraftingInventoryWindow(
+    override val type: GameUiWindow,
+) : AbstractInventoryWindow() {
+
+    override var selectedItem: InventoryItem? = null
+
+    val craftingItems = MutableList<InventoryItem?>(9) { null }
+
+    var craftResult: InventoryItem? = null
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CreativeInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CreativeInventoryWindow.kt
new file mode 100644 (file)
index 0000000..5f72243
--- /dev/null
@@ -0,0 +1,10 @@
+package ru.deadsoftware.cavedroid.game.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class CreativeInventoryWindow(
+    override val type: GameUiWindow,
+) : AbstractInventoryWindow() {
+    override var selectedItem: InventoryItem? = null
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/SurvivalInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/SurvivalInventoryWindow.kt
new file mode 100644 (file)
index 0000000..4eeba3c
--- /dev/null
@@ -0,0 +1,15 @@
+package ru.deadsoftware.cavedroid.game.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class SurvivalInventoryWindow(
+    override val type: GameUiWindow,
+) : AbstractInventoryWindow() {
+
+    override var selectedItem: InventoryItem? = null
+
+    val craftingItems = MutableList<InventoryItem?>(9) { null }
+
+    var craftResult: InventoryItem? = null
+}
\ No newline at end of file