DEADSOFTWARE

Add mobs damage and initial spawn master
authorfredboy <fredboy@protonmail.com>
Sun, 12 May 2024 16:46:18 +0000 (23:46 +0700)
committerfredboy <fredboy@protonmail.com>
Sun, 12 May 2024 16:46:18 +0000 (23:46 +0700)
280 files changed:
COPYING
README.md
android/assets/health.png [deleted file]
android/assets/joy_background.png [new file with mode: 0644]
android/assets/joy_stick.png [new file with mode: 0644]
android/assets/json/crafting.json
android/assets/json/game_items.json
android/assets/json/menu_main_buttons.json
android/assets/json/menu_options_buttons.json [new file with mode: 0644]
android/assets/json/texture_regions.json
android/assets/json/touch_buttons.json
android/assets/pp/allitems.png [moved from android/assets/allitems.png with 100% similarity]
android/assets/pp/background.png [moved from android/assets/background.png with 100% similarity]
android/assets/pp/background_top.png [moved from android/assets/background_top.png with 100% similarity]
android/assets/pp/break.png [moved from android/assets/break.png with 100% similarity]
android/assets/pp/buttons.png [moved from android/assets/buttons.png with 100% similarity]
android/assets/pp/chest.png [new file with mode: 0644]
android/assets/pp/chest_large.png [new file with mode: 0644]
android/assets/pp/crafting_table.png [moved from android/assets/crafting_table.png with 100% similarity]
android/assets/pp/furnace.png [new file with mode: 0644]
android/assets/pp/gui.png [moved from android/assets/gui.png with 100% similarity]
android/assets/pp/health.png [new file with mode: 0644]
android/assets/pp/inventory.png [moved from android/assets/inventory.png with 100% similarity]
android/assets/pp/mobs/char/0_0.png [moved from android/assets/mobs/char/0_0.png with 100% similarity]
android/assets/pp/mobs/char/0_1.png [moved from android/assets/mobs/char/0_1.png with 100% similarity]
android/assets/pp/mobs/char/0_2.png [moved from android/assets/mobs/char/0_2.png with 100% similarity]
android/assets/pp/mobs/char/0_3.png [moved from android/assets/mobs/char/0_3.png with 100% similarity]
android/assets/pp/mobs/char/1_0.png [moved from android/assets/mobs/char/1_0.png with 100% similarity]
android/assets/pp/mobs/char/1_1.png [moved from android/assets/mobs/char/1_1.png with 100% similarity]
android/assets/pp/mobs/char/1_2.png [moved from android/assets/mobs/char/1_2.png with 100% similarity]
android/assets/pp/mobs/char/1_3.png [moved from android/assets/mobs/char/1_3.png with 100% similarity]
android/assets/pp/mobs/pig/0_0.png [moved from android/assets/mobs/pig/0_0.png with 100% similarity]
android/assets/pp/mobs/pig/0_1.png [moved from android/assets/mobs/pig/0_1.png with 100% similarity]
android/assets/pp/mobs/pig/1_0.png [moved from android/assets/mobs/pig/1_0.png with 100% similarity]
android/assets/pp/mobs/pig/1_1.png [moved from android/assets/mobs/pig/1_1.png with 100% similarity]
android/assets/pp/shade.png [moved from android/assets/shade.png with 100% similarity]
android/assets/pp/textures/blocks/bed_l.png [moved from android/assets/textures/blocks/bed_l.png with 100% similarity]
android/assets/pp/textures/blocks/bed_r.png [moved from android/assets/textures/blocks/bed_r.png with 100% similarity]
android/assets/pp/textures/blocks/bedrock.png [moved from android/assets/textures/blocks/bedrock.png with 100% similarity]
android/assets/pp/textures/blocks/bookshelf.png [moved from android/assets/textures/blocks/bookshelf.png with 100% similarity]
android/assets/pp/textures/blocks/bricks.png [moved from android/assets/textures/blocks/bricks.png with 100% similarity]
android/assets/pp/textures/blocks/cactus.png [moved from android/assets/textures/blocks/cactus.png with 100% similarity]
android/assets/pp/textures/blocks/cake.png [moved from android/assets/textures/blocks/cake.png with 100% similarity]
android/assets/pp/textures/blocks/chest.png [new file with mode: 0644]
android/assets/pp/textures/blocks/clay.png [moved from android/assets/textures/blocks/clay.png with 100% similarity]
android/assets/pp/textures/blocks/coal_block.png [moved from android/assets/textures/blocks/coal_block.png with 100% similarity]
android/assets/pp/textures/blocks/coal_ore.png [moved from android/assets/textures/blocks/coal_ore.png with 100% similarity]
android/assets/pp/textures/blocks/cobblestone.png [moved from android/assets/textures/blocks/cobblestone.png with 100% similarity]
android/assets/pp/textures/blocks/cobblestone_mossy.png [moved from android/assets/textures/blocks/cobblestone_mossy.png with 100% similarity]
android/assets/pp/textures/blocks/crafting_table.png [moved from android/assets/textures/blocks/crafting_table.png with 100% similarity]
android/assets/pp/textures/blocks/dandelion.png [moved from android/assets/textures/blocks/dandelion.png with 100% similarity]
android/assets/pp/textures/blocks/deadbush.png [moved from android/assets/textures/blocks/deadbush.png with 100% similarity]
android/assets/pp/textures/blocks/diamond_block.png [moved from android/assets/textures/blocks/diamond_block.png with 100% similarity]
android/assets/pp/textures/blocks/diamond_ore.png [moved from android/assets/textures/blocks/diamond_ore.png with 100% similarity]
android/assets/pp/textures/blocks/dirt.png [moved from android/assets/textures/blocks/dirt.png with 100% similarity]
android/assets/pp/textures/blocks/furnace.png [new file with mode: 0644]
android/assets/pp/textures/blocks/glass.png [moved from android/assets/textures/blocks/glass.png with 100% similarity]
android/assets/pp/textures/blocks/gold_block.png [moved from android/assets/textures/blocks/gold_block.png with 100% similarity]
android/assets/pp/textures/blocks/gold_ore.png [moved from android/assets/textures/blocks/gold_ore.png with 100% similarity]
android/assets/pp/textures/blocks/grass.png [moved from android/assets/textures/blocks/grass.png with 100% similarity]
android/assets/pp/textures/blocks/grass_snowed.png [moved from android/assets/textures/blocks/grass_snowed.png with 100% similarity]
android/assets/pp/textures/blocks/gravel.png [moved from android/assets/textures/blocks/gravel.png with 100% similarity]
android/assets/pp/textures/blocks/iron_bars.png [moved from android/assets/textures/blocks/iron_bars.png with 100% similarity]
android/assets/pp/textures/blocks/iron_block.png [moved from android/assets/textures/blocks/iron_block.png with 100% similarity]
android/assets/pp/textures/blocks/iron_ore.png [moved from android/assets/textures/blocks/iron_ore.png with 100% similarity]
android/assets/pp/textures/blocks/ladder.png [moved from android/assets/textures/blocks/ladder.png with 100% similarity]
android/assets/pp/textures/blocks/lapis_block.png [moved from android/assets/textures/blocks/lapis_block.png with 100% similarity]
android/assets/pp/textures/blocks/lapis_ore.png [new file with mode: 0644]
android/assets/pp/textures/blocks/lava_flow.png [moved from android/assets/textures/blocks/lava_flow.png with 100% similarity]
android/assets/pp/textures/blocks/lava_still.png [moved from android/assets/textures/blocks/lava_still.png with 100% similarity]
android/assets/pp/textures/blocks/leaves_oak.png [moved from android/assets/textures/blocks/leaves_oak.png with 100% similarity]
android/assets/pp/textures/blocks/leaves_spruce.png [new file with mode: 0644]
android/assets/pp/textures/blocks/log_birch.png [moved from android/assets/textures/blocks/log_birch.png with 100% similarity]
android/assets/pp/textures/blocks/log_oak.png [moved from android/assets/textures/blocks/log_oak.png with 100% similarity]
android/assets/pp/textures/blocks/log_spruce.png [moved from android/assets/textures/blocks/log_spruce.png with 100% similarity]
android/assets/pp/textures/blocks/mushroom_brown.png [moved from android/assets/textures/blocks/mushroom_brown.png with 100% similarity]
android/assets/pp/textures/blocks/mushroom_red.png [moved from android/assets/textures/blocks/mushroom_red.png with 100% similarity]
android/assets/pp/textures/blocks/noteblock.png [moved from android/assets/textures/blocks/noteblock.png with 100% similarity]
android/assets/pp/textures/blocks/obsidian.png [moved from android/assets/textures/blocks/obsidian.png with 100% similarity]
android/assets/pp/textures/blocks/planks_birch.png [moved from android/assets/textures/blocks/planks_birch.png with 100% similarity]
android/assets/pp/textures/blocks/planks_oak.png [moved from android/assets/textures/blocks/planks_oak.png with 100% similarity]
android/assets/pp/textures/blocks/planks_spruce.png [moved from android/assets/textures/blocks/planks_spruce.png with 100% similarity]
android/assets/pp/textures/blocks/rose.png [moved from android/assets/textures/blocks/rose.png with 100% similarity]
android/assets/pp/textures/blocks/sand.png [moved from android/assets/textures/blocks/sand.png with 100% similarity]
android/assets/pp/textures/blocks/sandstone.png [moved from android/assets/textures/blocks/sandstone.png with 100% similarity]
android/assets/pp/textures/blocks/sapling_birch.png [moved from android/assets/textures/blocks/sapling_birch.png with 100% similarity]
android/assets/pp/textures/blocks/sapling_oak.png [moved from android/assets/textures/blocks/sapling_oak.png with 100% similarity]
android/assets/pp/textures/blocks/sapling_spruce.png [moved from android/assets/textures/blocks/sapling_spruce.png with 100% similarity]
android/assets/pp/textures/blocks/snow.png [new file with mode: 0644]
android/assets/pp/textures/blocks/sponge.png [moved from android/assets/textures/blocks/sponge.png with 100% similarity]
android/assets/pp/textures/blocks/sponge_wet.png [moved from android/assets/textures/blocks/sponge_wet.png with 100% similarity]
android/assets/pp/textures/blocks/stone.png [moved from android/assets/textures/blocks/stone.png with 100% similarity]
android/assets/pp/textures/blocks/stone_slab.png [moved from android/assets/textures/blocks/stone_slab.png with 100% similarity]
android/assets/pp/textures/blocks/stonebrick.png [moved from android/assets/textures/blocks/stonebrick.png with 100% similarity]
android/assets/pp/textures/blocks/tallgrass.png [moved from android/assets/textures/blocks/tallgrass.png with 100% similarity]
android/assets/pp/textures/blocks/water_flow.png [moved from android/assets/textures/blocks/water_flow.png with 100% similarity]
android/assets/pp/textures/blocks/water_still.png [moved from android/assets/textures/blocks/water_still.png with 100% similarity]
android/assets/pp/textures/blocks/web.png [moved from android/assets/textures/blocks/web.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_black.png [moved from android/assets/textures/blocks/wool_colored_black.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_blue.png [moved from android/assets/textures/blocks/wool_colored_blue.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_brown.png [moved from android/assets/textures/blocks/wool_colored_brown.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_cyan.png [moved from android/assets/textures/blocks/wool_colored_cyan.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_gray.png [moved from android/assets/textures/blocks/wool_colored_gray.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_green.png [moved from android/assets/textures/blocks/wool_colored_green.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_light_blue.png [moved from android/assets/textures/blocks/wool_colored_light_blue.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_lime.png [moved from android/assets/textures/blocks/wool_colored_lime.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_magenta.png [moved from android/assets/textures/blocks/wool_colored_magenta.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_orange.png [moved from android/assets/textures/blocks/wool_colored_orange.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_pink.png [moved from android/assets/textures/blocks/wool_colored_pink.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_purple.png [moved from android/assets/textures/blocks/wool_colored_purple.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_red.png [moved from android/assets/textures/blocks/wool_colored_red.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_silver.png [moved from android/assets/textures/blocks/wool_colored_silver.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_white.png [moved from android/assets/textures/blocks/wool_colored_white.png with 100% similarity]
android/assets/pp/textures/blocks/wool_colored_yellow.png [moved from android/assets/textures/blocks/wool_colored_yellow.png with 100% similarity]
android/assets/pp/textures/items/bed.png [new file with mode: 0644]
android/assets/pp/textures/items/bucket_empty.png [moved from android/assets/textures/items/bucket_empty.png with 100% similarity]
android/assets/pp/textures/items/bucket_lava.png [moved from android/assets/textures/items/bucket_lava.png with 100% similarity]
android/assets/pp/textures/items/bucket_milk.png [moved from android/assets/textures/items/bucket_milk.png with 100% similarity]
android/assets/pp/textures/items/bucket_water.png [moved from android/assets/textures/items/bucket_water.png with 100% similarity]
android/assets/pp/textures/items/charcoal.png [new file with mode: 0644]
android/assets/pp/textures/items/coal.png [new file with mode: 0644]
android/assets/pp/textures/items/diamond.png [new file with mode: 0644]
android/assets/pp/textures/items/diamond_axe.png [moved from android/assets/textures/items/diamond_axe.png with 100% similarity]
android/assets/pp/textures/items/diamond_hoe.png [moved from android/assets/textures/items/diamond_hoe.png with 100% similarity]
android/assets/pp/textures/items/diamond_pickaxe.png [moved from android/assets/textures/items/diamond_pickaxe.png with 100% similarity]
android/assets/pp/textures/items/diamond_shovel.png [moved from android/assets/textures/items/diamond_shovel.png with 100% similarity]
android/assets/pp/textures/items/diamond_sword.png [moved from android/assets/textures/items/diamond_sword.png with 100% similarity]
android/assets/pp/textures/items/gold_axe.png [moved from android/assets/textures/items/gold_axe.png with 100% similarity]
android/assets/pp/textures/items/gold_hoe.png [moved from android/assets/textures/items/gold_hoe.png with 100% similarity]
android/assets/pp/textures/items/gold_ingot.png [new file with mode: 0644]
android/assets/pp/textures/items/gold_pickaxe.png [moved from android/assets/textures/items/gold_pickaxe.png with 100% similarity]
android/assets/pp/textures/items/gold_shovel.png [moved from android/assets/textures/items/gold_shovel.png with 100% similarity]
android/assets/pp/textures/items/gold_sword.png [moved from android/assets/textures/items/gold_sword.png with 100% similarity]
android/assets/pp/textures/items/iron_axe.png [moved from android/assets/textures/items/iron_axe.png with 100% similarity]
android/assets/pp/textures/items/iron_hoe.png [moved from android/assets/textures/items/iron_hoe.png with 100% similarity]
android/assets/pp/textures/items/iron_ingot.png [new file with mode: 0644]
android/assets/pp/textures/items/iron_pickaxe.png [moved from android/assets/textures/items/iron_pickaxe.png with 100% similarity]
android/assets/pp/textures/items/iron_shovel.png [moved from android/assets/textures/items/iron_shovel.png with 100% similarity]
android/assets/pp/textures/items/iron_sword.png [moved from android/assets/textures/items/iron_sword.png with 100% similarity]
android/assets/pp/textures/items/lapis_lazuli.png [new file with mode: 0644]
android/assets/pp/textures/items/shears.png [moved from android/assets/textures/items/shears.png with 100% similarity]
android/assets/pp/textures/items/snowball.png [new file with mode: 0644]
android/assets/pp/textures/items/spawn_egg.png [new file with mode: 0644]
android/assets/pp/textures/items/stick.png [moved from android/assets/textures/items/stick.png with 100% similarity]
android/assets/pp/textures/items/stone_axe.png [moved from android/assets/textures/items/stone_axe.png with 100% similarity]
android/assets/pp/textures/items/stone_hoe.png [moved from android/assets/textures/items/stone_hoe.png with 100% similarity]
android/assets/pp/textures/items/stone_pickaxe.png [moved from android/assets/textures/items/stone_pickaxe.png with 100% similarity]
android/assets/pp/textures/items/stone_shovel.png [moved from android/assets/textures/items/stone_shovel.png with 100% similarity]
android/assets/pp/textures/items/stone_sword.png [moved from android/assets/textures/items/stone_sword.png with 100% similarity]
android/assets/pp/textures/items/wood_axe.png [moved from android/assets/textures/items/wood_axe.png with 100% similarity]
android/assets/pp/textures/items/wood_hoe.png [moved from android/assets/textures/items/wood_hoe.png with 100% similarity]
android/assets/pp/textures/items/wood_pickaxe.png [moved from android/assets/textures/items/wood_pickaxe.png with 100% similarity]
android/assets/pp/textures/items/wood_shovel.png [moved from android/assets/textures/items/wood_shovel.png with 100% similarity]
android/assets/pp/textures/items/wood_sword.png [moved from android/assets/textures/items/wood_sword.png with 100% similarity]
android/assets/textures/blocks/furnace_off.png [deleted file]
android/assets/textures/blocks/furnace_on.png [deleted file]
android/assets/textures/blocks/lapis_ore.png [deleted file]
android/assets/touch_gui.png
android/build.gradle
build.gradle
core/src/ru/deadsoftware/cavedroid/CaveGame.java
core/src/ru/deadsoftware/cavedroid/MainConfig.java
core/src/ru/deadsoftware/cavedroid/game/GameComponent.java
core/src/ru/deadsoftware/cavedroid/game/GameItemsHolder.kt
core/src/ru/deadsoftware/cavedroid/game/GameModule.java
core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java
core/src/ru/deadsoftware/cavedroid/game/GameProc.java
core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java
core/src/ru/deadsoftware/cavedroid/game/GameSaver.java
core/src/ru/deadsoftware/cavedroid/game/actions/UpdateBlockActionsModule.kt
core/src/ru/deadsoftware/cavedroid/game/actions/UseBlockActionsModule.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/UseItemActionsModule.kt
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateBedLeftAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateBedRightAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGrassAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSnowedGrassAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/useblock/IUseBlockAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseChestAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseCraftingTableAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseFurnaceAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseBedAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseEmptyBucketAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UsePigSpawnEggAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/debug/DebugInfoStringsProvider.kt
core/src/ru/deadsoftware/cavedroid/game/input/Joystick.kt [new file with mode: 0644]
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/action/keys/MouseInputActionKey.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/CloseGameWindowKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/DropItemKeyboardInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/FlyDownKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/FlyUpKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/GoLeftKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/GoRightKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/JumpKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/MoveCursorControlsModeKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenInventoryKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/PauseGameKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/SelectHotbarSlotKeyboardInputHandler.kt [moved from core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt with 54% similarity]
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/StopSwimKeyboardInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/SwimUpKeyboardInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleControlsModeKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/TurnOnFlyModeKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/AbstractInventoryItemsMouseInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/AttackMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CloseGameWindowMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CreativeInventoryScrollMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CursorMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/HotbarMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectChestInventoryItemMouseInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCraftingInventoryItemMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCreativeInventoryItemMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectFurnaceInventoryItemMouseInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectSurvivalInventoryItemMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/UseItemMouseInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/touch/JoystickInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/mapper/KeyboardInputActionMapper.kt
core/src/ru/deadsoftware/cavedroid/game/input/mapper/MouseInputActionMapper.kt
core/src/ru/deadsoftware/cavedroid/game/mobs/FallingGravel.java
core/src/ru/deadsoftware/cavedroid/game/mobs/FallingSand.java
core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java
core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.kt
core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.kt
core/src/ru/deadsoftware/cavedroid/game/mobs/player/Inventory.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/mobs/player/Player.java [moved from core/src/ru/deadsoftware/cavedroid/game/mobs/Player.java with 70% similarity]
core/src/ru/deadsoftware/cavedroid/game/model/block/Block.kt
core/src/ru/deadsoftware/cavedroid/game/model/block/CommonBlockParams.kt
core/src/ru/deadsoftware/cavedroid/game/model/craft/CraftingRecipe.kt
core/src/ru/deadsoftware/cavedroid/game/model/dto/BlockDto.kt
core/src/ru/deadsoftware/cavedroid/game/model/dto/ItemDto.kt
core/src/ru/deadsoftware/cavedroid/game/model/item/CommonItemParams.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/BlockMapper.kt
core/src/ru/deadsoftware/cavedroid/game/model/mapper/ItemMapper.kt
core/src/ru/deadsoftware/cavedroid/game/objects/container/Chest.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/objects/container/Container.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/objects/container/ContainerController.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/objects/container/Furnace.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/objects/drop/Drop.kt [moved from core/src/ru/deadsoftware/cavedroid/game/objects/Drop.kt with 72% similarity]
core/src/ru/deadsoftware/cavedroid/game/objects/drop/DropController.java [moved from core/src/ru/deadsoftware/cavedroid/game/objects/DropController.java with 68% similarity]
core/src/ru/deadsoftware/cavedroid/game/render/BlocksRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/DropsRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/HudRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/TouchControlsRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/WindowsRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/windows/ChestWindowRenderer.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/render/windows/CraftingWindowRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/windows/CreativeWindowRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/windows/FurnaceWindowRenderer.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/render/windows/SurvivalWindowRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/ui/TooltipManager.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/GameWindowsConfigs.kt [moved from core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsConfigs.kt with 55% similarity]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/GameWindowsManager.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/AbstractInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/AbstractInventoryWindowWithCraftGrid.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/ChestInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/CraftingInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/CreativeInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/FurnaceInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/SurvivalInventoryWindow.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsManager.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/windows/inventory/AbstractInventoryWindow.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CraftingInventoryWindow.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CreativeInventoryWindow.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/windows/inventory/SurvivalInventoryWindow.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldGenerator.kt
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldMobDamageControllerTask.kt
core/src/ru/deadsoftware/cavedroid/menu/MenuProc.java
core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuMain.java
core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuOptions.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/menu/submenus/MenusFactory.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/Assets.java
core/src/ru/deadsoftware/cavedroid/misc/utils/MeasureUnitsUtils.kt
core/src/ru/deadsoftware/cavedroid/misc/utils/RenderingUtils.kt
core/src/ru/deadsoftware/cavedroid/misc/utils/SpriteUtils.kt
make-release.sh
require-clean-work-tree.sh [moved from require-celan-work-tree.sh with 100% similarity, mode: 0755]

diff --git a/COPYING b/COPYING
index 6d0eb3acdafa494b776a3a16163fa4b22458a294..90ecac317cec31dfa6bb755d18d10b7034ab4fcb 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,13 @@
 This game is distributed under MIT License (see LICENSE).
 
-Textures used in this game:
+Textures used in this game (in android/assets/pp directory):
 Pixel Perfection by XSSheep is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.
 https://creativecommons.org/licenses/by-sa/4.0/
 
+On screen joystick is CC-0 from opengameart.org:
+https://opengameart.org/content/mmorpg-virtual-joysticks
+
+Font is Minecraft Font by JDGraphics licensed as Public Domain:
+https://www.fontspace.com/minecraft-font-f28180
+
 Some scripts from stack overflow are distributed under applicable licenses
index 684c5a354316afe22a10f1134a69e5e7848ab072..d7246af128305107451000e935c31527507b9209 100644 (file)
--- a/README.md
+++ b/README.md
@@ -3,6 +3,13 @@
 [![GitHub Tag](https://img.shields.io/github/v/tag/fredboy/cavedroid)](https://github.com/fredboy/cavedroid/tags) <br>
 2D Minecraft clone for Android and Desktop. <br>
 Written in Java using libGDX framework. <br>
+<details>
+  <summary>Screenshot</summary>
+
+![Screenshot](https://fredboy.ru/pub/cavedroid/screenshot.png)
+
+</details>
+
 ## Binary releases
 You can download apk and jar from here: <br>
 <https://fredboy.ru/pub/cavedroid/>
diff --git a/android/assets/health.png b/android/assets/health.png
deleted file mode 100644 (file)
index 93314e6..0000000
Binary files a/android/assets/health.png and /dev/null differ
diff --git a/android/assets/joy_background.png b/android/assets/joy_background.png
new file mode 100644 (file)
index 0000000..b1ea80b
Binary files /dev/null and b/android/assets/joy_background.png differ
diff --git a/android/assets/joy_stick.png b/android/assets/joy_stick.png
new file mode 100644 (file)
index 0000000..525dd7f
Binary files /dev/null and b/android/assets/joy_stick.png differ
index 6f651bb297f24b0acc6e0ad41637051d6a170a19..9a67e014520eb6d4cf1ae88d8b1606d5473949fa 100644 (file)
@@ -3,24 +3,28 @@
     "input": ["log_oak", "none", "none", "none", "none", "none", "none", "none", "none"],
     "count": 4
   },
+  "planks_spruce": {
+    "input": ["log_spruce", "none", "none", "none", "none", "none", "none", "none", "none"],
+    "count": 4
+  },
   "stick": {
-    "input": ["planks_oak", "none", "none", "planks_oak", "none", "none", "none", "none", "none"],
+    "input": ["planks.*", "none", "none", "planks.*", "none", "none", "none", "none", "none"],
     "count": 4
   },
   "wood_pickaxe": {
-    "input": ["planks_oak", "planks_oak", "planks_oak", "none", "stick", "none", "none", "stick", "none"],
+    "input": ["planks.*", "planks.*", "planks.*", "none", "stick", "none", "none", "stick", "none"],
     "count": 59
   },
   "wood_axe": {
-    "input": ["planks_oak", "planks_oak", "none", "planks_oak", "stick", "none", "none", "stick", "none"],
+    "input": ["planks.*", "planks.*", "none", "planks.*", "stick", "none", "none", "stick", "none"],
     "count": 60
   },
   "wood_sword": {
-    "input": ["none", "planks_oak", "none", "none", "planks_oak", "none", "none", "stick", "none"],
+    "input": ["planks.*", "none", "none", "planks.*", "none", "none", "stick", "none", "none"],
     "count": 60
   },
   "wood_shovel": {
-    "input": ["none", "planks_oak", "none", "none", "stick", "none", "none", "stick", "none"],
+    "input": ["planks.*", "none", "none", "stick", "none", "none", "stick", "none", "none"],
     "count": 59
   },
   "stone_pickaxe": {
     "count": 131
   },
   "stone_sword": {
-    "input": ["none", "cobblestone", "none", "none", "cobblestone", "none", "none", "stick", "none"],
+    "input": ["cobblestone", "none", "none", "cobblestone", "none", "none", "stick", "none", "none"],
     "count": 132
   },
   "stone_shovel": {
-    "input": ["none", "cobblestone", "none", "none", "stick", "none", "none", "stick", "none"],
+    "input": ["cobblestone", "none", "none", "stick", "none", "none", "stick", "none", "none"],
     "count": 131
+  },
+  "iron_pickaxe": {
+    "input": ["iron_ingot", "iron_ingot", "iron_ingot", "none", "stick", "none", "none", "stick", "none"],
+    "count": 251
+  },
+  "iron_axe": {
+    "input": ["iron_ingot", "iron_ingot", "none", "iron_ingot", "stick", "none", "none", "stick", "none"],
+    "count": 250
+  },
+  "iron_sword": {
+    "input": ["iron_ingot", "none", "none", "iron_ingot", "none", "none", "stick", "none", "none"],
+    "count": 251
+  },
+  "iron_shovel": {
+    "input": ["iron_ingot", "none", "none", "stick", "none", "none", "stick", "none", "none"],
+    "count": 250
+  },
+  "gold_pickaxe": {
+    "input": ["gold_ingot", "gold_ingot", "gold_ingot", "none", "stick", "none", "none", "stick", "none"],
+    "count": 33
+  },
+  "gold_axe": {
+    "input": ["gold_ingot", "gold_ingot", "none", "gold_ingot", "stick", "none", "none", "stick", "none"],
+    "count": 32
+  },
+  "gold_sword": {
+    "input": ["gold_ingot", "none", "none", "gold_ingot", "none", "none", "stick", "none", "none"],
+    "count": 33
+  },
+  "gold_shovel": {
+    "input": ["gold_ingot", "none", "none", "stick", "none", "none", "stick", "none", "none"],
+    "count": 32
+  },
+  "diamond_pickaxe": {
+    "input": ["diamond", "diamond", "diamond", "none", "stick", "none", "none", "stick", "none"],
+    "count": 1562
+  },
+  "diamond_axe": {
+    "input": ["diamond", "diamond", "none", "diamond", "stick", "none", "none", "stick", "none"],
+    "count": 1561
+  },
+  "diamond_sword": {
+    "input": ["diamond", "none", "none", "diamond", "none", "none", "stick", "none", "none"],
+    "count": 1562
+  },
+  "diamond_shovel": {
+    "input": ["diamond", "none", "none", "stick", "none", "none", "stick", "none", "none"],
+    "count": 1561
+  },
+  "snow_block": {
+    "input": ["snowball", "snowball", "none", "snowball", "snowball", "none", "none", "none", "none"],
+    "count": 1
+  },
+  "crafting_table": {
+    "input": ["planks.*", "planks.*", "none", "planks.*", "planks.*", "none", "none", "none", "none"]
+  },
+  "furnace": {
+    "input": ["cobblestone", "cobblestone", "cobblestone", "cobblestone", "none", "cobblestone", "cobblestone", "cobblestone", "cobblestone"]
+  },
+  "chest": {
+    "input": ["planks_.*", "planks_.*", "planks_.*", "planks_.*", "none", "planks_.*", "planks_.*", "planks_.*", "planks_.*"]
+  },
+  "bed": {
+    "input": ["wool_.*", "wool_.*", "wool_.*", "planks_.*", "planks_.*", "planks_.*", "none", "none", "none"]
   }
 }
\ No newline at end of file
index d6c3b441a3fc0cff5672e08a10985e8705bfbcd0..5c1157690a9141876f5d96311f711677e3a70d14 100644 (file)
       "tool_level": 0,
       "tool_type": "axe"
     },
+    "planks_spruce": {
+      "hp": 180,
+      "drop": "planks_spruce",
+      "texture": "planks_spruce",
+      "tool_level": 0,
+      "tool_type": "axe"
+    },
+    "crafting_table": {
+      "hp": 180,
+      "drop": "crafting_table",
+      "texture": "crafting_table",
+      "tool_level": 0,
+      "tool_type": "axe"
+    },
     "sapling_oak": {
       "collision": false,
       "transparent": true,
       "texture": "sapling_oak",
       "hp": 0
     },
+    "sapling_spruce": {
+      "collision": false,
+      "transparent": true,
+      "block_required": true,
+      "drop": "sapling_spruce",
+      "texture": "sapling_spruce",
+      "hp": 0
+    },
     "bedrock": {
       "drop": "bedrock",
       "texture": "bedrock"
       "tool_type": "shears",
       "tint": "#5AC557"
     },
+    "log_spruce": {
+      "hp": 180,
+      "drop": "log_spruce",
+      "texture": "log_spruce",
+      "tool_level": 0,
+      "tool_type": "axe"
+    },
+    "leaves_spruce": {
+      "hp": 21,
+      "drop": "leaves_spruce",
+      "texture": "leaves_spruce",
+      "tool_level": 1,
+      "tool_type": "shears",
+      "tint": "#486D4E"
+    },
     "sponge": {
       "hp": 54,
       "drop": "sponge",
       "collision": false,
       "background": true,
       "transparent": true,
-      "drop": "none",
+      "drop": "bed",
       "texture": "bed_l",
       "tool_level": 0,
       "tool_type": "axe"
       "collision": false,
       "background": true,
       "transparent": true,
-      "drop": "none",
+      "drop": "bed",
       "texture": "bed_r",
       "tool_level": 0,
       "tool_type": "axe"
       "tool_level": 0,
       "tool_type": "axe"
     },
+    "spruce_slab_bottom": {
+      "top": 8,
+      "sprite_top": 8,
+      "hp": 180,
+      "transparent": true,
+      "drop": "spruce_slab",
+      "meta": "slab",
+      "texture": "planks_spruce",
+      "full_block": "planks_spruce",
+      "other_part": "spruce_slab_top",
+      "tool_level": 0,
+      "tool_type": "axe"
+    },
+    "spruce_slab_top": {
+      "bottom": 8,
+      "sprite_bottom": 8,
+      "hp": 180,
+      "transparent": true,
+      "drop": "spruce_slab",
+      "meta": "slab",
+      "texture": "planks_spruce",
+      "full_block": "planks_spruce",
+      "other_part": "spruce_slab_bottom",
+      "tool_level": 0,
+      "tool_type": "axe"
+    },
     "cobblestone_slab_bottom": {
       "top": 8,
       "sprite_top": 8,
       "texture": "obsidian",
       "tool_level": 4,
       "tool_type": "pickaxe"
+    },
+    "snow": {
+      "top": 14,
+      "sprite_top": 14,
+      "collision": false,
+      "transparent": true,
+      "drop": "snowball",
+      "texture": "snow",
+      "hp": 6,
+      "tool_level": 1,
+      "tool_type": "shovel",
+      "block_required": true
+    },
+    "snow_block": {
+      "collision": true,
+      "transparent": true,
+      "drop": "snowball",
+      "drop_count": 4,
+      "texture": "snow",
+      "hp": 60,
+      "tool_level": 1,
+      "tool_type": "shovel"
+    },
+    "furnace": {
+      "hp": 1050,
+      "collision": true,
+      "transparent": false,
+      "drop": "furnace",
+      "texture": "furnace",
+      "animated": true,
+      "frames": 2,
+      "tool_level": 1,
+      "tool_type": "pickaxe",
+      "meta": "furnace"
+    },
+    "chest": {
+      "hp": 180,
+      "collision": true,
+      "transparent": true,
+      "drop": "chest",
+      "texture": "chest",
+      "tool_level": 0,
+      "tool_type": "axe",
+      "meta": "chest",
+      "top": 2,
+      "left": 1,
+      "right": 1,
+      "sprite_top": 2,
+      "sprite_left": 1,
+      "sprite_right": 1
     }
   },
   "items": {
     "cobblestone": {
       "name": "Cobblestone",
       "type": "block",
-      "texture": "cobblestone"
+      "texture": "cobblestone",
+      "smelt_product": "stone"
     },
     "planks_oak": {
       "name": "Oak Planks",
       "type": "block",
-      "texture": "planks_oak"
+      "texture": "planks_oak",
+      "burning_time": 15000
     },
     "sapling_oak": {
       "name": "Oak Sapling",
       "type": "block",
-      "texture": "sapling_oak"
+      "texture": "sapling_oak",
+      "burning_time": 5000
     },
-    "bedrock": {
-      "name": "Bedrock",
+    "planks_spruce": {
+      "name": "Spruce Planks",
       "type": "block",
-      "texture": "bedrock"
+      "texture": "planks_spruce",
+      "burning_time": 15000
     },
-    "water": {
-      "name": "Water",
+    "crafting_table": {
+      "name": "Crafting Table",
       "type": "block",
-      "texture": "water"
+      "texture": "crafting_table",
+      "burning_time": 15000
     },
-    "lava": {
-      "name": "Lava",
+    "furnace": {
+      "name": "Furnace",
+      "type": "block",
+      "texture": "furnace_off"
+    },
+    "chest": {
+      "name": "Chest",
+      "type": "block",
+      "texture": "chest"
+    },
+    "sapling_spruce": {
+      "name": "Spruce Sapling",
+      "type": "block",
+      "texture": "sapling_spruce",
+      "burning_time": 5000
+    },
+    "bedrock": {
+      "name": "Bedrock",
       "type": "block",
-      "texture": "lava"
+      "texture": "bedrock"
     },
     "sand": {
       "name": "Sand",
       "type": "block",
-      "texture": "sand"
+      "texture": "sand",
+      "smelt_product": "glass"
     },
     "gravel": {
       "name": "Gravel",
     "gold_ore": {
       "name": "Golden Ore",
       "type": "block",
-      "texture": "gold_ore"
+      "texture": "gold_ore",
+      "smelt_product": "gold_ingot"
     },
     "iron_ore": {
       "name": "Iron Ore",
       "type": "block",
-      "texture": "iron_ore"
+      "texture": "iron_ore",
+      "smelt_product": "iron_ingot"
     },
     "coal_ore": {
       "name": "Coal Ore",
       "type": "block",
-      "texture": "coal_ore"
+      "texture": "coal_ore",
+      "smelt_product": "coal"
     },
     "diamond_ore": {
       "name": "Diamond Ore",
       "type": "block",
-      "texture": "diamond_ore"
+      "texture": "diamond_ore",
+      "smelt_product": "diamond"
     },
     "log_oak": {
       "name": "Wood",
       "type": "block",
-      "texture": "log_oak"
+      "texture": "log_oak",
+      "burning_time": 15000,
+      "smelt_product": "charcoal"
     },
     "leaves_oak": {
       "name": "Leaves",
       "type": "block",
       "texture": "leaves_oak"
     },
+    "log_spruce": {
+      "name": "Spruce",
+      "type": "block",
+      "texture": "log_spruce",
+      "burning_time": 15000,
+      "smelt_product": "charcoal"
+    },
+    "leaves_spruce": {
+      "name": "Spruce Leaves",
+      "type": "block",
+      "texture": "leaves_spruce"
+    },
     "glass": {
       "name": "Glass",
       "type": "block",
     "lapis_ore": {
       "name": "Lapis Ore",
       "type": "block",
-      "texture": "lapis_ore"
+      "texture": "lapis_ore",
+      "smelt_product": "lapis_lazuli"
     },
     "lapis_block": {
       "name": "Lapis Block",
       "name": "Dead Bush",
       "type": "block",
       "texture": "deadbush",
-      "origin_x": 0.5
+      "origin_x": 0.5,
+      "burning_time": 5000
     },
     "bricks": {
       "name": "Bricks",
       "type": "slab",
       "texture": "oak_slab",
       "top_slab_block": "oak_slab_top",
-      "bottom_slab_block": "oak_slab_bottom"
+      "bottom_slab_block": "oak_slab_bottom",
+      "burn_time": 7500
+    },
+    "spruce_slab": {
+      "name": "Spruce Slab",
+      "type": "slab",
+      "texture": "spruce_slab",
+      "top_slab_block": "spruce_slab_top",
+      "bottom_slab_block": "spruce_slab_bottom",
+      "burn_time": 7500
     },
     "cobblestone_slab": {
       "name": "Cobblestone Slab",
       "type": "block",
       "texture": "obsidian"
     },
+    "snow_block": {
+      "name": "Snow Block",
+      "type": "block",
+      "texture": "snow"
+    },
     "stick": {
       "name": "Stick",
-      "texture": "stick"
+      "texture": "stick",
+      "burning_time": 5000
     },
     "wood_sword": {
       "name": "Wooden Sword",
       "texture": "wood_sword",
       "origin_x": 0.125,
       "tool_level": 1,
-      "max_stack": 60
+      "max_stack": 60,
+      "burning_time": 10000
     },
     "stone_sword": {
       "name": "Stone Sword",
       "texture": "wood_pickaxe",
       "origin_x": 0.125,
       "tool_level" : 1,
-      "max_stack": 59
+      "max_stack": 59,
+      "burning_time": 10000
     },
     "stone_pickaxe": {
       "name": "Stone Pickaxe",
       "texture": "wood_axe",
       "origin_x": 0.125,
       "tool_level" : 1,
-      "max_stack": 60
+      "max_stack": 60,
+      "burning_time": 10000
     },
     "stone_axe": {
       "name": "Stone Axe",
     },
     "bucket_empty": {
       "name": "Empty Bucket",
-      "type": "bucket",
+      "type": "usable",
       "texture": "bucket_empty",
       "origin_x": 0.25,
       "action_key": "use_empty_bucket",
     },
     "bucket_water": {
       "name": "Water Bucket",
-      "type": "bucket",
+      "type": "usable",
       "texture": "bucket_water",
       "origin_x": 0.25,
       "action_key": "use_water_bucket",
     },
     "bucket_lava": {
       "name": "Lava Bucket",
-      "type": "bucket",
+      "type": "usable",
       "texture": "bucket_lava",
       "origin_x": 0.25,
       "action_key": "use_lava_bucket",
-      "max_stack": 1
+      "max_stack": 1,
+      "burning_time": 1000000
+    },
+    "snowball": {
+      "name": "Snowball",
+      "type": "usable",
+      "texture": "snowball",
+      "action_key": "use_snow_ball_action",
+      "max_stack": 16
+    },
+    "coal": {
+      "name": "Coal",
+      "texture": "coal",
+      "burning_time": 80000,
+      "origin_x": 0.25
+    },
+    "charcoal": {
+      "name": "Charcoal",
+      "texture": "charcoal",
+      "burning_time": 80000,
+      "origin_x": 0.25
+    },
+    "iron_ingot": {
+      "name": "Iron Ingot",
+      "texture": "iron_ingot",
+      "origin_x": 0.5
+    },
+    "gold_ingot": {
+      "name": "Gold Ingot",
+      "texture": "gold_ingot",
+      "origin_x": 0.5
+    },
+    "diamond": {
+      "name": "Diamond",
+      "texture": "diamond",
+      "origin_x": 0.5
+    },
+    "lapis_lazuli": {
+      "name": "Lapis Lazuli",
+      "texture": "lapis_lazuli",
+      "origin_x": 0.5
+    },
+    "bed": {
+      "name": "Bed",
+      "type": "usable",
+      "texture": "bed",
+      "action_key": "use_bed_action"
+    },
+    "spawn_egg_pig": {
+      "name": "Pig Spawn Egg",
+      "type": "usable",
+      "texture": "spawn_egg",
+      "tint": "#EDBFB4",
+      "action_key": "use_spawn_egg_pig",
+      "origin_x": 0.5
     }
   }
 }
index c52f47ca32db60d012a7b8609b89477d03713a80..97d0938b4bdf4820636d4cf1fdd955b14deafa64 100644 (file)
@@ -6,6 +6,9 @@
     "label": "Load Game",
     "type": 0
   },
+  "options": {
+    "label": "Settings"
+  },
   "quit": {
     "label": "Quit"
   }
diff --git a/android/assets/json/menu_options_buttons.json b/android/assets/json/menu_options_buttons.json
new file mode 100644 (file)
index 0000000..ebe1bf7
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "dyncam": {
+    "label": "Dynamic Camera: %%isUseDynamicCamera%%"
+  },
+  "back": {
+    "label": "Back"
+  }
+}
\ No newline at end of file
index 800098081fedec8efe5483a3ba2af7803c742a71..3c6e7af2d8a356a88ce9b6d77979d4bcc2e8a0b7 100644 (file)
       "y": 26,
       "w": 26,
       "h": 26
+    },
+    "inv": {
+      "x": 78,
+      "y": 26,
+      "w": 26,
+      "h": 26
+    },
+    "pause": {
+      "x": 104,
+      "w": 26,
+      "h": 26
     }
   },
-  "allitems": {
+  "pp/allitems": {
     "creative": {
       "w": 176,
       "h": 136
       "h": 15
     }
   },
-  "inventory": {
+  "pp/inventory": {
     "survival": {
       "w": 176,
       "h": 166
     }
   },
-  "crafting_table": {
+  "pp/crafting_table": {
     "crafting_table": {
       "w": 176,
       "h": 166
     }
   },
-  "buttons": {
+  "pp/furnace": {
+    "furnace": {
+      "w": 176,
+      "h": 166
+    },
+    "furnace_burn": {
+      "x": 176,
+      "w": 14,
+      "h": 14
+    },
+    "furnace_progress": {
+      "x": 176,
+      "y": 14,
+      "w": 24,
+      "h": 14
+    }
+  },
+  "pp/chest": {
+    "chest": {
+      "w": 176,
+      "h": 168
+    }
+  },
+  "pp/buttons": {
     "button_0": {
       "w": 200,
       "h": 20
       "h": 20
     }
   },
-  "gui": {
+  "pp/gui": {
     "hotbar": {
       "y": 16,
       "w": 182,
       "h": 16
     }
   },
-  "shade": {},
+  "pp/shade": {},
   "gamelogo": {},
-  "background": {},
-  "health":{
+  "pp/background": {},
+  "pp/health":{
     "heart_whole": {
       "w": 9
     },
     "heart_half": {
       "x": 9,
       "w": 9
+    },
+    "heart_empty": {
+      "x": 18,
+      "w": 9
     }
   }
 }
\ No newline at end of file
index cddaf534397ea531931a299d935742d52e4b3b82..cbb9a151f2b4cdb9fba1096c3ddf820b411399ef 100644 (file)
@@ -1,53 +1,23 @@
 {
-  "up": {
-    "x": 26,
-    "y": -52,
-    "w": 26,
-    "h": 26,
-    "key": "W"
-  },
-  "down": {
-    "x": 26,
-    "y": -26,
-    "w": 26,
-    "h": 26,
-    "key": "S"
-  },
-  "left": {
-    "x": 0,
-    "y": -26,
-    "w": 26,
-    "h": 26,
-    "key": "A"
-  },
-  "right": {
-    "x": 52,
-    "y": -26,
-    "w": 26,
-    "h": 26,
-    "key": "D"
-  },
   "alt": {
-    "x": 78,
-    "y": -26,
-    "w": 26,
-    "h": 26,
+    "x": -48,
+    "y": -48,
+    "w": 48,
+    "h": 48,
     "key": "L-Alt"
   },
-  "lmb": {
-    "x": -52,
-    "y": -26,
-    "w": 26,
-    "h": 26,
-    "mouse": true,
-    "key": "Left"
+  "inv": {
+    "x": -96,
+    "y": -48,
+    "w": 48,
+    "h": 48,
+    "key": "E"
   },
-  "rmb": {
-    "x": -26,
-    "y": -26,
-    "w": 26,
-    "h": 26,
-    "mouse": true,
-    "key": "Right"
+  "pause": {
+    "x": -48,
+    "y": 0,
+    "w": 48,
+    "h": 48,
+    "key": "Escape"
   }
 }
\ No newline at end of file
diff --git a/android/assets/pp/chest.png b/android/assets/pp/chest.png
new file mode 100644 (file)
index 0000000..2c6d0c6
Binary files /dev/null and b/android/assets/pp/chest.png differ
diff --git a/android/assets/pp/chest_large.png b/android/assets/pp/chest_large.png
new file mode 100644 (file)
index 0000000..961891f
Binary files /dev/null and b/android/assets/pp/chest_large.png differ
diff --git a/android/assets/pp/furnace.png b/android/assets/pp/furnace.png
new file mode 100644 (file)
index 0000000..fc04793
Binary files /dev/null and b/android/assets/pp/furnace.png differ
diff --git a/android/assets/pp/health.png b/android/assets/pp/health.png
new file mode 100644 (file)
index 0000000..99011d5
Binary files /dev/null and b/android/assets/pp/health.png differ
diff --git a/android/assets/pp/textures/blocks/chest.png b/android/assets/pp/textures/blocks/chest.png
new file mode 100644 (file)
index 0000000..a2ca371
Binary files /dev/null and b/android/assets/pp/textures/blocks/chest.png differ
diff --git a/android/assets/pp/textures/blocks/furnace.png b/android/assets/pp/textures/blocks/furnace.png
new file mode 100644 (file)
index 0000000..080d8ad
Binary files /dev/null and b/android/assets/pp/textures/blocks/furnace.png differ
diff --git a/android/assets/pp/textures/blocks/lapis_ore.png b/android/assets/pp/textures/blocks/lapis_ore.png
new file mode 100644 (file)
index 0000000..44fcd7f
Binary files /dev/null and b/android/assets/pp/textures/blocks/lapis_ore.png differ
diff --git a/android/assets/pp/textures/blocks/leaves_spruce.png b/android/assets/pp/textures/blocks/leaves_spruce.png
new file mode 100644 (file)
index 0000000..0be1897
Binary files /dev/null and b/android/assets/pp/textures/blocks/leaves_spruce.png differ
diff --git a/android/assets/pp/textures/blocks/snow.png b/android/assets/pp/textures/blocks/snow.png
new file mode 100644 (file)
index 0000000..20a836e
Binary files /dev/null and b/android/assets/pp/textures/blocks/snow.png differ
diff --git a/android/assets/pp/textures/items/bed.png b/android/assets/pp/textures/items/bed.png
new file mode 100644 (file)
index 0000000..0e1565d
Binary files /dev/null and b/android/assets/pp/textures/items/bed.png differ
diff --git a/android/assets/pp/textures/items/charcoal.png b/android/assets/pp/textures/items/charcoal.png
new file mode 100644 (file)
index 0000000..9fe49a7
Binary files /dev/null and b/android/assets/pp/textures/items/charcoal.png differ
diff --git a/android/assets/pp/textures/items/coal.png b/android/assets/pp/textures/items/coal.png
new file mode 100644 (file)
index 0000000..c94ebad
Binary files /dev/null and b/android/assets/pp/textures/items/coal.png differ
diff --git a/android/assets/pp/textures/items/diamond.png b/android/assets/pp/textures/items/diamond.png
new file mode 100644 (file)
index 0000000..eff11e0
Binary files /dev/null and b/android/assets/pp/textures/items/diamond.png differ
diff --git a/android/assets/pp/textures/items/gold_ingot.png b/android/assets/pp/textures/items/gold_ingot.png
new file mode 100644 (file)
index 0000000..b159455
Binary files /dev/null and b/android/assets/pp/textures/items/gold_ingot.png differ
diff --git a/android/assets/pp/textures/items/iron_ingot.png b/android/assets/pp/textures/items/iron_ingot.png
new file mode 100644 (file)
index 0000000..1ff5c69
Binary files /dev/null and b/android/assets/pp/textures/items/iron_ingot.png differ
diff --git a/android/assets/pp/textures/items/lapis_lazuli.png b/android/assets/pp/textures/items/lapis_lazuli.png
new file mode 100644 (file)
index 0000000..84bdcd4
Binary files /dev/null and b/android/assets/pp/textures/items/lapis_lazuli.png differ
diff --git a/android/assets/pp/textures/items/snowball.png b/android/assets/pp/textures/items/snowball.png
new file mode 100644 (file)
index 0000000..56a11e9
Binary files /dev/null and b/android/assets/pp/textures/items/snowball.png differ
diff --git a/android/assets/pp/textures/items/spawn_egg.png b/android/assets/pp/textures/items/spawn_egg.png
new file mode 100644 (file)
index 0000000..8733a83
Binary files /dev/null and b/android/assets/pp/textures/items/spawn_egg.png differ
diff --git a/android/assets/textures/blocks/furnace_off.png b/android/assets/textures/blocks/furnace_off.png
deleted file mode 100644 (file)
index 6d9b405..0000000
Binary files a/android/assets/textures/blocks/furnace_off.png and /dev/null differ
diff --git a/android/assets/textures/blocks/furnace_on.png b/android/assets/textures/blocks/furnace_on.png
deleted file mode 100644 (file)
index ecbc607..0000000
Binary files a/android/assets/textures/blocks/furnace_on.png and /dev/null differ
diff --git a/android/assets/textures/blocks/lapis_ore.png b/android/assets/textures/blocks/lapis_ore.png
deleted file mode 100644 (file)
index 04a4630..0000000
Binary files a/android/assets/textures/blocks/lapis_ore.png and /dev/null differ
index b1944137a192cc296ac26d7233486d182bc61ec0..57dffc457a9ed27fa82a208a71a0f5deb0922c9a 100644 (file)
Binary files a/android/assets/touch_gui.png and b/android/assets/touch_gui.png differ
index d64557af4eb2ecfae920cabe31b77c3ad2b861a6..80d010c8221e89caec14c6e48b4233ea2044ae40 100644 (file)
@@ -34,8 +34,8 @@ android {
         applicationId "ru.deadsoftware.cavedroid"
         minSdkVersion 24
         targetSdkVersion 34
-        versionCode 19
-        versionName "alpha0.6.2"
+        versionCode 25
+        versionName "alpha0.9.2"
     }
     applicationVariants.all { variant ->
         variant.outputs.all {
index dddba92f5feeb17f84c52887f85a19ccd68f6fcc..2a9831aeabf8fd040f5b1bb7324d24a877918243 100644 (file)
@@ -25,7 +25,7 @@ buildscript {
 
 allprojects {
 
-    version = 'alpha0.6.2'
+    version = 'alpha0.9.2'
 
     repositories {
         mavenLocal()
index 3960176d059f885a66c13c5163225b0211c4bde9..934055296938f3a4e2eb2533fbf1649fd6ce9893 100644 (file)
@@ -13,7 +13,7 @@ public class CaveGame extends Game {
 
     private static final String TAG = "CaveGame";
 
-    public static final String VERSION = "alpha 0.6.2";
+    public static final String VERSION = "alpha 0.9.2";
 
     private final MainConfig mMainConfig;
     private final MainComponent mMainComponent;
@@ -42,7 +42,7 @@ public class CaveGame extends Game {
     }
 
     private void initConfig() {
-        int width = mTouch ? 320 : 480;
+        int width = 480;
         int height = (int) (width * ((float) Gdx.graphics.getHeight() / Gdx.graphics.getWidth()));
 
         mMainConfig.setMainComponent(mMainComponent);
index 6577f297254f82928b2e18d28f29911d32764750..64950f2d43e17325d3afd348d073c066ae989088 100644 (file)
@@ -1,6 +1,7 @@
 package ru.deadsoftware.cavedroid;
 
 import ru.deadsoftware.cavedroid.game.GameUiWindow;
+import ru.deadsoftware.cavedroid.game.input.Joystick;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -15,6 +16,9 @@ public class MainConfig {
     @CheckForNull
     private MainComponent mMainComponent;
 
+    @CheckForNull
+    private Joystick mJoystick;
+
     private GameUiWindow mGameUiWindow;
     private String mGameFolder;
 
@@ -25,6 +29,8 @@ public class MainConfig {
     private float mWidth;
     private float mHeight;
 
+    private boolean mUseDynamicCamera = true;
+
     @Nullable
     private String mAssetsPackPath = null;
 
@@ -113,4 +119,21 @@ public class MainConfig {
     public void setAssetsPackPath(@Nullable String assetsPackPath) {
         mAssetsPackPath = assetsPackPath;
     }
+
+    @CheckForNull
+    public Joystick getJoystick() {
+        return mJoystick;
+    }
+
+    public void setJoystick(@CheckForNull Joystick joystick) {
+        mJoystick = joystick;
+    }
+
+    public boolean isUseDynamicCamera() {
+        return mUseDynamicCamera;
+    }
+
+    public void setUseDynamicCamera(boolean useDynamicCamera) {
+        mUseDynamicCamera = useDynamicCamera;
+    }
 }
index 2dafcd5e0346fb00a7d2fec91d132f7bce74a664..624e544ec17034d6bba141a35b28e250ea4cbab3 100644 (file)
@@ -4,6 +4,7 @@ import dagger.Component;
 import ru.deadsoftware.cavedroid.MainComponent;
 import ru.deadsoftware.cavedroid.game.actions.PlaceBlockActionsModule;
 import ru.deadsoftware.cavedroid.game.actions.UpdateBlockActionsModule;
+import ru.deadsoftware.cavedroid.game.actions.UseBlockActionsModule;
 import ru.deadsoftware.cavedroid.game.actions.UseItemActionsModule;
 import ru.deadsoftware.cavedroid.game.input.KeyboardInputHandlersModule;
 import ru.deadsoftware.cavedroid.game.input.MouseInputHandlersModule;
@@ -17,7 +18,8 @@ import ru.deadsoftware.cavedroid.game.render.RenderModule;
                 PlaceBlockActionsModule.class,
                 RenderModule.class,
                 KeyboardInputHandlersModule.class,
-                MouseInputHandlersModule.class
+                MouseInputHandlersModule.class,
+                UseBlockActionsModule.class
         })
 public interface GameComponent {
     GameProc getGameProc();
index 832845eb0c11f60f2ae6168661d1a99961b28c37..050ae6fe442acacc4d3c20211d94b7200738182c 100644 (file)
@@ -83,7 +83,7 @@ class GameItemsHolder @Inject constructor(
 
         jsonMap.forEach { (key, value) ->
             craftingRecipes += CraftingRecipe(
-                input = value.input.map(::getItem),
+                input = value.input.map(::Regex),
                 output = CraftingResult(getItem(key), value.count)
             )
         }
@@ -131,8 +131,20 @@ 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 -> rec.input == input}.output.toInventoryItem()
+            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
         }
index 29175da1968fba60e36f64a8ebb8ec746c73b630..d57ab337e9e49c0cbc872ee037cb42b407908d24 100644 (file)
@@ -5,7 +5,9 @@ import dagger.Provides;
 import ru.deadsoftware.cavedroid.MainConfig;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
 import ru.deadsoftware.cavedroid.game.model.block.Block;
-import ru.deadsoftware.cavedroid.game.objects.DropController;
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController;
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController;
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
 import javax.annotation.CheckForNull;
@@ -44,11 +46,25 @@ public class GameModule {
 
     @Provides
     @GameScope
-    public static MobsController provideMobsController(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
+    public static ContainerController provideFurnaceController(MainConfig mainConfig, DropController dropController, GameItemsHolder gameItemsHolder) {
         load(mainConfig, gameItemsHolder);
-        MobsController controller = data != null ? data.retrieveMobsController() : new MobsController(gameItemsHolder);
+        ContainerController controller = data != null ? data.retrieveFurnaceController() : new ContainerController(dropController, gameItemsHolder);
         makeDataNullIfEmpty();
-        controller.getPlayer().initInventory(gameItemsHolder);
+        controller.init(dropController, gameItemsHolder);
+        return controller;
+    }
+
+    @Provides
+    @GameScope
+    public static MobsController provideMobsController(MainConfig mainConfig,
+                                                       GameItemsHolder gameItemsHolder,
+                                                       TooltipManager tooltipManager) {
+        load(mainConfig, gameItemsHolder);
+        MobsController controller = data != null
+                ? data.retrieveMobsController()
+                : new MobsController(gameItemsHolder, tooltipManager);
+        makeDataNullIfEmpty();
+        controller.getPlayer().initInventory(gameItemsHolder, tooltipManager);
         return controller;
     }
 
@@ -57,12 +73,13 @@ public class GameModule {
     public static GameWorld provideGameWorld(MainConfig mainConfig,
                                              DropController dropController,
                                              MobsController mobsController,
-                                             GameItemsHolder gameItemsHolder) {
+                                             GameItemsHolder gameItemsHolder,
+                                             ContainerController containerController) {
         load(mainConfig, gameItemsHolder);
         Block[][] fm = data != null ? data.retrieveForeMap() : null;
         Block[][] bm = data != null ? data.retrieveBackMap() : null;
         makeDataNullIfEmpty();
-        return new GameWorld(dropController, mobsController, gameItemsHolder, fm, bm);
+        return new GameWorld(dropController, mobsController, gameItemsHolder, containerController, fm, bm);
     }
 
 }
index a0c8c378ef3c35e6006e99dab47b192fb669d75c..cb5b37e35a5d5a0c157c225cfac938e4154dbdfc 100644 (file)
@@ -7,10 +7,10 @@ import com.badlogic.gdx.math.Vector2;
 import ru.deadsoftware.cavedroid.MainConfig;
 import ru.deadsoftware.cavedroid.game.mobs.Mob;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
-import ru.deadsoftware.cavedroid.game.mobs.Player;
+import ru.deadsoftware.cavedroid.game.mobs.player.Player;
 import ru.deadsoftware.cavedroid.game.model.block.Block;
-import ru.deadsoftware.cavedroid.game.objects.Drop;
-import ru.deadsoftware.cavedroid.game.objects.DropController;
+import ru.deadsoftware.cavedroid.game.objects.drop.Drop;
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
 import javax.annotation.CheckForNull;
@@ -56,7 +56,7 @@ public class GamePhysics {
         int blY = (int) (mob.getY() + mob.getHeight() - 8);
         Block block = mGameWorld.getForeMap(blX / 16, blY / 16);
 
-        if (checkColl(new Rectangle(blX, mob.getY() - 18, mob.getWidth(), mob.getHeight()))) {
+        if (checkColl(new Rectangle(blX, mob.getY() - 18, mob.getWidth(), mob.getHeight())) != null) {
             return false;
         }
 
@@ -64,7 +64,11 @@ public class GamePhysics {
                 (mob.getY() + mob.getHeight()) - block.getRectangle(blX / 16, blY / 16).y > 8);
     }
 
-    private boolean checkColl(Rectangle rect) {
+    /**
+     * @return colliding rect or null if no collision
+     */
+    @CheckForNull
+    private Rectangle checkColl(Rectangle rect) {
         int minX = (int) ((rect.x + rect.width / 2) / 16) - 4;
         int minY = (int) ((rect.y + rect.height / 2) / 16) - 4;
         int maxX = (int) ((rect.x + rect.width / 2) / 16) + 4;
@@ -86,14 +90,15 @@ public class GamePhysics {
                 }
                 block = mGameWorld.getForeMap(x, y);
                 if (block.hasCollision()) {
-                    if (Intersector.overlaps(rect, block.getRectangle(x, y))) {
-                        return true;
+                    final Rectangle blockRect = block.getRectangle(x, y);
+                    if (Intersector.overlaps(rect, blockRect)) {
+                        return blockRect;
                     }
                 }
             }
         }
 
-        return false;
+        return null;
     }
 
     private Block getBlock(Rectangle rect) {
@@ -113,6 +118,10 @@ public class GamePhysics {
     private Rectangle getShiftedMagnetingPlayerRect(Drop drop) {
         final Player player = mMobsController.getPlayer();
 
+        if (!player.inventory.canPickItem(drop)) {
+            return null;
+        }
+
         if (drop.canMagnetTo(player)) {
             return getShiftedPlayerRect(0);
         }
@@ -134,7 +143,7 @@ public class GamePhysics {
         final Player player = mMobsController.getPlayer();
 
         if (Intersector.overlaps(shiftedPlayerTarget, drop)) {
-            player.pickUpDrop(drop);
+            player.inventory.pickDrop(drop);
         }
     }
 
@@ -158,11 +167,11 @@ public class GamePhysics {
         drop.x += dropVelocity.x * delta;
         drop.y += dropVelocity.y * delta;
 
-        if (checkColl(drop)) {
+        if (checkColl(drop) != null) {
             dropVelocity.setZero();
             do {
                 drop.y--;
-            } while (checkColl(drop));
+            } while (checkColl(drop) != null);
         }
 
         if (playerMagnetTarget != null) {
@@ -171,16 +180,21 @@ public class GamePhysics {
     }
 
     private void mobXColl(Mob mob) {
-        if (checkColl(mob)) {
-            if (mob.canJump() && !mob.isFlyMode()) {
-                mob.y -= 8;
+        if (mob.getVelocity().x == 0f) {
+            return;
+        }
+
+        @CheckForNull Rectangle collidingRect = checkColl(mob);
+
+        if (collidingRect != null) {
+            if (mob.canJump() && !mob.isFlyMode() && collidingRect.y >= mob.y + mob.height - 8) {
+                mob.y = collidingRect.y - mob.height;
+                return;
             }
 
-            if (checkColl(mob)) {
-                if (mob.canJump() && !mob.isFlyMode()) {
-                    mob.y += 8;
-                }
+            collidingRect = checkColl(mob);
 
+            if (collidingRect != null) {
                 int d = 0;
 
                 if (mob.getVelocity().x < 0) {
@@ -189,12 +203,17 @@ public class GamePhysics {
                     d = -1;
                 }
 
-                mob.x = MathUtils.round(mob.getX());
-
-                while (checkColl(mob)) {
-                    mob.x += d;
+                if (d < 0) {
+                    mob.x = collidingRect.x - mob.width;
+                } else {
+                    mob.x = collidingRect.x + collidingRect.width;
                 }
 
+//                mob.x = MathUtils.round(mob.getX());
+//                while (checkColl(mob) != null) {
+//                    mob.x += d;
+//                }
+
                 if (mob.canJump()) {
                     mob.changeDir();
                 }
@@ -205,7 +224,8 @@ public class GamePhysics {
     }
 
     private void mobYColl(Mob mob) {
-        if (checkColl(mob)) {
+        @CheckForNull final Rectangle collidingRect = checkColl(mob);
+        if (collidingRect != null) {
             int d = -1;
 
             if (mob.getVelocity().y < 0) {
@@ -222,17 +242,24 @@ public class GamePhysics {
                 }
             }
 
-            mob.y = MathUtils.round(mob.getY());
-
-            while (checkColl(mob)) {
-                mob.y += d;
+            if (d < 0) {
+                mob.y = collidingRect.y - mob.height;
+            } else {
+                mob.y = collidingRect.y + collidingRect.height;
             }
 
+
+//            mob.y = MathUtils.round(mob.getY());
+//
+//            while (checkColl(mob)) {
+//                mob.y += d;
+//            }
+
             mob.getVelocity().y = 0;
 
         } else {
             mob.y += 1;
-            mob.setCanJump(checkColl(mob));
+            mob.setCanJump(checkColl(mob) != null);
             mob.y -= 1;
         }
 
@@ -327,7 +354,7 @@ public class GamePhysics {
 
         for (Iterator<Mob> it = mMobsController.getMobs().iterator(); it.hasNext(); ) {
             Mob mob = it.next();
-            mob.ai(mGameWorld, mGameItemsHolder, delta);
+            mob.ai(mGameWorld, mGameItemsHolder, mMobsController, delta);
             mobPhy(mob, delta);
             if (mob.isDead()) {
                 it.remove();
@@ -335,7 +362,7 @@ public class GamePhysics {
         }
 
         playerPhy(player, delta);
-        player.ai(mGameWorld, mGameItemsHolder, delta);
+        player.ai(mGameWorld, mGameItemsHolder, mMobsController, delta);
         if (player.isDead()) {
             player.respawn(mGameWorld, mGameItemsHolder);
         }
index 94cd34ba9729bac41f22b0c6df010b5b9ac0c3f9..29062a44a07d24deff2efcefa40a2ec542a8978a 100644 (file)
@@ -5,7 +5,8 @@ import com.badlogic.gdx.utils.Disposable;
 import com.badlogic.gdx.utils.Timer;
 import ru.deadsoftware.cavedroid.MainConfig;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
-import ru.deadsoftware.cavedroid.game.mobs.Player;
+import ru.deadsoftware.cavedroid.game.mobs.player.Player;
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController;
 import ru.deadsoftware.cavedroid.game.world.GameWorldBlocksLogicControllerTask;
 import ru.deadsoftware.cavedroid.game.world.GameWorldFluidsLogicControllerTask;
 import ru.deadsoftware.cavedroid.game.world.GameWorldMobDamageControllerTask;
@@ -18,6 +19,8 @@ public class GameProc implements Disposable {
     private final GamePhysics mGamePhysics;
     private final GameRenderer mGameRenderer;
     private final MobsController mMobsController;
+    private final ContainerController mContainerController;
+    private final GameItemsHolder mGameItemsHolder;
     private final GameWorldFluidsLogicControllerTask mGameWorldFluidsLogicControllerTask;
     private final GameWorldBlocksLogicControllerTask mGameWorldBlocksLogicControllerTask;
     private final GameWorldMobDamageControllerTask mGameWorldMobDamageControllerTask;
@@ -29,6 +32,8 @@ public class GameProc implements Disposable {
                     GamePhysics gamePhysics,
                     GameRenderer gameRenderer,
                     MobsController mobsController,
+                    ContainerController containerController,
+                    GameItemsHolder gameItemsHolder,
                     GameWorldFluidsLogicControllerTask gameWorldFluidsLogicControllerTask,
                     GameWorldBlocksLogicControllerTask gameWorldBlocksLogicControllerTask,
                     GameWorldMobDamageControllerTask gameWorldMobDamageControllerTask
@@ -36,6 +41,8 @@ public class GameProc implements Disposable {
         mGamePhysics = gamePhysics;
         mGameRenderer = gameRenderer;
         mMobsController = mobsController;
+        mContainerController = containerController;
+        mGameItemsHolder = gameItemsHolder;
         mGameWorldFluidsLogicControllerTask = gameWorldFluidsLogicControllerTask;
         mGameWorldBlocksLogicControllerTask = gameWorldBlocksLogicControllerTask;
         mGameWorldMobDamageControllerTask = gameWorldMobDamageControllerTask;
@@ -57,6 +64,7 @@ public class GameProc implements Disposable {
     public void update(float delta) {
         mGamePhysics.update(delta);
         mGameRenderer.render(delta);
+        mContainerController.update();
     }
 
     public void show() {
index e96f3af48e3936fb31ccb99ecfac36a8b67d6774..2ae4756f54ab06b08b1c309ca3330348f4726d3c 100644 (file)
@@ -1,11 +1,16 @@
 package ru.deadsoftware.cavedroid.game;
 
 import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Input;
+import com.badlogic.gdx.graphics.Color;
 import com.badlogic.gdx.graphics.GL20;
 import com.badlogic.gdx.math.Rectangle;
+import com.badlogic.gdx.math.Vector2;
 import com.badlogic.gdx.utils.ObjectMap;
+import com.badlogic.gdx.utils.TimeUtils;
 import ru.deadsoftware.cavedroid.MainConfig;
 import ru.deadsoftware.cavedroid.game.input.IGameInputHandler;
+import ru.deadsoftware.cavedroid.game.input.Joystick;
 import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction;
 import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction;
 import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey;
@@ -13,12 +18,16 @@ import ru.deadsoftware.cavedroid.game.input.handler.mouse.CursorMouseInputHandle
 import ru.deadsoftware.cavedroid.game.input.mapper.KeyboardInputActionMapper;
 import ru.deadsoftware.cavedroid.game.input.mapper.MouseInputActionMapper;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
-import ru.deadsoftware.cavedroid.game.mobs.Player;
+import ru.deadsoftware.cavedroid.game.mobs.player.Player;
 import ru.deadsoftware.cavedroid.game.objects.TouchButton;
 import ru.deadsoftware.cavedroid.game.render.IGameRenderer;
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager;
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager;
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager;
+import ru.deadsoftware.cavedroid.game.world.GameWorld;
 import ru.deadsoftware.cavedroid.misc.Assets;
 import ru.deadsoftware.cavedroid.misc.Renderer;
+import ru.deadsoftware.cavedroid.misc.utils.MeasureUnitsUtilsKt;
+import ru.deadsoftware.cavedroid.misc.utils.RenderingUtilsKt;
 
 import javax.annotation.CheckForNull;
 import javax.inject.Inject;
@@ -30,10 +39,14 @@ import java.util.Set;
 @GameScope
 public class GameRenderer extends Renderer {
 
+    private static final float CAMERA_SPEED = 72f;
+    private static final float MAX_CAM_DISTANCE_FROM_PLAYER = 64f;
+    private static final float DRAG_THRESHOLD = 1f;
     private static final TouchButton nullButton = new TouchButton(null, -1, true);
 
     private final MainConfig mMainConfig;
     private final MobsController mMobsController;
+    private final GameWorld mGameWorld;
     private final List<IGameRenderer> mRenderers;
     private final CursorMouseInputHandler mCursorMouseInputHandler;
     private final MouseInputActionMapper mMouseInputActionMapper;
@@ -41,21 +54,32 @@ public class GameRenderer extends Renderer {
     private final Set<IGameInputHandler<MouseInputAction>> mMouseInputHandlers;
     private final Set<IGameInputHandler<KeyboardInputAction>> mKeyboardInputHandlers;
     private final GameWindowsManager mGameWindowsManager;
+    private final TooltipManager mTooltipManager;
+
+    private final TouchButton mouseLeftTouchButton, mouseRightTouchButton;
+
+    private final Vector2 mCamCenterToPlayer = new Vector2();
+
+    private float mTouchDownX, mTouchDownY;
+    private long mCameraDelayMs = 0L;
 
     @Inject
     GameRenderer(MainConfig mainConfig,
                  MobsController mobsController,
+                 GameWorld gameWorld,
                  Set<IGameRenderer> renderers,
                  CursorMouseInputHandler cursorMouseInputHandler,
                  MouseInputActionMapper mouseInputActionMapper,
                  KeyboardInputActionMapper keyboardInputActionMapper,
                  Set<IGameInputHandler<MouseInputAction>> mouseInputHandlers,
                  Set<IGameInputHandler<KeyboardInputAction>> keyboardInputHandlers,
-                 GameWindowsManager gameWindowsManager) {
+                 GameWindowsManager gameWindowsManager,
+                 TooltipManager tooltipManager) {
         super(mainConfig.getWidth(), mainConfig.getHeight());
 
         mMainConfig = mainConfig;
         mMobsController = mobsController;
+        mGameWorld = gameWorld;
         mRenderers = new ArrayList<>(renderers);
         mRenderers.sort(Comparator.comparingInt(IGameRenderer::getRenderLayer));
         mCursorMouseInputHandler = cursorMouseInputHandler;
@@ -64,16 +88,105 @@ public class GameRenderer extends Renderer {
         mMouseInputHandlers = mouseInputHandlers;
         mKeyboardInputHandlers = keyboardInputHandlers;
         mGameWindowsManager = gameWindowsManager;
+        mTooltipManager = tooltipManager;
+
+        mouseLeftTouchButton = new TouchButton(new Rectangle(getWidth() / 2, 0f, getWidth() / 2, getHeight() / 2), Input.Buttons.LEFT, true);
+        mouseRightTouchButton = new TouchButton(new Rectangle(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2), Input.Buttons.RIGHT, true);
+
+        mMainConfig.setJoystick(new Joystick(mMobsController.getPlayer().getSpeed()));
 
         Gdx.gl.glClearColor(0f, .6f, .6f, 1f);
     }
 
-    private float mTouchDownX, mTouchDownY;
+    private void updateDynamicCameraPosition(float delta) {
+        Player player = mMobsController.getPlayer();
+
+        float plTargetX = player.getX() + player.getWidth() / 2;
+        float plTargetY = player.getY() + player.getHeight() / 2;
+
+        float camCenterX = getCamX() + getWidth() / 2;
+        float camCenterY = getCamY() + getHeight() / 2;
+
+        float camTargetX, camTargetY;
+
+        boolean followPlayer = player.controlMode == Player.ControlMode.WALK || !mMainConfig.isTouch();
+
+        if (followPlayer) {
+            camTargetX = plTargetX + Math.min(player.getVelocity().x * 2, getWidth() / 2);
+            camTargetY = plTargetY + player.getVelocity().y;
+        } else {
+            camTargetX = MeasureUnitsUtilsKt.getPx(player.cursorX) + MeasureUnitsUtilsKt.getPx(1) / 2;
+            camTargetY = MeasureUnitsUtilsKt.getPx(player.cursorY) + MeasureUnitsUtilsKt.getPx(1) / 2;
+        }
+
+        Vector2 moveVector = new Vector2(camTargetX - camCenterX, camTargetY - camCenterY);
+
+        if (followPlayer && player.getVelocity().isZero()) {
+            mCameraDelayMs = TimeUtils.millis();
+            mCamCenterToPlayer.x = plTargetX - camCenterX;
+            mCamCenterToPlayer.y = plTargetY - camCenterY;
+        }
+
+        if (TimeUtils.timeSinceMillis(mCameraDelayMs) < 500L && !player.getVelocity().isZero()) {
+            updateStaticCameraPosition(plTargetX - mCamCenterToPlayer.x,
+                    camCenterY + moveVector.y * delta * 2);
+            return;
+        }
+
+        float camX = getCamX();
+        float camY = getCamY();
+        float worldWidth = MeasureUnitsUtilsKt.getPx(mGameWorld.getWidth()) - getWidth() / 2;
+
+        if (moveVector.x >= worldWidth) {
+            camX += mGameWorld.getWidthPx();
+            moveVector.x -= mGameWorld.getWidthPx();
+        } else if (moveVector.x <= -worldWidth) {
+            camX -= mGameWorld.getWidthPx();
+            moveVector.x += mGameWorld.getWidthPx();
+        }
+
+        setCamPos(camX + moveVector.x * delta * 2, camY + moveVector.y * delta * 2);
+
+
+        camX = getCamX();
+        camY = getCamY();
+
+        if (camX + getWidth() / 2 > plTargetX + MAX_CAM_DISTANCE_FROM_PLAYER) {
+            camX = plTargetX + MAX_CAM_DISTANCE_FROM_PLAYER - getWidth() / 2;
+        }
+
+        if (camY + getHeight() / 2 > plTargetY + MAX_CAM_DISTANCE_FROM_PLAYER) {
+            camY = plTargetY + MAX_CAM_DISTANCE_FROM_PLAYER - getHeight() / 2;
+        }
 
-    private void updateCameraPosition() {
+        if (camX + getWidth() / 2 < plTargetX - MAX_CAM_DISTANCE_FROM_PLAYER) {
+            camX = plTargetX - MAX_CAM_DISTANCE_FROM_PLAYER - getWidth() / 2;
+        }
+
+        if (camY + getHeight() / 2 < plTargetY - MAX_CAM_DISTANCE_FROM_PLAYER) {
+            camY = plTargetY - MAX_CAM_DISTANCE_FROM_PLAYER - getHeight() / 2;
+        }
+
+        setCamPos(camX, camY);
+    }
+
+    private void updateStaticCameraPosition(float targetX, float targetY) {
+        setCamPos(targetX - getWidth() / 2, targetY - getHeight() / 2);
+    }
+
+    private void updateStaticCameraPosition() {
         Player player = mMobsController.getPlayer();
-        setCamPos(player.getX() + player.getWidth() / 2 - getWidth() / 2,
-                player.getY() + player.getHeight() / 2 - getHeight() / 2);
+
+        updateStaticCameraPosition(player.getX() + player.getWidth() / 2,
+                player.getY() + player.getHeight() / 2);
+    }
+
+    private void updateCameraPosition(float delta) {
+        if (mMainConfig.isUseDynamicCamera()) {
+            updateDynamicCameraPosition(delta);
+        } else {
+            updateStaticCameraPosition();
+        }
     }
 
     private float transformScreenX(int screenX) {
@@ -86,13 +199,22 @@ public class GameRenderer extends Renderer {
 
     private void handleMousePosition() {
         final Rectangle viewport = getCameraViewport();
+
+        final float screenX = transformScreenX(Gdx.input.getX());
+        final float screenY = transformScreenY(Gdx.input.getY());
+
         final MouseInputAction action = new MouseInputAction(
-                Gdx.input.getX() * (viewport.width / Gdx.graphics.getWidth()),
-                Gdx.input.getY() * (viewport.height / Gdx.graphics.getHeight()),
+                screenX,
+                screenY,
                 MouseInputActionKey.None.INSTANCE,
                 viewport);
 
         mCursorMouseInputHandler.handle(action);
+
+        if (!mTooltipManager.getCurrentMouseTooltip().isEmpty()) {
+            RenderingUtilsKt.drawString(spriter, mTooltipManager.getCurrentMouseTooltip(), screenX + 1, screenY + 1, Color.BLACK);
+            RenderingUtilsKt.drawString(spriter, mTooltipManager.getCurrentMouseTooltip(), screenX, screenY, Color.WHITE);
+        }
     }
 
     private boolean handleMouseAction(@CheckForNull  MouseInputAction action) {
@@ -114,9 +236,9 @@ public class GameRenderer extends Renderer {
         return anyProcessed;
     }
 
-    private boolean onMouseActionEvent(int mouseX, int mouseY, int button, boolean touchUp) {
+    private boolean onMouseActionEvent(int mouseX, int mouseY, int button, boolean touchUp, int pointer) {
         @CheckForNull MouseInputAction action = mMouseInputActionMapper
-                .map((float) mouseX, (float) mouseY, getCameraViewport(), button, touchUp);
+                .map((float) mouseX, (float) mouseY, getCameraViewport(), button, touchUp, pointer);
         return handleMouseAction(action);
     }
 
@@ -125,16 +247,22 @@ public class GameRenderer extends Renderer {
         float touchX = transformScreenX(screenX);
         float touchY = transformScreenY(screenY);
 
+        final Joystick joy = mMainConfig.getJoystick();
+
         if (mMainConfig.isTouch()) {
+            if (joy != null && joy.getActive() && joy.getPointer() == pointer) {
+                return onMouseActionEvent(screenX, screenY, nullButton.getCode(), true, pointer);
+            }
+
             TouchButton touchedKey = getTouchedKey(touchX, touchY);
             if (touchedKey.isMouse()) {
-                return onMouseActionEvent(screenX, screenY, touchedKey.getCode(), true);
+                return onMouseActionEvent(screenX, screenY, touchedKey.getCode(), true, pointer);
             } else {
                 return keyUp(touchedKey.getCode());
             }
         }
 
-        return onMouseActionEvent(screenX, screenY, button, true);
+        return onMouseActionEvent(screenX, screenY, button, true, pointer);
     }
 
     private TouchButton getTouchedKey(float touchX, float touchY) {
@@ -147,6 +275,15 @@ public class GameRenderer extends Renderer {
                 return button;
             }
         }
+
+        if (mouseLeftTouchButton.getRect().contains(touchX, touchY)) {
+            return mouseLeftTouchButton;
+        }
+
+        if (mouseRightTouchButton.getRect().contains(touchX, touchY)) {
+            return mouseRightTouchButton;
+        }
+
         return nullButton;
     }
 
@@ -161,13 +298,13 @@ public class GameRenderer extends Renderer {
         if (mMainConfig.isTouch()) {
             TouchButton touchedKey = getTouchedKey(touchX, touchY);
             if (touchedKey.isMouse()) {
-                return onMouseActionEvent(screenX, screenY, touchedKey.getCode(), false);
+                return onMouseActionEvent(screenX, screenY, touchedKey.getCode(), false, pointer);
             } else {
                 return keyDown(touchedKey.getCode());
             }
         }
 
-        return onMouseActionEvent(screenX, screenY, button, false);
+        return onMouseActionEvent(screenX, screenY, button, false, pointer);
     }
 
     @Override
@@ -175,12 +312,12 @@ public class GameRenderer extends Renderer {
         float touchX = transformScreenX(screenX);
         float touchY = transformScreenY(screenY);
 
-        if (Math.abs(touchX - mTouchDownX) < 16 && Math.abs(touchY - mTouchDownY) < 16) {
+        if (Math.abs(touchX - mTouchDownX) < 16 && Math.abs(touchY - mTouchDownY) < DRAG_THRESHOLD) {
             return false;
         }
 
         @CheckForNull MouseInputAction action =
-                mMouseInputActionMapper.mapDragged(screenX, screenY, getCameraViewport());
+                mMouseInputActionMapper.mapDragged(screenX, screenY, getCameraViewport(), pointer);
         return handleMouseAction(action);
     }
 
@@ -225,15 +362,22 @@ public class GameRenderer extends Renderer {
 
     @Override
     public void render(float delta) {
-        updateCameraPosition();
-        handleMousePosition();
-//        mGameInput.moveCursor(this);
+        updateCameraPosition(delta);
+
+        if (mMainConfig.getJoystick() != null && mMainConfig.getJoystick().getActive()) {
+            mMainConfig.getJoystick().updateState(
+                    transformScreenX(Gdx.input.getX(mMainConfig.getJoystick().getPointer())),
+                    transformScreenY(Gdx.input.getY(mMainConfig.getJoystick().getPointer()))
+            );
+        }
 
         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
 
         spriter.begin();
         mRenderers.forEach(iGameRenderer -> iGameRenderer.draw(spriter, shaper, getCameraViewport(), delta));
+        handleMousePosition();
         spriter.end();
+
     }
 
 }
index cb52b3c76b126812e355c43d9ce076c7d5749140..39d227844ac1badb1d7898e28490d37019535e70 100644 (file)
@@ -5,7 +5,8 @@ import com.badlogic.gdx.files.FileHandle;
 import ru.deadsoftware.cavedroid.MainConfig;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
 import ru.deadsoftware.cavedroid.game.model.block.Block;
-import ru.deadsoftware.cavedroid.game.objects.DropController;
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController;
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
 import javax.annotation.CheckForNull;
@@ -24,11 +25,18 @@ public class GameSaver {
         @CheckForNull
         private DropController mDropController;
         @CheckForNull
+        private ContainerController mContainerController;
+        @CheckForNull
         private Block[][] mForeMap, mBackMap;
 
-        public Data(MobsController mobsController, DropController dropController, Block[][] foreMap, Block[][] backMap) {
+        public Data(MobsController mobsController,
+                    DropController dropController,
+                    ContainerController containerController,
+                    Block[][] foreMap,
+                    Block[][] backMap) {
             mMobsController = mobsController;
             mDropController = dropController;
+            mContainerController = containerController;
             mForeMap = foreMap;
             mBackMap = backMap;
         }
@@ -47,6 +55,13 @@ public class GameSaver {
             return dropController;
         }
 
+        public ContainerController retrieveFurnaceController() {
+            assert mContainerController != null;
+            ContainerController containerController = mContainerController;
+            mContainerController = null;
+            return containerController;
+        }
+
         public Block[][] retrieveForeMap() {
             assert mForeMap != null;
             Block[][] foreMap = mForeMap;
@@ -62,7 +77,11 @@ public class GameSaver {
         }
 
         public boolean isEmpty() {
-            return mMobsController == null && mDropController == null && mForeMap == null && mBackMap == null;
+            return mMobsController == null &&
+                    mDropController == null &&
+                    mContainerController == null &&
+                    mForeMap == null &&
+                    mBackMap == null;
         }
     }
 
@@ -183,10 +202,12 @@ public class GameSaver {
             int version = in.readInt();
             DropController dropController;
             MobsController mobsController;
+            ContainerController containerController;
 
             if (SAVE_VERSION == version) {
                 dropController = (DropController) in.readObject();
                 mobsController = (MobsController) in.readObject();
+                containerController = (ContainerController) in.readObject();
             } else {
                 throw new Exception("version mismatch");
             }
@@ -201,7 +222,7 @@ public class GameSaver {
                 throw new Exception("couldn't load");
             }
 
-            return new Data(mobsController, dropController, foreMap, backMap);
+            return new Data(mobsController, dropController, containerController, foreMap, backMap);
         } catch (Exception e) {
             Gdx.app.error("GameSaver", e.getMessage());
         }
@@ -212,6 +233,7 @@ public class GameSaver {
     public static void save(MainConfig mainConfig,
                             DropController dropController,
                             MobsController mobsController,
+                            ContainerController containerController,
                             GameWorld gameWorld) {
         String folder = mainConfig.getGameFolder();
         FileHandle file = Gdx.files.absolute(folder + "/saves/");
@@ -229,6 +251,7 @@ public class GameSaver {
             out.writeInt(SAVE_VERSION);
             out.writeObject(dropController);
             out.writeObject(mobsController);
+            out.writeObject(containerController);
             out.close();
 
             saveDict(Gdx.files.absolute(folder + "/saves/dict"), dict);
index 5dcfe0e72a80c987a6ac3227190855570d89e804..6f0fc0fcae365668b884095a250377119b6a18d4 100644 (file)
@@ -49,4 +49,20 @@ class UpdateBlockActionsModule {
     fun bindUpdateSnowedGrassAction(action: UpdateSnowedGrassAction): IUpdateBlockAction {
         return action;
     }
+
+    @Binds
+    @IntoMap
+    @StringKey(UpdateBedLeftAction.BLOCK_KEY)
+    @GameScope
+    fun bindUpdateBedLeftAction(action: UpdateBedLeftAction): IUpdateBlockAction {
+        return action;
+    }
+
+    @Binds
+    @IntoMap
+    @StringKey(UpdateBedRightAction.BLOCK_KEY)
+    @GameScope
+    fun bindUpdateBedRightAction(action: UpdateBedRightAction): IUpdateBlockAction {
+        return action;
+    }
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/UseBlockActionsModule.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/UseBlockActionsModule.kt
new file mode 100644 (file)
index 0000000..7458dfc
--- /dev/null
@@ -0,0 +1,39 @@
+package ru.deadsoftware.cavedroid.game.actions
+
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.actions.useblock.IUseBlockAction
+import ru.deadsoftware.cavedroid.game.actions.useblock.UseChestAction
+import ru.deadsoftware.cavedroid.game.actions.useblock.UseCraftingTableAction
+import ru.deadsoftware.cavedroid.game.actions.useblock.UseFurnaceAction
+
+@Module
+class UseBlockActionsModule {
+
+    @Binds
+    @IntoMap
+    @StringKey(UseCraftingTableAction.KEY)
+    @GameScope
+    fun bindUseCraftingTableAction(action: UseCraftingTableAction): IUseBlockAction {
+        return action
+    }
+
+    @Binds
+    @IntoMap
+    @StringKey(UseFurnaceAction.KEY)
+    @GameScope
+    fun bindUseFurnaceTableAction(action: UseFurnaceAction): IUseBlockAction {
+        return action
+    }
+
+    @Binds
+    @IntoMap
+    @StringKey(UseChestAction.KEY)
+    @GameScope
+    fun bindUseChestAction(action: UseChestAction): IUseBlockAction {
+        return action
+    }
+}
index 0206d5f82fe9c0fb61511d45e662a7dd1e8456ec..d5e4f4fa80c5cda9a10328c9834a9d4dc7ba089a 100644 (file)
@@ -34,4 +34,20 @@ class UseItemActionsModule {
         return action
     }
 
+    @Binds
+    @IntoMap
+    @StringKey(UsePigSpawnEggAction.ACTION_KEY)
+    @GameScope
+    fun bindUsePigSpawnEgg(action: UsePigSpawnEggAction): IUseItemAction {
+        return action
+    }
+
+    @Binds
+    @IntoMap
+    @StringKey(UseBedAction.ACTION_KEY)
+    @GameScope
+    fun bindUseBedAction(action: UseBedAction): IUseItemAction {
+        return action
+    }
+
 }
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateBedLeftAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateBedLeftAction.kt
new file mode 100644 (file)
index 0000000..15621a2
--- /dev/null
@@ -0,0 +1,26 @@
+package ru.deadsoftware.cavedroid.game.actions.updateblock
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.FallingGravel
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UpdateBedLeftAction @Inject constructor(
+    private val gameWorld: GameWorld,
+    private val gameItemsHolder: GameItemsHolder,
+) : IUpdateBlockAction {
+
+    override fun update(x: Int, y: Int) {
+        val bedRight = gameItemsHolder.getBlock("bed_r")
+        if (gameWorld.getForeMap(x + 1, y) != bedRight) {
+            gameWorld.resetForeMap(x, y)
+        }
+    }
+
+    companion object {
+        const val BLOCK_KEY = "bed_l"
+    }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateBedRightAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateBedRightAction.kt
new file mode 100644 (file)
index 0000000..4d47b35
--- /dev/null
@@ -0,0 +1,26 @@
+package ru.deadsoftware.cavedroid.game.actions.updateblock
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.FallingGravel
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UpdateBedRightAction @Inject constructor(
+    private val gameWorld: GameWorld,
+    private val gameItemsHolder: GameItemsHolder,
+) : IUpdateBlockAction {
+
+    override fun update(x: Int, y: Int) {
+        val bedLeft = gameItemsHolder.getBlock("bed_l")
+        if (gameWorld.getForeMap(x - 1, y) != bedLeft) {
+            gameWorld.resetForeMap(x, y)
+        }
+    }
+
+    companion object {
+        const val BLOCK_KEY = "bed_r"
+    }
+}
\ No newline at end of file
index 27005b8da53f4eb008c31ab26ba92b2568f11953..d5ca3b161c242b97e1e6931ad0ee64f66ccc5d3c 100644 (file)
@@ -13,8 +13,12 @@ class UpdateGrassAction @Inject constructor(
 
     override fun update(x: Int, y: Int) {
         val blockOnTop = gameWorld.getForeMap(x, y - 1)
-        if (blockOnTop.collision || blockOnTop.isFluid()) {
-            gameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("dirt"))
+
+        val makesDirt = blockOnTop.params.hasCollision || blockOnTop.isFluid()
+
+        when {
+            makesDirt -> gameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("dirt"))
+            blockOnTop.params.key == "snow" -> gameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("grass_snowed"))
         }
     }
 
index 8b7f0e34ecc9be0e650590edb9a152115d45e670..0aa5c90f9ae94e66617bee224415ff71cac841b2 100644 (file)
@@ -13,8 +13,11 @@ class UpdateSnowedGrassAction @Inject constructor(
 
     override fun update(x: Int, y: Int) {
         val blockOnTop = gameWorld.getForeMap(x, y - 1)
-        if (blockOnTop.collision || blockOnTop.isFluid()) {
-            gameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("dirt"))
+        val makesDirt = blockOnTop.params.hasCollision || blockOnTop.isFluid()
+
+        when {
+            makesDirt -> gameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("dirt"))
+            blockOnTop.params.key != "snow" -> gameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("grass"))
         }
     }
 
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useblock/IUseBlockAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useblock/IUseBlockAction.kt
new file mode 100644 (file)
index 0000000..ddc70c5
--- /dev/null
@@ -0,0 +1,9 @@
+package ru.deadsoftware.cavedroid.game.actions.useblock
+
+import ru.deadsoftware.cavedroid.game.model.block.Block
+
+interface IUseBlockAction {
+
+    fun perform(block: Block, x: Int, y: Int)
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseChestAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseChestAction.kt
new file mode 100644 (file)
index 0000000..fa50c94
--- /dev/null
@@ -0,0 +1,26 @@
+package ru.deadsoftware.cavedroid.game.actions.useblock
+
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.objects.container.Chest
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UseChestAction @Inject constructor(
+    private val gameWorld: GameWorld,
+    private val gameWindowsManager: GameWindowsManager,
+) : IUseBlockAction {
+
+    override fun perform(block: Block, x: Int, y: Int) {
+        val chest = (gameWorld.getForegroundContainer(x, y) as? Chest)
+            ?: (gameWorld.getBackgroundContainer(x, y) as? Chest)
+            ?: return
+        gameWindowsManager.openChest(chest)
+    }
+
+    companion object {
+        const val KEY = "chest"
+    }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseCraftingTableAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseCraftingTableAction.kt
new file mode 100644 (file)
index 0000000..3ce42b9
--- /dev/null
@@ -0,0 +1,20 @@
+package ru.deadsoftware.cavedroid.game.actions.useblock
+
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import javax.inject.Inject
+
+@GameScope
+class UseCraftingTableAction @Inject constructor(
+    private val gameWindowsManager: GameWindowsManager
+) : IUseBlockAction {
+
+    override fun perform(block: Block, x: Int, y: Int) {
+        gameWindowsManager.openCrafting()
+    }
+
+    companion object {
+        const val KEY = "crafting_table"
+    }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseFurnaceAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useblock/UseFurnaceAction.kt
new file mode 100644 (file)
index 0000000..7c30402
--- /dev/null
@@ -0,0 +1,23 @@
+package ru.deadsoftware.cavedroid.game.actions.useblock
+
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UseFurnaceAction @Inject constructor(
+    private val gameWorld: GameWorld,
+    private val gameWindowsManager: GameWindowsManager,
+) : IUseBlockAction {
+
+    override fun perform(block: Block, x: Int, y: Int) {
+        val furnace = gameWorld.getForegroundFurnace(x, y) ?: gameWorld.getBackgroundFurnace(x, y) ?: return
+        gameWindowsManager.openFurnace(furnace)
+    }
+
+    companion object {
+        const val KEY = "furnace"
+    }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseBedAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseBedAction.kt
new file mode 100644 (file)
index 0000000..e0f9964
--- /dev/null
@@ -0,0 +1,31 @@
+package ru.deadsoftware.cavedroid.game.actions.useitem
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UseBedAction @Inject constructor(
+    private val gameWorld: GameWorld,
+    private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
+) : IUseItemAction {
+
+    override fun perform(item: Item.Usable, x: Int, y: Int) {
+        val bedLeft = gameItemsHolder.getBlock("bed_l")
+        val bedRight = gameItemsHolder.getBlock("bed_r")
+
+        if (gameWorld.canPlaceToForeground(x, y, bedLeft) && gameWorld.canPlaceToForeground(x + 1, y, bedRight)) {
+            gameWorld.placeToForeground(x, y, bedLeft)
+            gameWorld.placeToForeground(x + 1, y, bedRight)
+            mobsController.player.inventory.decreaseCurrentItemAmount()
+        }
+    }
+
+    companion object {
+        const val ACTION_KEY = "use_bed_action"
+    }
+}
index 9f7c9f41d2ac7a854a6e5006311cf53ef44dfee3..57ca3f755305a5531b9cf972078d67e7cbcb6b48 100644 (file)
@@ -22,6 +22,7 @@ class UseEmptyBucketAction @Inject constructor(
         }
         gameWorld.resetForeMap(x, y)
 
+        @Suppress("REDUNDANT_ELSE_IN_WHEN")
         val filled = when (foregroundBlock) {
             is Block.Lava -> gameItemsHolder.getItem("bucket_lava")
             is Block.Water -> gameItemsHolder.getItem("bucket_water")
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UsePigSpawnEggAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UsePigSpawnEggAction.kt
new file mode 100644 (file)
index 0000000..6c0c3ba
--- /dev/null
@@ -0,0 +1,29 @@
+package ru.deadsoftware.cavedroid.game.actions.useitem
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Pig
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.misc.utils.px
+import javax.inject.Inject
+
+@GameScope
+class UsePigSpawnEggAction @Inject constructor(
+    private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
+) : IUseItemAction {
+
+    override fun perform(item: Item.Usable, x: Int, y: Int) {
+        Pig(mobsController.player.cursorX.px, mobsController.player.cursorY.px)
+            .apply {
+                attachToController(mobsController)
+            }
+
+        mobsController.player.decreaseCurrentItemCount(gameItemsHolder)
+    }
+
+    companion object {
+        const val ACTION_KEY = "use_spawn_egg_pig"
+    }
+}
index c8147d1987ce8cb7abe2bb05e1990b0d315c2ce6..51dc335c50b3b302ea8cb7ce7d2ee50efe0fbe52 100644 (file)
@@ -3,7 +3,8 @@ package ru.deadsoftware.cavedroid.game.debug
 import com.badlogic.gdx.Gdx
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.objects.DropController
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
 import ru.deadsoftware.cavedroid.game.world.GameWorld
 import javax.inject.Inject
 
@@ -11,7 +12,8 @@ import javax.inject.Inject
 class DebugInfoStringsProvider @Inject constructor(
     private val mobsController: MobsController,
     private val dropController: DropController,
-    private val gameWorld: GameWorld
+    private val containerController: ContainerController,
+    private val gameWorld: GameWorld,
 ) {
 
     fun getDebugStrings(): List<String> {
@@ -20,15 +22,16 @@ class DebugInfoStringsProvider @Inject constructor(
         return listOf(
             "FPS: ${Gdx.graphics.framesPerSecond}",
             "X: ${player.mapX}",
-            "Y: ${gameWorld.height - player.upperMapY}",
+            "Y: ${player.upperMapY} (${gameWorld.height - player.upperMapY})",
             "CurX: ${player.cursorX}",
             "CurY: ${player.cursorY}",
             "Velocity: ${player.velocity}",
             "Swim: ${player.swim}",
             "Mobs: ${mobsController.mobs.size}",
             "Drops: ${dropController.size}",
+            "Containers: ${containerController.size}",
             "Block: ${gameWorld.getForeMap(player.cursorX, player.cursorY).params.key}",
-            "Hand: ${player.inventory[player.slot].item.params.key}",
+            "Hand: ${player.inventory.activeItem.item.params.key}",
             "Game mode: ${player.gameMode}",
             "Block damage: ${player.blockDamage}"
         )
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/Joystick.kt b/core/src/ru/deadsoftware/cavedroid/game/input/Joystick.kt
new file mode 100644 (file)
index 0000000..6cbc052
--- /dev/null
@@ -0,0 +1,73 @@
+package ru.deadsoftware.cavedroid.game.input
+
+import com.badlogic.gdx.math.Vector2
+import com.badlogic.gdx.utils.TimeUtils
+
+class Joystick(
+    private val value: Float,
+) {
+
+    var active = false
+        private set
+    var centerX = 0f
+        private set
+    var centerY = 0f
+        private set
+
+    var activeX = 0f
+        private set
+    var activeY = 0f
+        private set
+
+    var pointer = 0
+        private set
+
+    private val stickVector = Vector2()
+
+    private var activateTimeMs = 0L
+
+    fun activate(touchX: Float, touchY: Float, pointer: Int) {
+        active = true
+        centerX = touchX
+        centerY = touchY
+        activateTimeMs = TimeUtils.millis()
+        this.pointer = pointer
+    }
+
+    fun deactivate() {
+        active = false
+    }
+
+    fun getVelocityVector(): Vector2 {
+        if (!active) {
+            return Vector2.Zero
+        }
+        return Vector2(
+            stickVector.x * value,
+            stickVector.y * value
+        )
+    }
+
+    fun updateState(touchX: Float, touchY: Float) {
+        if (!active) {
+            return
+        }
+
+        stickVector.x = touchX - centerX
+        stickVector.y = touchY - centerY
+        stickVector.clamp(0f, RADIUS)
+
+        activeX = centerX + stickVector.x
+        activeY = centerY + stickVector.y
+
+        stickVector.x /= RADIUS
+        stickVector.y /= RADIUS
+    }
+
+    companion object {
+        const val RADIUS = 48f
+        const val SIZE = RADIUS * 2
+        const val STICK_SIZE = 32f
+    }
+
+}
\ No newline at end of file
index f590398c30db185354b2ae2738c664649be7e4fa..a58e52294dfe6a196c94f4f199a4023c7fcf9060 100644 (file)
@@ -111,7 +111,28 @@ object KeyboardInputHandlersModule {
     @Binds
     @IntoSet
     @GameScope
-    fun bindOpenCraftingKeyboardInputHandler(handler: OpenCraftingKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
+    fun bindOpenCraftingKeyboardInputHandler(handler: DropItemKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
+        return handler
+    }
+
+    @Binds
+    @IntoSet
+    @GameScope
+    fun bindSwimUpKeyboardInputHandler(handler: SwimUpKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
+        return handler
+    }
+
+    @Binds
+    @IntoSet
+    @GameScope
+    fun bindStopSwimKeyboardInputHandler(handler: StopSwimKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
+        return handler
+    }
+
+    @Binds
+    @IntoSet
+    @GameScope
+    fun bindSelectHotbarSlotKeyboardInputHandler(handler: SelectHotbarSlotKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
         return handler
     }
 
index 937b3e68d921e6d4dfd831c23dd7ca27439e26b3..f0c3b3b13f3579ee07a47ec2fa34bf906407c802 100644 (file)
@@ -5,6 +5,7 @@ import dagger.Module
 import dagger.multibindings.IntoSet
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.handler.touch.JoystickInputHandler
 import ru.deadsoftware.cavedroid.game.input.handler.mouse.*
 
 @Module
@@ -72,4 +73,25 @@ object MouseInputHandlersModule {
     fun bindSelectCraftingInventoryItemMouseInputHandler(handler: SelectCraftingInventoryItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
         return handler
     }
+
+    @Binds
+    @IntoSet
+    @GameScope
+    fun bindSelectFurnaceInventoryItemMouseInputHandler(handler: SelectFurnaceInventoryItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
+        return handler
+    }
+
+    @Binds
+    @IntoSet
+    @GameScope
+    fun bindJoystickInputHandler(handler: JoystickInputHandler): IGameInputHandler<MouseInputAction> {
+        return handler
+    }
+
+    @Binds
+    @IntoSet
+    @GameScope
+    fun bindSelectChestInventoryItemMouseInputHandler(handler: SelectChestInventoryItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
+        return handler
+    }
 }
\ No newline at end of file
index fb59efbf4dae71cc79b726ca51f28717ee66c655..fd14fc4947412527773b85e76edf0864d62d8db8 100644 (file)
@@ -5,11 +5,12 @@ sealed interface KeyboardInputActionKey {
     data object Left : KeyboardInputActionKey
     data object Right : KeyboardInputActionKey
     data object Down : KeyboardInputActionKey
-
-    data object Jump : KeyboardInputActionKey
+    data object Up : KeyboardInputActionKey
 
     data object Crouch : KeyboardInputActionKey
 
+    data object DropItem : KeyboardInputActionKey
+
     data object SwitchControlsMode : KeyboardInputActionKey
 
     data object OpenInventory : KeyboardInputActionKey
@@ -20,5 +21,8 @@ sealed interface KeyboardInputActionKey {
     data object SpawnPig : KeyboardInputActionKey
     data object SwitchGameMode : KeyboardInputActionKey
     data object ShowMap : KeyboardInputActionKey
-    data object OpenCraft : KeyboardInputActionKey
+
+    data class SelectHotbarSlot(
+        val slot: Int
+    ) : KeyboardInputActionKey
 }
\ No newline at end of file
index 58227603ae8b08f2b295fbf14f89b0e8adc05ffa..3b2744f7da431bea7f8a254782ec4cf4fc47dbac 100644 (file)
@@ -4,12 +4,18 @@ sealed interface MouseInputActionKey {
 
     val touchUp: Boolean
 
+    sealed interface Touch : MouseInputActionKey {
+        val pointer: Int
+    }
+
     data object None : MouseInputActionKey {
         override val touchUp: Boolean
             get() = throw IllegalAccessException("not applicable for mouse move action")
     }
 
-    data object Dragged : MouseInputActionKey {
+    data class Dragged(
+        override val pointer: Int
+    ) : Touch {
         override val touchUp: Boolean
             get() = throw IllegalAccessException("not applicable for mouse dragged action")
     }
@@ -26,9 +32,10 @@ sealed interface MouseInputActionKey {
         override val touchUp: Boolean
     ) : MouseInputActionKey
 
-    data class Touch(
-        override val touchUp: Boolean
-    ) : MouseInputActionKey
+    data class Screen(
+        override val touchUp: Boolean,
+        override val pointer: Int,
+    ) : Touch
 
     data class Scroll(
         val amountX: Float,
index 705f9aa0e71a5ae60a64c8ac1e411c8cbf511a45..89a7b693c02492243440ca4385805bc462e47924 100644 (file)
@@ -6,8 +6,8 @@ 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 ru.deadsoftware.cavedroid.game.objects.drop.DropController
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import javax.inject.Inject
 
 @GameScope
@@ -19,7 +19,7 @@ class CloseGameWindowKeyboardInputHandler @Inject constructor(
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return action.actionKey is KeyboardInputActionKey.OpenInventory &&
-                action.isKeyDown && gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE
+                !action.isKeyDown && gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE
     }
 
     override fun handle(action: KeyboardInputAction) {
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/DropItemKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/DropItemKeyboardInputHandler.kt
new file mode 100644 (file)
index 0000000..04f5520
--- /dev/null
@@ -0,0 +1,49 @@
+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.model.item.Item
+import ru.deadsoftware.cavedroid.game.objects.drop.Drop
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import javax.inject.Inject
+
+@GameScope
+class DropItemKeyboardInputHandler @Inject constructor(
+    private val gameWindowsManager: GameWindowsManager,
+    private val mobsController: MobsController,
+    private val dropController: DropController,
+) : IGameInputHandler<KeyboardInputAction> {
+
+    override fun checkConditions(action: KeyboardInputAction): Boolean {
+        return action.actionKey is KeyboardInputActionKey.DropItem &&
+                action.isKeyDown && gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE &&
+                !mobsController.player.inventory.activeItem.item.isNone()
+    }
+
+    private fun createDrop(item: Item, playerX: Float, playerY: Float, amount: Int) {
+        dropController.addDrop(
+            /* x = */ playerX + ((DROP_DISTANCE - Drop.DROP_SIZE / 2) * mobsController.player.direction.basis),
+            /* y = */ playerY,
+            /* item = */ item,
+            /* count = */ amount
+        )
+    }
+
+    override fun handle(action: KeyboardInputAction) {
+        val player = mobsController.player
+        val currentItem = player.inventory.activeItem
+        val dropAmount =  if (currentItem.item.isTool()) currentItem.amount else 1
+
+        createDrop(currentItem.item, player.x, player.y, dropAmount)
+        player.inventory.decreaseCurrentItemAmount(dropAmount)
+    }
+
+    companion object {
+        const val DROP_DISTANCE = 20f
+    }
+}
\ No newline at end of file
index 90347c44b86b5723950777f592b3cc50eb1c00cd..0bb0a772e8d0ae54f51f21464a46c02289cfb128 100644 (file)
@@ -6,7 +6,7 @@ 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.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import javax.inject.Inject
 
 @GameScope
index 2c6efc90beb2dee1f4efaab5127ca7814312e6c6..bf12c1ad14aed9a0068ef42b9f2ba5e212148bbe 100644 (file)
@@ -6,7 +6,7 @@ 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.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import javax.inject.Inject
 
 @GameScope
@@ -16,7 +16,8 @@ class FlyUpKeyboardInputHandler @Inject constructor(
 ) : IGameInputHandler<KeyboardInputAction> {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
-        return action.actionKey is KeyboardInputActionKey.Jump &&
+        return action.actionKey is KeyboardInputActionKey.Up &&
+                !mobsController.player.swim &&
                 mobsController.player.isFlyMode &&
                 (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
     }
index 07ddb52ca61e3f06f243c4c7f734fda96221c2cd..421e47e5a56ce72de6a3fc6ea602c19583edca48 100644 (file)
@@ -7,7 +7,7 @@ import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
 import ru.deadsoftware.cavedroid.game.mobs.Mob
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import javax.inject.Inject
 
 @GameScope
index 8f8f0385cf3af9a9e8c8d07a4038c34241b15b7c..e3f5bf99f054c658aa6e402994794578f9d707e1 100644 (file)
@@ -7,7 +7,7 @@ import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
 import ru.deadsoftware.cavedroid.game.mobs.Mob
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import javax.inject.Inject
 
 @GameScope
index 1fbc9fb76b02c7926d9d6881336db5d1465adc12..1e68cea9f4ca2639f882544553c359d673039268 100644 (file)
@@ -6,7 +6,7 @@ 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.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import javax.inject.Inject
 
 @GameScope
@@ -16,7 +16,7 @@ class JumpKeyboardInputHandler @Inject constructor(
 ) : IGameInputHandler<KeyboardInputAction> {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
-        return action.actionKey is KeyboardInputActionKey.Jump &&
+        return action.actionKey is KeyboardInputActionKey.Up &&
                 mobsController.player.canJump() && !mobsController.player.isFlyMode &&
                 action.isKeyDown &&
                 (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
index ac82cb8f62e6887a8be27ebf87b08361b14eadde..0cb74d7d0ff20bd0d01ceafa873b46f5bdea863c 100644 (file)
@@ -1,13 +1,12 @@
 package ru.deadsoftware.cavedroid.game.input.handler.keyboard
 
-import com.badlogic.gdx.math.MathUtils
 import ru.deadsoftware.cavedroid.MainConfig
 import ru.deadsoftware.cavedroid.game.GameScope
 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.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import ru.deadsoftware.cavedroid.game.world.GameWorld
 import javax.inject.Inject
 
@@ -23,37 +22,22 @@ class MoveCursorControlsModeKeyboardInputHandler @Inject constructor(
                 mobsController.player.controlMode == Player.ControlMode.CURSOR && action.isKeyDown &&
                 (action.actionKey is KeyboardInputActionKey.Left ||
                 action.actionKey is KeyboardInputActionKey.Right ||
-                        action.actionKey is KeyboardInputActionKey.Jump ||
+                        action.actionKey is KeyboardInputActionKey.Up ||
                         action.actionKey is KeyboardInputActionKey.Down)
     }
 
-    private fun checkCursorBounds() {
-        val player = mobsController.player
-        if (player.gameMode == 0) {
-            val minCursorX = player.mapX - SURVIVAL_CURSOR_RANGE
-            val maxCursorX = player.mapX + SURVIVAL_CURSOR_RANGE
-            val minCursorY = player.middleMapY - SURVIVAL_CURSOR_RANGE
-            val maxCursorY = player.middleMapY + SURVIVAL_CURSOR_RANGE
-
-            player.cursorX = MathUtils.clamp(player.cursorX, minCursorX, maxCursorX)
-            player.cursorY = MathUtils.clamp(player.cursorY, minCursorY, maxCursorY)
-        }
-
-        player.cursorY = MathUtils.clamp(player.cursorY, 0, gameWorld.height - 1)
-    }
-
     override fun handle(action: KeyboardInputAction) {
         val player = mobsController.player
 
         when (action.actionKey) {
             KeyboardInputActionKey.Left -> player.cursorX--
             KeyboardInputActionKey.Right -> player.cursorX++
-            KeyboardInputActionKey.Jump -> player.cursorY--
+            KeyboardInputActionKey.Up -> player.cursorY--
             KeyboardInputActionKey.Down -> player.cursorY++
             else -> return
         }
 
-        checkCursorBounds()
+        player.checkCursorBounds(gameWorld);
     }
 
     companion object {
index 0e2c11bd17a0adce97b6853cbe37965891b11fb0..ec8190a40c2a3902888a404463ab5fc0e0eb9d03 100644 (file)
@@ -5,9 +5,7 @@ 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 ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import javax.inject.Inject
 
 @GameScope
@@ -17,7 +15,7 @@ class OpenInventoryKeyboardInputHandler @Inject constructor(
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return action.actionKey is KeyboardInputActionKey.OpenInventory &&
-                action.isKeyDown && gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
+                !action.isKeyDown && gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
     }
 
     override fun handle(action: KeyboardInputAction) {
index 60a9edfdcaacfb015da3a7ee79e05367e8b8c80c..b39ee7403ee30f69ca8676f8c3906b63e4c3a79c 100644 (file)
@@ -3,11 +3,14 @@ package ru.deadsoftware.cavedroid.game.input.handler.keyboard
 import ru.deadsoftware.cavedroid.MainConfig
 import ru.deadsoftware.cavedroid.game.GameSaver
 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.objects.drop.DropController
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import ru.deadsoftware.cavedroid.game.world.GameWorld
 import javax.inject.Inject
 
@@ -17,6 +20,8 @@ class PauseGameKeyboardInputHandler @Inject constructor(
     private val dropController: DropController,
     private val mobsController: MobsController,
     private val gameWorld: GameWorld,
+    private val containerController: ContainerController,
+    private val gameWindowsManager: GameWindowsManager,
 ) : IGameInputHandler<KeyboardInputAction> {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
@@ -24,7 +29,12 @@ class PauseGameKeyboardInputHandler @Inject constructor(
     }
 
     override fun handle(action: KeyboardInputAction) {
-        GameSaver.save(mainConfig, dropController, mobsController, gameWorld)
+        if (gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE) {
+            gameWindowsManager.closeWindow()
+            return
+        }
+
+        GameSaver.save(mainConfig, dropController, mobsController, containerController, gameWorld)
         mainConfig.caveGame.quitGame()
     }
 }
\ No newline at end of file
similarity index 54%
rename from core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt
rename to core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/SelectHotbarSlotKeyboardInputHandler.kt
index d2e333c2cac6377d9f06f69a045932dbd85d4091..45e04da208433324c6463a6f4ccef974510bbe4d 100644 (file)
@@ -1,26 +1,24 @@
 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,
+class SelectHotbarSlotKeyboardInputHandler @Inject constructor(
+    private val mobsController: MobsController,
 ) : IGameInputHandler<KeyboardInputAction> {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
-        return action.actionKey is KeyboardInputActionKey.OpenCraft &&
-                action.isKeyDown && gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
+        return action.actionKey is KeyboardInputActionKey.SelectHotbarSlot &&
+                action.isKeyDown
     }
 
     override fun handle(action: KeyboardInputAction) {
-        gameWindowsManager.openCrafting()
+        mobsController.player.inventory.activeSlot = (action.actionKey as KeyboardInputActionKey.SelectHotbarSlot).slot
     }
+
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/StopSwimKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/StopSwimKeyboardInputHandler.kt
new file mode 100644 (file)
index 0000000..e25a99f
--- /dev/null
@@ -0,0 +1,30 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+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.mobs.player.Player
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class StopSwimKeyboardInputHandler @Inject constructor(
+    private val mainConfig: MainConfig,
+    private val mobsController: MobsController,
+    private val gameWorld: GameWorld,
+) : IGameInputHandler<KeyboardInputAction> {
+
+    override fun checkConditions(action: KeyboardInputAction): Boolean {
+        return action.actionKey is KeyboardInputActionKey.Up && !action.isKeyDown &&
+                mobsController.player.swim &&
+                (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
+    }
+
+    override fun handle(action: KeyboardInputAction) {
+        mobsController.player.swim = false
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/SwimUpKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/SwimUpKeyboardInputHandler.kt
new file mode 100644 (file)
index 0000000..3159e7f
--- /dev/null
@@ -0,0 +1,36 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+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.mobs.player.Player
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class SwimUpKeyboardInputHandler @Inject constructor(
+    private val mainConfig: MainConfig,
+    private val mobsController: MobsController,
+    private val gameWorld: GameWorld,
+) : IGameInputHandler<KeyboardInputAction> {
+
+    private fun checkSwim(): Boolean {
+        return gameWorld.getForeMap(mobsController.player.mapX, mobsController.player.lowerMapY).isFluid()
+    }
+
+    override fun checkConditions(action: KeyboardInputAction): Boolean {
+        return action.actionKey is KeyboardInputActionKey.Up && action.isKeyDown &&
+                !mobsController.player.swim &&
+                !mobsController.player.canJump() &&
+                checkSwim() && !mobsController.player.isFlyMode &&
+                (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
+    }
+
+    override fun handle(action: KeyboardInputAction) {
+        mobsController.player.swim = true
+    }
+
+}
\ No newline at end of file
index 3deacbd34b786f7cc25a2b93faa4fa34dd658ef5..fc95e29b84794fb271aa3520cf09d58f0b6d7b20 100644 (file)
@@ -6,7 +6,7 @@ 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.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import javax.inject.Inject
 
 @GameScope
@@ -16,7 +16,7 @@ class ToggleControlsModeKeyboardInputHandler @Inject constructor(
 ) : IGameInputHandler<KeyboardInputAction> {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
-        return action.actionKey is KeyboardInputActionKey.SwitchControlsMode && action.isKeyDown
+        return action.actionKey is KeyboardInputActionKey.SwitchControlsMode && !action.isKeyDown
                 && mainConfig.isTouch
     }
 
index 6a73a9bc54f316db61c68134ce2f8355035aaf46..f3b65eb0a226899e466dda68814586f56e574ca2 100644 (file)
@@ -6,7 +6,7 @@ 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.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import javax.inject.Inject
 
 @GameScope
@@ -16,7 +16,8 @@ class TurnOnFlyModeKeyboardInputHandler @Inject constructor(
 ) : IGameInputHandler<KeyboardInputAction> {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
-        return mobsController.player.gameMode == 1 && action.actionKey is KeyboardInputActionKey.Jump &&
+        return mobsController.player.gameMode == 1 && action.actionKey is KeyboardInputActionKey.Up &&
+                !mobsController.player.swim &&
                 !mobsController.player.isFlyMode && !mobsController.player.canJump() && action.isKeyDown &&
                 (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
     }
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/AbstractInventoryItemsMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/AbstractInventoryItemsMouseInputHandler.kt
new file mode 100644 (file)
index 0000000..dec8b7d
--- /dev/null
@@ -0,0 +1,103 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.graphics.g2d.TextureRegion
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+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.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem.Companion.isNoneOrNull
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.AbstractInventoryWindow
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.AbstractInventoryWindowWithCraftGrid
+
+abstract class AbstractInventoryItemsMouseInputHandler(
+    private val gameItemsHolder: GameItemsHolder,
+    private val gameWindowsManager: GameWindowsManager,
+    private val windowType: GameUiWindow,
+) : IGameInputHandler<MouseInputAction> {
+
+    protected abstract val windowTexture: TextureRegion
+
+    override fun checkConditions(action: MouseInputAction): Boolean {
+        return gameWindowsManager.getCurrentWindow() == windowType &&
+                isInsideWindow(action, windowTexture) &&
+                (action.actionKey is MouseInputActionKey.Left ||
+                        action.actionKey is MouseInputActionKey.Right ||
+                        action.actionKey is MouseInputActionKey.Screen)
+                && (action.actionKey.touchUp || action.actionKey is MouseInputActionKey.Screen)
+    }
+
+    protected fun updateCraftResult(window: AbstractInventoryWindowWithCraftGrid) {
+        window.craftResult = gameItemsHolder.craftItem(window.craftingItems.map(InventoryItem::item))
+            ?: gameItemsHolder.fallbackItem.toInventoryItem()
+    }
+
+    private fun reduceCraftItems(window: AbstractInventoryWindowWithCraftGrid) {
+        for (i in window.craftingItems.indices) {
+            if (window.craftingItems[i].amount > 1) {
+                window.craftingItems[i].amount--
+            } else {
+                window.craftingItems[i] = gameItemsHolder.fallbackItem.toInventoryItem()
+            }
+        }
+    }
+
+    protected fun handleInsidePlaceableCell(
+        action: MouseInputAction,
+        items: MutableList<InventoryItem>,
+        window: AbstractInventoryWindow,
+        index: Int
+    ) {
+        if (action.actionKey is MouseInputActionKey.Screen) {
+            if (!action.actionKey.touchUp) {
+                window.onLeftCLick(items, gameItemsHolder, index, action.actionKey.pointer)
+            } else {
+                if (action.actionKey.pointer == window.selectItemPointer) {
+                    window.onLeftCLick(items, gameItemsHolder, index, action.actionKey.pointer)
+                } else {
+                    window.onRightClick(items, gameItemsHolder, index)
+                }
+            }
+        } else if (action.actionKey is MouseInputActionKey.Left) {
+            window.onLeftCLick(items, gameItemsHolder, index)
+        } else {
+            window.onRightClick(items, gameItemsHolder, index)
+        }
+    }
+
+    protected fun handleInsideCraftResultCell(
+        action: MouseInputAction,
+        items: MutableList<InventoryItem>,
+        window: AbstractInventoryWindow,
+        index: Int
+    ) {
+        val selectedItem = window.selectedItem
+
+        if (!selectedItem.isNoneOrNull() && (selectedItem.item != items[index].item ||
+                    !selectedItem.canBeAdded(items[index].amount))) {
+            return
+        }
+
+        if (!selectedItem.isNoneOrNull()) {
+            selectedItem.amount += items[index].amount
+            items[index] = gameItemsHolder.fallbackItem.toInventoryItem()
+        } else {
+            if (action.actionKey is MouseInputActionKey.Screen) {
+                if (!action.actionKey.touchUp) {
+                    window.onLeftCLick(items, gameItemsHolder, index, action.actionKey.pointer)
+                }
+            } else if (action.actionKey is MouseInputActionKey.Left) {
+                window.onLeftCLick(items, gameItemsHolder, index)
+            }
+        }
+
+        if (window is AbstractInventoryWindowWithCraftGrid) {
+            reduceCraftItems(window)
+        }
+
+    }
+
+}
\ No newline at end of file
index a0bfa4cd8da9ea8d176a73c2c7da1175c31e229a..2afc3d5b1bbb50d6310f0fa4a247926c29bd9d3b 100644 (file)
@@ -7,7 +7,7 @@ import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
 import ru.deadsoftware.cavedroid.game.input.isInsideHotbar
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import ru.deadsoftware.cavedroid.game.world.GameWorld
 import javax.inject.Inject
 
index e80ff476b00fdfbb02c1c7623ddb2b8969b3ee6a..f6f6cfc97eeb83ad0a126225decd6d84b260e685 100644 (file)
@@ -3,13 +3,13 @@ package ru.deadsoftware.cavedroid.game.input.handler.mouse
 import com.badlogic.gdx.graphics.g2d.TextureRegion
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.GameUiWindow
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 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.game.objects.drop.DropController
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 
@@ -23,11 +23,13 @@ class CloseGameWindowMouseInputHandler @Inject constructor(
     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"])
+    private val furnaceInventoryTexture get() = requireNotNull(Assets.textureRegions["furnace"])
+    private val chestInventoryTexture get() = requireNotNull(Assets.textureRegions["chest"])
 
     override fun checkConditions(action: MouseInputAction): Boolean {
         return gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE &&
-                (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) &&
-                !action.actionKey.touchUp &&
+                (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Screen) &&
+                action.actionKey.touchUp &&
                 !isInsideWindow(action, getCurrentWindowTexture())
     }
 
@@ -36,6 +38,8 @@ class CloseGameWindowMouseInputHandler @Inject constructor(
             GameUiWindow.CREATIVE_INVENTORY -> creativeInventoryTexture
             GameUiWindow.SURVIVAL_INVENTORY -> survivalInventoryTexture
             GameUiWindow.CRAFTING_TABLE -> craftingInventoryTexture
+            GameUiWindow.FURNACE -> furnaceInventoryTexture
+            GameUiWindow.CHEST -> chestInventoryTexture
             else -> throw UnsupportedOperationException("Cant close window ${window.name}")
         }
     }
@@ -43,13 +47,12 @@ class CloseGameWindowMouseInputHandler @Inject constructor(
     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
+                    /* item = */ selectedItem.item,
+                    /* count = */ selectedItem.amount,
                 )
-            }
             gameWindowsManager.currentWindow?.selectedItem = null
         }
         gameWindowsManager.closeWindow()
index 01cc4e23a5c1f78a38c7dd2ee6a4a8b47ef2e9b9..11f76e9a691fd8a34c2086fdc21c4023ab592481 100644 (file)
@@ -5,7 +5,7 @@ import ru.deadsoftware.cavedroid.MainConfig
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.GameUiWindow
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
 import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
@@ -34,12 +34,12 @@ class CreativeInventoryScrollMouseInputHandler @Inject constructor(
     }
 
     private fun checkStartDragConditions(action: MouseInputAction): Boolean {
-        return (action.actionKey is MouseInputActionKey.Touch) &&
+        return (action.actionKey is MouseInputActionKey.Screen) &&
                 !action.actionKey.touchUp && !gameWindowsManager.isDragging
     }
 
     private fun checkEndDragConditions(action: MouseInputAction): Boolean {
-        return action.actionKey is MouseInputActionKey.Touch &&
+        return action.actionKey is MouseInputActionKey.Screen &&
                 action.actionKey.touchUp && gameWindowsManager.isDragging
     }
 
@@ -75,7 +75,7 @@ class CreativeInventoryScrollMouseInputHandler @Inject constructor(
 
     override fun handle(action: MouseInputAction) {
         when (action.actionKey) {
-            is MouseInputActionKey.Touch -> handleStartOrEndDrag(action)
+            is MouseInputActionKey.Screen -> handleStartOrEndDrag(action)
             is MouseInputActionKey.Dragged -> handleDrag(action)
             is MouseInputActionKey.Scroll -> handleScroll(action)
             else -> return
index 54aacf2b5cd6c6ebeee03517a93bf7578bff8595..585a5ab09d181a62cb0ff1767664118282c4047c 100644 (file)
@@ -2,15 +2,21 @@ package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
 import com.badlogic.gdx.math.MathUtils
 import ru.deadsoftware.cavedroid.MainConfig
+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.mobs.Mob
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.mobs.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
 import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.Assets
 import ru.deadsoftware.cavedroid.misc.utils.bl
 import ru.deadsoftware.cavedroid.misc.utils.px
 import javax.inject.Inject
@@ -20,30 +26,21 @@ class CursorMouseInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
     private val gameWorld: GameWorld,
+    private val gameWindowsManager: GameWindowsManager,
+    private val gameItemsHolder: GameItemsHolder,
+    private val tooltipManager: TooltipManager,
 ) : IGameInputHandler<MouseInputAction> {
 
     private val player get() = mobsController.player
 
+    private val creativeInventoryTexture get() = requireNotNull(Assets.textureRegions["creative"])
+
     private val Block.isAutoselectable
         get() = !isNone() && params.hasCollision
 
     private fun GameWorld.isCurrentBlockAutoselectable() =
         getForeMap(player.cursorX, player.cursorY).isAutoselectable
 
-    private fun checkCursorBounds() {
-        if (player.gameMode == 0) {
-            val minCursorX = player.mapX - SURVIVAL_CURSOR_RANGE
-            val maxCursorX = player.mapX + SURVIVAL_CURSOR_RANGE
-            val minCursorY = player.middleMapY - SURVIVAL_CURSOR_RANGE
-            val maxCursorY = player.middleMapY + SURVIVAL_CURSOR_RANGE
-
-            player.cursorX = MathUtils.clamp(player.cursorX, minCursorX, maxCursorX)
-            player.cursorY = MathUtils.clamp(player.cursorY, minCursorY, maxCursorY)
-        }
-
-        player.cursorY = MathUtils.clamp(player.cursorY, 0, gameWorld.height - 1)
-    }
-
     private fun setPlayerDirectionToCursor() {
         if (player.controlMode != Player.ControlMode.CURSOR) {
             return
@@ -59,6 +56,7 @@ class CursorMouseInputHandler @Inject constructor(
     private fun handleWalkTouch() {
         player.cursorX = player.mapX + player.direction.basis
         player.cursorY = player.upperMapY
+        player.headRotation = 0f
 
         for (i in 1..2) {
             if (gameWorld.isCurrentBlockAutoselectable()) {
@@ -92,6 +90,27 @@ class CursorMouseInputHandler @Inject constructor(
         player.headRotation = getPlayerHeadRotation(worldX, worldY)
     }
 
+    private fun getCreativeTooltip(action: MouseInputAction): String? {
+        val creativeTexture = creativeInventoryTexture
+        val xOnGrid = (action.screenX - (action.cameraViewport.width / 2 - creativeTexture.regionWidth / 2 +
+                GameWindowsConfigs.Creative.itemsGridMarginLeft)) /
+                GameWindowsConfigs.Creative.itemsGridColWidth
+        val yOnGrid = (action.screenY - (action.cameraViewport.height / 2 - creativeTexture.regionHeight / 2 +
+                GameWindowsConfigs.Creative.itemsGridMarginTop)) /
+                GameWindowsConfigs.Creative.itemsGridRowHeight
+
+        if (xOnGrid < 0 || xOnGrid >= GameWindowsConfigs.Creative.itemsInRow ||
+            yOnGrid < 0 || yOnGrid >= GameWindowsConfigs.Creative.itemsInCol) {
+            return null
+        }
+
+        val itemIndex = (gameWindowsManager.creativeScrollAmount * GameWindowsConfigs.Creative.itemsInRow +
+                (xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Creative.itemsInRow))
+        val item = gameItemsHolder.getItemFromCreativeInventory(itemIndex)
+
+        return item.params.name
+    }
+
     override fun checkConditions(action: MouseInputAction): Boolean {
         return action.actionKey is MouseInputActionKey.None
     }
@@ -105,12 +124,16 @@ class CursorMouseInputHandler @Inject constructor(
             !mainConfig.isTouch -> handleMouse(action)
         }
 
-        checkCursorBounds()
+        player.checkCursorBounds(gameWorld)
         setPlayerDirectionToCursor()
 
         if (player.cursorX != pastCursorX || player.cursorY != pastCursorY) {
             player.blockDamage = 0f
         }
+
+        if (gameWindowsManager.getCurrentWindow() == GameUiWindow.CREATIVE_INVENTORY) {
+            tooltipManager.showMouseTooltip(getCreativeTooltip(action).orEmpty())
+        }
     }
 
     companion object {
index 31827d22a3e5c088ad03ee09fb278b63727f10d4..18cd41ddb2d36401321b83acf84364c1c4317afa 100644 (file)
@@ -3,12 +3,17 @@ package ru.deadsoftware.cavedroid.game.input.handler.mouse
 import com.badlogic.gdx.utils.Timer
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.GameUiWindow
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 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.handler.keyboard.DropItemKeyboardInputHandler.Companion.DROP_DISTANCE
 import ru.deadsoftware.cavedroid.game.input.isInsideHotbar
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.objects.drop.Drop
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 
@@ -16,6 +21,7 @@ import javax.inject.Inject
 class HotbarMouseInputHandler @Inject constructor(
     private val gameWindowsManager: GameWindowsManager,
     private val mobsController: MobsController,
+    private val dropController: DropController,
 ) : IGameInputHandler<MouseInputAction> {
 
     private val hotbarTexture get() = requireNotNull(Assets.textureRegions["hotbar"])
@@ -24,7 +30,7 @@ class HotbarMouseInputHandler @Inject constructor(
 
     override fun checkConditions(action: MouseInputAction): Boolean {
         return buttonHoldTask?.isScheduled == true ||
-                ((action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch)
+                ((action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Screen)
                         && isInsideHotbar(action)
                         || action.actionKey is MouseInputActionKey.Scroll) &&
                 gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
@@ -35,15 +41,37 @@ class HotbarMouseInputHandler @Inject constructor(
         buttonHoldTask = null
     }
 
-    private fun handleHold() {
-        buttonHoldTask = null
-        gameWindowsManager.openInventory()
+    private fun createDrop(item: Item, playerX: Float, playerY: Float, amount: Int) {
+        dropController.addDrop(
+            /* x = */ playerX + ((DROP_DISTANCE - Drop.DROP_SIZE / 2) * mobsController.player.direction.basis),
+            /* y = */ playerY,
+            /* item = */ item,
+            /* count = */ amount
+        )
+    }
+
+    private fun getActionSlot(action: MouseInputAction): Int {
+        return ((action.screenX -
+                (action.cameraViewport.width / 2 - hotbarTexture.regionWidth / 2))
+                / HOTBAR_CELL_WIDTH).toInt()
+    }
+
+    private fun handleHold(action: MouseInputAction) {
+//        buttonHoldTask = null
+//        gameWindowsManager.openInventory()
+        val player = mobsController.player
+        val actionSlot = getActionSlot(action)
+        val currentItem = player.inventory.items[actionSlot]
+        val dropAmount = if (currentItem.item.isTool()) currentItem.amount else 1
+
+        createDrop(currentItem.item, player.x, player.y, dropAmount)
+        player.inventory.decreaseItemAmount(actionSlot, dropAmount)
     }
 
     private fun handleDown(action: MouseInputAction) {
         buttonHoldTask = object : Timer.Task() {
             override fun run() {
-                handleHold()
+                handleHold(action)
             }
         }
 
@@ -51,21 +79,18 @@ class HotbarMouseInputHandler @Inject constructor(
     }
 
     private fun handleUp(action: MouseInputAction) {
-        mobsController.player.slot =
-            ((action.screenX -
-                    (action.cameraViewport.width / 2 - hotbarTexture.regionWidth / 2))
-                    / HOTBAR_CELL_WIDTH).toInt()
+        mobsController.player.inventory.activeSlot = getActionSlot(action)
     }
 
     private fun handleScroll(action: MouseInputAction) {
         if (action.actionKey !is MouseInputActionKey.Scroll) {
             return
         }
-        mobsController.player.slot += action.actionKey.amountY.toInt()
-        if (mobsController.player.slot < 0) {
-            mobsController.player.slot = HOTBAR_ITEMS - 1
-        } else if (mobsController.player.slot >= HOTBAR_ITEMS){
-            mobsController.player.slot = 0
+        mobsController.player.inventory.activeSlot += action.actionKey.amountY.toInt()
+        if (mobsController.player.inventory.activeSlot < 0) {
+            mobsController.player.inventory.activeSlot = Player.HOTBAR_SIZE - 1
+        } else if (mobsController.player.inventory.activeSlot >= Player.HOTBAR_SIZE) {
+            mobsController.player.inventory.activeSlot = 0
         }
     }
 
@@ -74,7 +99,12 @@ class HotbarMouseInputHandler @Inject constructor(
             cancelHold()
         }
 
-        if (action.actionKey !is MouseInputActionKey.Left && action.actionKey !is MouseInputActionKey.Touch ) {
+        if (buttonHoldTask != null && buttonHoldTask?.isScheduled != true) {
+            buttonHoldTask = null
+            return
+        }
+
+        if (action.actionKey !is MouseInputActionKey.Left && action.actionKey !is MouseInputActionKey.Screen) {
             if (action.actionKey is MouseInputActionKey.Scroll) {
                 handleScroll(action)
             }
@@ -91,7 +121,6 @@ class HotbarMouseInputHandler @Inject constructor(
     companion object {
         private const val TOUCH_HOLD_TIME_SEC = 0.5f
         private const val HOTBAR_CELL_WIDTH = 20
-        private const val HOTBAR_ITEMS = 9
     }
 
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectChestInventoryItemMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectChestInventoryItemMouseInputHandler.kt
new file mode 100644 (file)
index 0000000..d47202e
--- /dev/null
@@ -0,0 +1,72 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.ChestInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class SelectChestInventoryItemMouseInputHandler @Inject constructor(
+    private val gameWindowsManager: GameWindowsManager,
+    private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
+) : AbstractInventoryItemsMouseInputHandler(gameItemsHolder, gameWindowsManager, GameUiWindow.CHEST) {
+
+    override val windowTexture get() = requireNotNull(Assets.textureRegions["chest"])
+
+    private fun handleInsideContentGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
+        val window = gameWindowsManager.currentWindow as ChestInventoryWindow
+        val itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Chest.contentsInRow
+
+        handleInsidePlaceableCell(action, window.chest.items, window, itemIndex)
+    }
+
+    private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
+        val window = gameWindowsManager.currentWindow as ChestInventoryWindow
+
+        var itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Chest.itemsInRow
+        itemIndex += GameWindowsConfigs.Chest.hotbarCells
+
+        if (itemIndex >= mobsController.player.inventory.size) {
+            itemIndex -= mobsController.player.inventory.size
+        }
+
+        handleInsidePlaceableCell(action, mobsController.player.inventory.items, window, itemIndex)
+    }
+
+    override fun handle(action: MouseInputAction) {
+        val texture = windowTexture
+
+        val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - texture.regionWidth / 2)
+        val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - texture.regionHeight / 2)
+
+        val xOnGrid = (xOnWindow - GameWindowsConfigs.Chest.itemsGridMarginLeft) /
+                GameWindowsConfigs.Chest.itemsGridColWidth
+        val yOnGrid = (yOnWindow - GameWindowsConfigs.Chest.itemsGridMarginTop) /
+                GameWindowsConfigs.Chest.itemsGridRowHeight
+
+        val xOnContent = (xOnWindow - GameWindowsConfigs.Chest.contentsMarginLeft) /
+                GameWindowsConfigs.Chest.itemsGridColWidth
+        val yOnContent = (yOnWindow - GameWindowsConfigs.Chest.contentsMarginTop) /
+                GameWindowsConfigs.Chest.itemsGridRowHeight
+
+        val isInsideInventoryGrid = xOnGrid >= 0 && xOnGrid < GameWindowsConfigs.Chest.itemsInRow &&
+                yOnGrid >= 0 && yOnGrid < GameWindowsConfigs.Chest.itemsInCol
+
+        val isInsideContentGrid = xOnContent >= 0 && xOnContent < GameWindowsConfigs.Chest.contentsInRow &&
+                yOnContent >= 0 && yOnContent < GameWindowsConfigs.Chest.contentsInCol
+
+
+        if (isInsideInventoryGrid) {
+            handleInsideInventoryGrid(action, xOnGrid.toInt(), yOnGrid.toInt())
+        } else if (isInsideContentGrid) {
+            handleInsideContentGrid(action, xOnContent.toInt(), yOnContent.toInt())
+        }
+    }
+}
\ No newline at end of file
index 7005a679ebf87566e9a7bb1e74e94c8c30e8946b..88e362005316a756b7e0aaf829493fe20d5a3747 100644 (file)
@@ -1,18 +1,13 @@
 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.game.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.CraftingInventoryWindow
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 
@@ -21,90 +16,45 @@ class SelectCraftingInventoryItemMouseInputHandler @Inject constructor(
     private val gameWindowsManager: GameWindowsManager,
     private val mobsController: MobsController,
     private val gameItemsHolder: GameItemsHolder,
-) : IGameInputHandler<MouseInputAction> {
+) : AbstractInventoryItemsMouseInputHandler(gameItemsHolder, gameWindowsManager, GameUiWindow.CRAFTING_TABLE) {
 
-    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
-        }
-    }
+    override val windowTexture get() = requireNotNull(Assets.textureRegions["crafting_table"])
 
     private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
         val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
 
-        var itemIndex = ((xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Crafting.itemsInRow))
+        var itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Crafting.itemsInRow
         itemIndex += GameWindowsConfigs.Crafting.hotbarCells
 
-        if (itemIndex >= 36) {
-            itemIndex -= 36
+        if (itemIndex >= mobsController.player.inventory.size) {
+            itemIndex -= mobsController.player.inventory.size
         }
 
-        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)"
-        )
+        handleInsidePlaceableCell(action, mobsController.player.inventory.items, window, itemIndex)
     }
 
     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)
-        }
+        handleInsidePlaceableCell(action, window.craftingItems, window, index)
 
-        window.craftResult =
-            gameItemsHolder.craftItem(window.craftingItems.map { it?.item ?: gameItemsHolder.fallbackItem })
+        updateCraftResult(window)
     }
 
-    override fun handle(action: MouseInputAction) {
-        val survivalTexture = survivalWindowTexture
+    private fun handleInsideCraftResult(action: MouseInputAction) {
         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)
+        handleInsideCraftResultCell(action, window.craftResultList, window, 0)
+
+        updateCraftResult(window)
+    }
+
+    override fun handle(action: MouseInputAction) {
+        val texture = windowTexture
+
+        val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - texture.regionWidth / 2)
+        val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - texture.regionHeight / 2)
 
         val xOnGrid = (xOnWindow - GameWindowsConfigs.Crafting.itemsGridMarginLeft) /
                 GameWindowsConfigs.Crafting.itemsGridColWidth
@@ -132,30 +82,8 @@ class SelectCraftingInventoryItemMouseInputHandler @Inject constructor(
         } 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 })
-            }
+            handleInsideCraftResult(action)
         }
 
     }
-
-    companion object {
-        private const val TAG = "SelectCraftingInventoryItemMouseInputHandler"
-
-    }
 }
\ No newline at end of file
index 7c587b67b7162415ecceeb9525ce568fcdb2831c..272a17874b69897af5f4b3376131cdc61cfca5ea 100644 (file)
@@ -3,13 +3,13 @@ package ru.deadsoftware.cavedroid.game.input.handler.mouse
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.GameUiWindow
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 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.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsConfigs
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 
@@ -25,7 +25,7 @@ class SelectCreativeInventoryItemMouseInputHandler @Inject constructor(
     override fun checkConditions(action: MouseInputAction): Boolean {
         return gameWindowsManager.getCurrentWindow() == GameUiWindow.CREATIVE_INVENTORY &&
                 !gameWindowsManager.isDragging &&
-                (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) &&
+                (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Screen) &&
                 action.actionKey.touchUp && isInsideWindow(action, creativeInventoryTexture)
     }
 
@@ -46,13 +46,7 @@ class SelectCreativeInventoryItemMouseInputHandler @Inject constructor(
         val itemIndex = (gameWindowsManager.creativeScrollAmount * GameWindowsConfigs.Creative.itemsInRow +
                 (xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Creative.itemsInRow))
         val item = gameItemsHolder.getItemFromCreativeInventory(itemIndex)
-        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)
-        }
+        mobsController.player.inventory.addItem(item)
     }
 
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectFurnaceInventoryItemMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectFurnaceInventoryItemMouseInputHandler.kt
new file mode 100644 (file)
index 0000000..f4a00eb
--- /dev/null
@@ -0,0 +1,100 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem.Companion.isNoneOrNull
+import ru.deadsoftware.cavedroid.game.objects.container.Furnace
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.FurnaceInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class SelectFurnaceInventoryItemMouseInputHandler @Inject constructor(
+    private val gameWindowsManager: GameWindowsManager,
+    private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
+) : AbstractInventoryItemsMouseInputHandler(gameItemsHolder, gameWindowsManager, GameUiWindow.FURNACE) {
+
+    override val windowTexture get() = requireNotNull(Assets.textureRegions["furnace"])
+
+    private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
+        val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow
+
+        var itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Furnace.itemsInRow
+        itemIndex += GameWindowsConfigs.Furnace.hotbarCells
+
+        if (itemIndex >= mobsController.player.inventory.size) {
+            itemIndex -= mobsController.player.inventory.size
+        }
+
+        handleInsidePlaceableCell(action, mobsController.player.inventory.items, window, itemIndex)
+    }
+
+    private fun handleInsideFuel(action: MouseInputAction) {
+        val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow
+
+        if (!window.selectedItem.isNoneOrNull() && window.selectedItem?.item?.params?.burningTimeMs == null) {
+            return
+        }
+
+        handleInsidePlaceableCell(action, window.furnace.items, window, Furnace.FUEL_INDEX)
+    }
+
+    private fun handleInsideInput(action: MouseInputAction) {
+        val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow
+
+        handleInsidePlaceableCell(action, window.furnace.items, window, Furnace.INPUT_INDEX)
+    }
+
+    private fun handleInsideResult(action: MouseInputAction) {
+        val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow
+
+        handleInsideCraftResultCell(action, window.furnace.items, window, Furnace.RESULT_INDEX)
+    }
+
+    override fun handle(action: MouseInputAction) {
+        val texture = windowTexture
+
+        val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - texture.regionWidth / 2)
+        val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - texture.regionHeight / 2)
+
+        val xOnGrid = (xOnWindow - GameWindowsConfigs.Furnace.itemsGridMarginLeft) /
+                GameWindowsConfigs.Furnace.itemsGridColWidth
+        val yOnGrid = (yOnWindow - GameWindowsConfigs.Furnace.itemsGridMarginTop) /
+                GameWindowsConfigs.Furnace.itemsGridRowHeight
+
+        val isInsideInput = xOnWindow > GameWindowsConfigs.Furnace.smeltInputMarginLeft &&
+                xOnWindow < GameWindowsConfigs.Furnace.smeltInputMarginLeft + GameWindowsConfigs.Furnace.itemsGridColWidth &&
+                yOnWindow > GameWindowsConfigs.Furnace.smeltInputMarginTop &&
+                yOnWindow < GameWindowsConfigs.Furnace.smeltInputMarginTop + GameWindowsConfigs.Furnace.itemsGridRowHeight
+
+        val isInsideFuel = xOnWindow > GameWindowsConfigs.Furnace.smeltFuelMarginLeft &&
+                xOnWindow < GameWindowsConfigs.Furnace.smeltFuelMarginLeft + GameWindowsConfigs.Furnace.itemsGridColWidth &&
+                yOnWindow > GameWindowsConfigs.Furnace.smeltFuelMarginTop &&
+                yOnWindow < GameWindowsConfigs.Furnace.smeltFuelMarginTop + GameWindowsConfigs.Furnace.itemsGridRowHeight
+
+        val isInsideResult = xOnWindow > GameWindowsConfigs.Furnace.smeltResultOffsetX &&
+                xOnWindow < GameWindowsConfigs.Furnace.smeltResultOffsetX + GameWindowsConfigs.Furnace.itemsGridColWidth &&
+                yOnWindow > GameWindowsConfigs.Furnace.smeltResultOffsetY &&
+                yOnWindow < GameWindowsConfigs.Furnace.smeltResultOffsetY + GameWindowsConfigs.Furnace.itemsGridRowHeight
+
+        val isInsideInventoryGrid = xOnGrid >= 0 && xOnGrid < GameWindowsConfigs.Furnace.itemsInRow &&
+                yOnGrid >= 0 && yOnGrid < GameWindowsConfigs.Furnace.itemsInCol
+
+        if (isInsideInventoryGrid) {
+            handleInsideInventoryGrid(action, xOnGrid.toInt(), yOnGrid.toInt())
+        } else if (isInsideFuel) {
+            handleInsideFuel(action)
+        } else if (isInsideInput) {
+            handleInsideInput(action)
+        } else if (isInsideResult) {
+            handleInsideResult(action)
+        }
+
+    }
+}
\ No newline at end of file
index 61e1b18892e8970a8f65f79d52c841eea5e9088b..0a455ab2c70c47e53824d4225a0d395078c0809e 100644 (file)
@@ -1,18 +1,13 @@
 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.SurvivalInventoryWindow
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.SurvivalInventoryWindow
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 
@@ -21,90 +16,43 @@ class SelectSurvivalInventoryItemMouseInputHandler @Inject constructor(
     private val gameWindowsManager: GameWindowsManager,
     private val mobsController: MobsController,
     private val gameItemsHolder: GameItemsHolder,
-) : IGameInputHandler<MouseInputAction> {
+) : AbstractInventoryItemsMouseInputHandler(gameItemsHolder, gameWindowsManager, GameUiWindow.SURVIVAL_INVENTORY) {
 
-    private val survivalWindowTexture get() = requireNotNull(Assets.textureRegions["survival"])
-
-    override fun checkConditions(action: MouseInputAction): Boolean {
-        return gameWindowsManager.getCurrentWindow() == GameUiWindow.SURVIVAL_INVENTORY &&
-                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: SurvivalInventoryWindow, 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: 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
-        }
-    }
+    override val windowTexture get() = requireNotNull(Assets.textureRegions["survival"])
 
     private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
         val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
 
-        var itemIndex = ((xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Survival.itemsInRow))
+        var itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Survival.itemsInRow
         itemIndex += GameWindowsConfigs.Survival.hotbarCells
 
-        if (itemIndex >= 36) {
-            itemIndex -= 36
+        if (itemIndex >= mobsController.player.inventory.size) {
+            itemIndex -= mobsController.player.inventory.size
         }
 
-        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)"
-        )
+        handleInsidePlaceableCell(action, mobsController.player.inventory.items, window, itemIndex)
     }
 
     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)
-        }
+        handleInsidePlaceableCell(action, window.craftingItems, window, index)
 
-        window.craftResult =
-            gameItemsHolder.craftItem(window.craftingItems.map { it?.item ?: gameItemsHolder.fallbackItem })
+        updateCraftResult(window)
     }
 
-    override fun handle(action: MouseInputAction) {
-        val survivalTexture = survivalWindowTexture
+    private fun handleInsideCraftResult(action: MouseInputAction) {
         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)
+        handleInsideCraftResultCell(action, window.craftResultList, window, 0)
+
+        updateCraftResult(window)
+    }
+
+    override fun handle(action: MouseInputAction) {
+        val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - windowTexture.regionWidth / 2)
+        val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - windowTexture.regionHeight / 2)
 
         val xOnGrid = (xOnWindow - GameWindowsConfigs.Survival.itemsGridMarginLeft) /
                 GameWindowsConfigs.Survival.itemsGridColWidth
@@ -132,30 +80,8 @@ class SelectSurvivalInventoryItemMouseInputHandler @Inject constructor(
         } 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 })
-            }
+            handleInsideCraftResult(action)
         }
 
     }
-
-    companion object {
-        private const val TAG = "SelectSurvivalInventoryItemMouseInputHandler"
-
-    }
 }
\ No newline at end of file
index e2d3f1dc23d737842503ad15dc6316905cae5703..04bc6198295fcf3fede52cfa413b685f3c6b6d9b 100644 (file)
@@ -7,6 +7,7 @@ import ru.deadsoftware.cavedroid.game.GameUiWindow
 import ru.deadsoftware.cavedroid.game.actions.placeToBackgroundAction
 import ru.deadsoftware.cavedroid.game.actions.placeToForegroundAction
 import ru.deadsoftware.cavedroid.game.actions.placeblock.IPlaceBlockAction
+import ru.deadsoftware.cavedroid.game.actions.useblock.IUseBlockAction
 import ru.deadsoftware.cavedroid.game.actions.useitem.IUseItemAction
 import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
 import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
@@ -14,7 +15,8 @@ import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
 import ru.deadsoftware.cavedroid.game.input.isInsideHotbar
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.model.item.Item
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.world.GameWorld
 import javax.inject.Inject
 
 @GameScope
@@ -22,7 +24,9 @@ class UseItemMouseInputHandler @Inject constructor(
     private val mobsController: MobsController,
     private val useItemActionMap: Map<String, @JvmSuppressWildcards IUseItemAction>,
     private val placeBlockActionMap: Map<String, @JvmSuppressWildcards IPlaceBlockAction>,
+    private val useBlockActionMap: Map<String, @JvmSuppressWildcards IUseBlockAction>,
     private val gameWindowsManager: GameWindowsManager,
+    private val gameWorld: GameWorld,
 ) : IGameInputHandler<MouseInputAction> {
 
     private var buttonHoldTask: Timer.Task? = null
@@ -43,13 +47,13 @@ class UseItemMouseInputHandler @Inject constructor(
         cancelHold()
 
         val player = mobsController.player
-        val item = player.currentItem.item
+        val item = player.inventory.activeItem.item
         player.startHitting(false)
         player.stopHitting()
 
         if (item is Item.Placeable) {
             placeBlockActionMap.placeToBackgroundAction(
-                item = player.currentItem.item as Item.Placeable,
+                item = item,
                 x = player.cursorX,
                 y = player.cursorY
             )
@@ -67,9 +71,23 @@ class UseItemMouseInputHandler @Inject constructor(
         Timer.schedule(buttonHoldTask, TOUCH_HOLD_TIME_SEC)
     }
 
+    private fun tryUseBlock() {
+        val block = gameWorld.getForeMap(mobsController.player.cursorX, mobsController.player.cursorY)
+            .takeIf { !it.isNone() }
+            ?: gameWorld.getBackMap(mobsController.player.cursorX, mobsController.player.cursorY)
+                .takeIf { !it.isNone() }
+            ?: return
+
+        useBlockActionMap[block.params.key]?.perform(
+            block = block,
+            x = mobsController.player.cursorX,
+            y = mobsController.player.cursorY
+        )
+    }
+
     private fun handleUp(action: MouseInputAction) {
         val player = mobsController.player
-        val item = player.currentItem.item
+        val item = player.inventory.activeItem.item
         cancelHold()
 
         player.startHitting(false)
@@ -84,6 +102,8 @@ class UseItemMouseInputHandler @Inject constructor(
         } else if (item is Item.Usable) {
             useItemActionMap[item.useActionKey]?.perform(item, player.cursorX, player.cursorY)
                 ?: Gdx.app.error(TAG, "use item action ${item.useActionKey} not found");
+        } else {
+            tryUseBlock()
         }
     }
 
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/touch/JoystickInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/touch/JoystickInputHandler.kt
new file mode 100644 (file)
index 0000000..1c020a5
--- /dev/null
@@ -0,0 +1,148 @@
+package ru.deadsoftware.cavedroid.game.input.handler.touch
+
+import com.badlogic.gdx.utils.TimeUtils
+import ru.deadsoftware.cavedroid.MainConfig
+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.Joystick
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+import ru.deadsoftware.cavedroid.game.input.isInsideHotbar
+import ru.deadsoftware.cavedroid.game.mobs.Mob
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class JoystickInputHandler @Inject constructor(
+    private val mainConfig: MainConfig,
+    private val mobsController: MobsController,
+    private val gameWindowsManager: GameWindowsManager,
+    private val gameWorld: GameWorld,
+) : IGameInputHandler<MouseInputAction> {
+
+    private var activateTimeMs = 0L
+    private var cursorTimeoutMs = 100L
+
+    private var active = false
+        set(value) {
+            if (!value) {
+                resetVelocity()
+                if (TimeUtils.timeSinceMillis(activateTimeMs) < 200L &&
+                    mobsController.player.controlMode != Player.ControlMode.CURSOR) {
+                    mobsController.player.jump()
+                }
+            } else {
+                activateTimeMs = TimeUtils.millis()
+            }
+            field = value
+        }
+
+    private fun resetVelocity() {
+        mobsController.player.velocity.x = 0f
+
+        if (mobsController.player.isFlyMode) {
+            mobsController.player.velocity.y = 0f
+        }
+    }
+
+    override fun checkConditions(action: MouseInputAction): Boolean {
+        return gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE &&
+                mainConfig.isTouch &&
+//                mobsController.player.controlMode == Player.ControlMode.WALK &&
+                mainConfig.joystick != null &&
+                (action.actionKey is MouseInputActionKey.Touch) &&
+                (action.actionKey.pointer == mainConfig.joystick?.pointer || !active) &&
+                ((action.actionKey is MouseInputActionKey.Dragged) ||
+                        (action.screenX < action.cameraViewport.width / 2 && !action.actionKey.touchUp || active)) &&
+                !(action.actionKey is MouseInputActionKey.Screen && isInsideHotbar(action))
+
+    }
+
+    private fun handleTouchDown(action: MouseInputAction) {
+        val key = action.actionKey as MouseInputActionKey.Screen
+        mainConfig.joystick?.activate(action.screenX, action.screenY, key.pointer) ?: return
+        active = true
+    }
+
+    private fun handleTouchUp(action: MouseInputAction) {
+        mainConfig.joystick?.deactivate()
+        active = false
+    }
+
+    private fun handleCursor() {
+        val joystick = mainConfig.joystick ?: return
+
+        if (TimeUtils.timeSinceMillis(cursorTimeoutMs) < 150L) {
+            return
+        }
+
+        val pastCursorX = mobsController.player.cursorX
+        val pastCursorY = mobsController.player.cursorY
+
+        if (Math.abs(joystick.activeX - joystick.centerX) >= Joystick.RADIUS / 2) {
+            mobsController.player.cursorX += if (joystick.activeX > joystick.centerX) 1 else -1
+            cursorTimeoutMs = TimeUtils.millis()
+        }
+
+        if (Math.abs(joystick.activeY - joystick.centerY) >= Joystick.RADIUS / 2) {
+            mobsController.player.cursorY += if (joystick.activeY > joystick.centerY) 1 else -1
+            cursorTimeoutMs = TimeUtils.millis()
+        }
+
+        mobsController.player.checkCursorBounds(gameWorld)
+
+        if (mobsController.player.cursorX != pastCursorX || mobsController.player.cursorY != pastCursorY) {
+            mobsController.player.blockDamage = 0f
+        }
+    }
+
+    private fun handleDragged() {
+        if (!active) {
+            return
+        }
+
+        if (mobsController.player.controlMode == Player.ControlMode.CURSOR) {
+            handleCursor()
+            return
+        }
+
+        val joystick = mainConfig.joystick ?: return
+        val joyVector = joystick.getVelocityVector()
+
+        if (mobsController.player.isFlyMode) {
+            joyVector.scl(2f);
+        }
+
+        mobsController.player.velocity.x = joyVector.x
+
+        mobsController.player.setDir(
+            if (joyVector.x < 0) {
+                Mob.Direction.LEFT
+            } else {
+                Mob.Direction.RIGHT
+            }
+        )
+
+        if (mobsController.player.isFlyMode) {
+            mobsController.player.velocity.y = joyVector.y
+        }
+    }
+
+    override fun handle(action: MouseInputAction) {
+        when (action.actionKey) {
+            is MouseInputActionKey.Dragged -> handleDragged()
+            else -> {
+                if (action.actionKey.touchUp) {
+                    handleTouchUp(action)
+                } else {
+                    handleTouchDown(action)
+                }
+            }
+        }
+    }
+
+}
\ No newline at end of file
index ca91f486951fdf0b737ede5f84d8fbd3e92df3dd..8d18d070703931aeafdb4a41867d66e7c4b2cc35 100644 (file)
@@ -13,7 +13,7 @@ class KeyboardInputActionMapper @Inject constructor() {
         val actionKey = when (key) {
             Input.Keys.A, Input.Keys.LEFT -> KeyboardInputActionKey.Left
             Input.Keys.D, Input.Keys.RIGHT -> KeyboardInputActionKey.Right
-            Input.Keys.W, Input.Keys.SPACE -> KeyboardInputActionKey.Jump
+            Input.Keys.W, Input.Keys.SPACE -> KeyboardInputActionKey.Up
             Input.Keys.S -> KeyboardInputActionKey.Down
 
             Input.Keys.E -> KeyboardInputActionKey.OpenInventory
@@ -25,7 +25,17 @@ class KeyboardInputActionMapper @Inject constructor() {
             Input.Keys.GRAVE -> KeyboardInputActionKey.SwitchGameMode
             Input.Keys.M -> KeyboardInputActionKey.ShowMap
 
-            Input.Keys.T -> KeyboardInputActionKey.OpenCraft
+            Input.Keys.Q -> KeyboardInputActionKey.DropItem
+
+            Input.Keys.NUM_1 -> KeyboardInputActionKey.SelectHotbarSlot(0)
+            Input.Keys.NUM_2 -> KeyboardInputActionKey.SelectHotbarSlot(1)
+            Input.Keys.NUM_3 -> KeyboardInputActionKey.SelectHotbarSlot(2)
+            Input.Keys.NUM_4 -> KeyboardInputActionKey.SelectHotbarSlot(3)
+            Input.Keys.NUM_5 -> KeyboardInputActionKey.SelectHotbarSlot(4)
+            Input.Keys.NUM_6 -> KeyboardInputActionKey.SelectHotbarSlot(5)
+            Input.Keys.NUM_7 -> KeyboardInputActionKey.SelectHotbarSlot(6)
+            Input.Keys.NUM_8 -> KeyboardInputActionKey.SelectHotbarSlot(7)
+            Input.Keys.NUM_9 -> KeyboardInputActionKey.SelectHotbarSlot(8)
 
             else -> null
         }
index 254cee2f49f2cda6ccdc5d48ee34264ed00556e0..5ef450a3b700502f4b3a0b16cd7e95251c3bc53d 100644 (file)
@@ -19,9 +19,10 @@ class MouseInputActionMapper @Inject constructor(
         mouseY: Float,
         cameraViewport: Rectangle,
         button: Int,
-        touchUp: Boolean
+        touchUp: Boolean,
+        pointer: Int,
     ): MouseInputAction? {
-        val actionKey = mapActionKey(button, touchUp) ?: return null
+        val actionKey = mapActionKey(button, touchUp, pointer) ?: return null
 
         return MouseInputAction(
             screenX = getScreenX(mouseX),
@@ -35,11 +36,12 @@ class MouseInputActionMapper @Inject constructor(
         mouseX: Float,
         mouseY: Float,
         cameraViewport: Rectangle,
+        pointer: Int,
     ): MouseInputAction {
         return MouseInputAction(
             screenX = getScreenX(mouseX),
             screenY = getScreenY(mouseY),
-            actionKey = MouseInputActionKey.Dragged,
+            actionKey = MouseInputActionKey.Dragged(pointer),
             cameraViewport = cameraViewport,
         )
     }
@@ -59,12 +61,12 @@ class MouseInputActionMapper @Inject constructor(
         )
     }
 
-    private fun mapActionKey(button: Int, touchUp: Boolean): MouseInputActionKey? {
+    private fun mapActionKey(button: Int, touchUp: Boolean, pointer: Int): MouseInputActionKey? {
         return when (button) {
             Input.Buttons.LEFT -> MouseInputActionKey.Left(touchUp)
             Input.Buttons.RIGHT -> MouseInputActionKey.Right(touchUp)
             Input.Buttons.MIDDLE -> MouseInputActionKey.Middle(touchUp)
-            -1 -> MouseInputActionKey.Touch(touchUp)
+            -1 -> MouseInputActionKey.Screen(touchUp, pointer)
             else -> null
         }
     }
index 641ffe554ec84255bbe0b1099b3f67a9aded0464..2ad4287577e770758ab76bb2a7db3da59cb6616e 100644 (file)
@@ -40,7 +40,7 @@ public class FallingGravel extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta) {
         if (mVelocity.isZero()) {
             gameWorld.setForeMap(getMapX(), getUpperMapY(), gameItemsHolder.getBlock("gravel"));
             kill();
@@ -51,6 +51,11 @@ public class FallingGravel extends Mob {
     public void changeDir() {
     }
 
+    @Override
+    public void damage(int damage) {
+        // no-op
+    }
+
     @Override
     public void draw(SpriteBatch spriteBatch, float x, float y, float delta) {
         @CheckForNull final Texture texture = Assets.blockTextures.get("gravel");
index f41da96872c12aa7fc32ce6dffc885822d13935d..970ff538c0b762bf88bcae6949e7e95c96355a5b 100644 (file)
@@ -41,7 +41,7 @@ public class FallingSand extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta) {
         if (mVelocity.isZero()) {
             gameWorld.setForeMap(getMapX(), getUpperMapY(), gameItemsHolder.getBlock("sand"));
             kill();
@@ -52,6 +52,11 @@ public class FallingSand extends Mob {
     public void changeDir() {
     }
 
+    @Override
+    public void damage(int damage) {
+        // no-op
+    }
+
     @Override
     public void draw(SpriteBatch spriteBatch, float x, float y, float delta) {
         @CheckForNull final Texture texture = Assets.blockTextures.get("sand");
index 415ef30376503a491a3eefd9eb8c127d53089b77..62ddc50d4df7a6e00217a3a1f161cee8e745cb44 100644 (file)
@@ -1,13 +1,16 @@
 package ru.deadsoftware.cavedroid.game.mobs;
 
 import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.graphics.Color;
 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
 import com.badlogic.gdx.math.MathUtils;
 import com.badlogic.gdx.math.Rectangle;
 import com.badlogic.gdx.math.Vector2;
+import com.badlogic.gdx.utils.Timer;
 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
+import javax.annotation.CheckForNull;
 import java.io.Serializable;
 
 /**
@@ -15,6 +18,11 @@ import java.io.Serializable;
  */
 public abstract class Mob extends Rectangle implements Serializable {
 
+    private static final float DAMAGE_TINT_TIMEOUT_S = 0.5f;
+    private static final Color DAMAGE_TINT_COLOR = new Color(0xff8080ff);
+
+    private static final float HIT_RANGE = 8f;
+
     protected static int ANIMATION_SPEED = 360;
 
     public enum Type {
@@ -51,6 +59,14 @@ public abstract class Mob extends Rectangle implements Serializable {
         }
     }
 
+    private class ResetTakeDamageTask extends Timer.Task {
+
+        @Override
+        public void run() {
+            mTakingDamage = false;
+        }
+    }
+
     protected Vector2 mVelocity;
     protected Type mType;
     protected int mAnimDelta = ANIMATION_SPEED;
@@ -64,6 +80,9 @@ public abstract class Mob extends Rectangle implements Serializable {
     private final int mMaxHealth;
     private int mHealth;
 
+    private transient boolean mTakingDamage = false;
+    @CheckForNull private transient ResetTakeDamageTask mResetTakeDamageTask = null;
+
     /**
      * @param x          in pixels
      * @param y          in pixels
@@ -99,17 +118,21 @@ public abstract class Mob extends Rectangle implements Serializable {
     }
 
     protected final void updateAnimation(float delta) {
-        if (mVelocity.x != 0f || Math.abs(mAnim) > mAnimDelta * delta) {
-            mAnim += mAnimDelta * delta;
+        final float velocityMultiplier = (Math.abs(getVelocity().x) / getSpeed());
+        final float animMultiplier = (velocityMultiplier == 0f ? 1f : velocityMultiplier) * delta;
+        final float maxAnim = 60f * (velocityMultiplier == 0f ? 1f : velocityMultiplier);
+
+        if (mVelocity.x != 0f || Math.abs(mAnim) > mAnimDelta * animMultiplier) {
+            mAnim += mAnimDelta * animMultiplier;
         } else {
             mAnim = 0;
         }
 
-        if (mAnim > 60f) {
-            mAnim = 60f;
+        if (mAnim > maxAnim) {
+            mAnim = maxAnim;
             mAnimDelta = -ANIMATION_SPEED;
-        } else if (mAnim < -60f) {
-            mAnim = -60f;
+        } else if (mAnim < -maxAnim) {
+            mAnim = -maxAnim;
             mAnimDelta = ANIMATION_SPEED;
         }
 
@@ -257,6 +280,8 @@ public abstract class Mob extends Rectangle implements Serializable {
 
         mHealth -= damage;
         checkHealth();
+
+        setTakingDamage(true);
     }
 
     public void heal(int heal) {
@@ -273,9 +298,35 @@ public abstract class Mob extends Rectangle implements Serializable {
         checkHealth();
     }
 
+    public Rectangle getHitBox() {
+        return new Rectangle(x - HIT_RANGE, y - HIT_RANGE, width + HIT_RANGE, height + HIT_RANGE);
+    }
+
+    public boolean isTakingDamage() {
+        return mTakingDamage;
+    }
+
+    public void setTakingDamage(boolean takingDamage) {
+        mTakingDamage = takingDamage;
+
+        if (takingDamage) {
+            if (mResetTakeDamageTask != null && mResetTakeDamageTask.isScheduled()) {
+                mResetTakeDamageTask.cancel();
+            } else if (mResetTakeDamageTask == null) {
+                mResetTakeDamageTask = new ResetTakeDamageTask();
+            }
+
+            Timer.schedule(mResetTakeDamageTask, DAMAGE_TINT_TIMEOUT_S);
+        }
+    }
+
+    protected Color getTintColor() {
+        return isTakingDamage() ? DAMAGE_TINT_COLOR : Color.WHITE;
+    }
+
     public abstract void draw(SpriteBatch spriteBatch, float x, float y, float delta);
 
-    public abstract void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta);
+    public abstract void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta);
 
     public abstract void changeDir();
 
index 0ec111bbd66bd5b96ff38a9cdd7e323d53b7bf0c..158d22a9e55445ae1056e680c4fb3621f03a03e0 100644 (file)
@@ -2,18 +2,22 @@ package ru.deadsoftware.cavedroid.game.mobs
 
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager
 import java.io.Serializable
 import java.util.*
 import javax.inject.Inject
 
 @GameScope
 class MobsController @Inject constructor(
-    gameItemsHolder: GameItemsHolder
+    gameItemsHolder: GameItemsHolder,
+    tooltipManager: TooltipManager,
 ) : Serializable {
 
     private val _mobs = LinkedList<Mob>()
 
-    val player: Player = Player(gameItemsHolder)
+    val player: Player =
+        Player(gameItemsHolder, tooltipManager)
 
     val mobs: List<Mob>
         get() = _mobs
index b55f42934be9f2d8d100d72d7068aa6e055c2951..68c457ee05887ee81521ac93bffe0740e8e84688 100644 (file)
@@ -27,8 +27,18 @@ class Pig(x: Float, y: Float) : Mob(x, y, WIDTH, HEIGHT, randomDir(), Type.MOB,
     override fun jump() {
         velocity.y = JUMP_VELOCITY
     }
-    
-    override fun ai(world: GameWorld, gameItemsHolder: GameItemsHolder, delta: Float) {
+
+    override fun damage(damage: Int) {
+        super.damage(damage)
+
+        if (damage > 0) {
+            if (canJump()) {
+                jump()
+            }
+        }
+    }
+
+    override fun ai(world: GameWorld, gameItemsHolder: GameItemsHolder, mobsController: MobsController, delta: Float) {
         if (MathUtils.randomBoolean(delta)) {
             if (velocity.x != 0f) {
                 velocity.x = 0f
@@ -45,19 +55,19 @@ class Pig(x: Float, y: Float) : Mob(x, y, WIDTH, HEIGHT, randomDir(), Type.MOB,
         val rightLegX = x + getRightLegRelativeX(direction)
         val legY = y + getLegsRelativeY()
 
-        spriteBatch.drawSprite(getBackgroundLeg(), leftLegX, legY, -anim)
-        spriteBatch.drawSprite(getBackgroundLeg(), rightLegX, legY, -anim)
-        spriteBatch.drawSprite(getBody(direction), x, y)
-        spriteBatch.drawSprite(getForegroundLeg(), leftLegX, legY, anim)
-        spriteBatch.drawSprite(getForegroundLeg(), rightLegX, legY, anim)
+        spriteBatch.drawSprite(getBackgroundLeg(), leftLegX, legY, -anim, tint = tintColor)
+        spriteBatch.drawSprite(getBackgroundLeg(), rightLegX, legY, -anim, tint = tintColor)
+        spriteBatch.drawSprite(getBody(direction), x, y, tint = tintColor)
+        spriteBatch.drawSprite(getForegroundLeg(), leftLegX, legY, anim, tint = tintColor)
+        spriteBatch.drawSprite(getForegroundLeg(), rightLegX, legY, anim, tint = tintColor)
     }
     
     
     private companion object {
         private const val WIDTH = 25f
         private const val HEIGHT = 18f
-        private const val SPEED =  69.072f
+        private const val SPEED =  48f
         private const val JUMP_VELOCITY = -133.332f
-        private const val MAX_HEALTH = 10;
+        private const val MAX_HEALTH = 10
     }
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/player/Inventory.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/player/Inventory.kt
new file mode 100644 (file)
index 0000000..3012d66
--- /dev/null
@@ -0,0 +1,136 @@
+package ru.deadsoftware.cavedroid.game.mobs.player
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.objects.drop.Drop
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager
+import java.io.Serializable
+
+class Inventory(
+    val size: Int,
+    val hotbarSize: Int,
+    gameItemsHolder: GameItemsHolder,
+    tooltipManager: TooltipManager,
+) : Serializable {
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    @Transient
+    private lateinit var tooltipManager: TooltipManager
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    @Transient
+    private lateinit var fallbackItem: InventoryItem
+
+    init {
+        fallbackItem = gameItemsHolder.fallbackItem.toInventoryItem()
+        this.tooltipManager = tooltipManager
+
+        if (size < 0 || hotbarSize < 0 || hotbarSize > size) {
+            throw IllegalArgumentException("Invalid inventory sizes: hotbarSize=$hotbarSize; size=$size")
+        }
+    }
+
+    private val _items = Array(size) { InventoryItem(gameItemsHolder.fallbackItem) }
+
+    val items get() = _items.asList() as MutableList<InventoryItem>
+
+    val hotbarItems get() = items.subList(0, hotbarSize)
+
+    var activeSlot = 0
+        set(value) {
+            if (value in 0 ..< hotbarSize) {
+                field = value
+                showCurrentItemTooltip()
+            }
+        }
+
+    fun showCurrentItemTooltip() {
+        tooltipManager.showHotbarTooltip(activeItem.item.params.name)
+    }
+
+    val activeItem get() = _items[activeSlot]
+
+    fun initItems(gameItemsHolder: GameItemsHolder, tooltipManager: TooltipManager) {
+        this.tooltipManager = tooltipManager
+        fallbackItem = gameItemsHolder.fallbackItem.toInventoryItem()
+        _items.forEach { item ->
+            item.init(gameItemsHolder)
+        }
+    }
+
+    private fun getItemPickSlot(drop: Drop): Int {
+        val item = drop.item
+
+        for (i in _items.indices) {
+            val inventoryItem = _items[i]
+
+            if (item == inventoryItem.item && inventoryItem.canBeAdded()) {
+                return i
+            }
+        }
+
+        for (i in _items.indices) {
+            val inventoryItem = _items[i]
+
+            if (inventoryItem.item.isNone()) {
+                return i
+            }
+        }
+
+        return -1
+    }
+
+    fun canPickItem(drop: Drop): Boolean {
+        return getItemPickSlot(drop) >= 0
+    }
+
+    fun pickDrop(drop: Drop) {
+        val slot = getItemPickSlot(drop).takeIf { it >= 0 } ?: return
+        val inventoryItem = _items[slot]
+
+        if (inventoryItem.item == drop.item) {
+            if (inventoryItem.canBeAdded(drop.amount)) {
+                inventoryItem.add(drop.amount)
+                drop.pickedUp = true
+            } else {
+                val addCount = inventoryItem.item.params.maxStack - inventoryItem.amount
+                inventoryItem.add(addCount)
+                drop.subtract(addCount)
+                pickDrop(drop)
+            }
+        } else {
+            _items[slot] = drop.item.toInventoryItem(drop.amount)
+            if (slot == activeSlot) {
+                showCurrentItemTooltip()
+            }
+            drop.pickedUp = true
+        }
+    }
+
+    fun addItem(item: Item) {
+        _items.copyInto(
+            destination = _items,
+            destinationOffset = 1,
+            startIndex = 0,
+            endIndex = size - 1
+        )
+
+        _items[0] = item.toInventoryItem(item.params.maxStack)
+        showCurrentItemTooltip()
+    }
+
+    @JvmOverloads
+    fun decreaseItemAmount(slot: Int, count: Int = 1) {
+        val item = _items[slot]
+        item.subtract(count)
+        if (item.amount <= 0) {
+            _items[slot] = fallbackItem
+        }
+    }
+
+    @JvmOverloads
+    fun decreaseCurrentItemAmount(count: Int = 1) {
+        decreaseItemAmount(activeSlot, count)
+    }
+}
\ No newline at end of file
similarity index 70%
rename from core/src/ru/deadsoftware/cavedroid/game/mobs/Player.java
rename to core/src/ru/deadsoftware/cavedroid/game/mobs/player/Player.java
index 0e8af6e0eed0407b1cc3a4be057ae2dc344f5b87..8ea87c11d8b42868a4dc393c9f100689496b4788 100644 (file)
@@ -1,34 +1,41 @@
-package ru.deadsoftware.cavedroid.game.mobs;
+package ru.deadsoftware.cavedroid.game.mobs.player;
 
 import com.badlogic.gdx.graphics.g2d.Sprite;
 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
 import com.badlogic.gdx.math.MathUtils;
 import com.badlogic.gdx.math.Vector2;
 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
+import ru.deadsoftware.cavedroid.game.mobs.Mob;
+import ru.deadsoftware.cavedroid.game.mobs.MobsController;
 import ru.deadsoftware.cavedroid.game.model.block.Block;
 import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
 import ru.deadsoftware.cavedroid.game.model.item.Item;
-import ru.deadsoftware.cavedroid.game.objects.Drop;
+import ru.deadsoftware.cavedroid.game.objects.drop.Drop;
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController;
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 import ru.deadsoftware.cavedroid.misc.Assets;
 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 {
 
     private static final float SPEED = 69.072f;
     private static final float JUMP_VELOCITY = -133.332f;
-    private static final int MAX_HEALTH = 20;
+    private static final int SURVIVAL_CURSOR_RANGE = 4;
+
+    public static final int MAX_HEALTH = 20;
+    public static final int INVENTORY_SIZE = 36;
+    public static final int HOTBAR_SIZE = 9;
 
     private boolean hitting = false, hittingWithDamage = false;
     private float hitAnim = 0f;
     private float hitAnimDelta = ANIMATION_SPEED;
 
-    public final ArrayList<InventoryItem> inventory;
-    public int slot;
+    public final Inventory inventory;
+
     public int gameMode;
     public boolean swim;
     public float headRotation = 0f;
@@ -47,26 +54,16 @@ public class Player extends Mob {
         CURSOR
     }
 
-    public Player(GameItemsHolder gameItemsHolder) {
+    public Player(GameItemsHolder gameItemsHolder, TooltipManager tooltipManager) {
         super(0, 0, 4, 30, randomDir(), Type.MOB, MAX_HEALTH);
-        inventory = new ArrayList<>(36);
-        for (int i = 0; i < 36; i++) {
-            inventory.add(gameItemsHolder.getFallbackItem().toInventoryItem());
-        }
+        inventory = new Inventory(INVENTORY_SIZE, HOTBAR_SIZE, gameItemsHolder, tooltipManager);
         swim = false;
     }
 
-    public void initInventory(GameItemsHolder gameItemsHolder) {
-        for (InventoryItem invItem : inventory) {
-            invItem.init(gameItemsHolder);
-        }
+    public void initInventory(GameItemsHolder gameItemsHolder, TooltipManager tooltipManager) {
+        inventory.initItems(gameItemsHolder, tooltipManager);
     }
 
-    @CheckForNull
-    public Item inventory(int i) {
-        return inventory.get(i).getItem();
-    }
-    
     public void respawn(GameWorld gameWorld, GameItemsHolder itemsHolder) {
         Vector2 pos = getSpawnPoint(gameWorld, itemsHolder);
         this.x = pos.x;
@@ -80,34 +77,17 @@ public class Player extends Mob {
         if (gameMode == 1) {
             return;
         }
-        getCurrentItem().setAmount(getCurrentItem().getAmount() - 1);
-        if (getCurrentItem().getAmount() <= 0) {
+
+        final InventoryItem item = inventory.getActiveItem();
+        item.subtract();
+        if (item.getAmount() <= 0) {
             setCurrentInventorySlotItem(gameItemsHolder.getFallbackItem());
         }
     }
 
-    public InventoryItem getCurrentItem() {
-        return inventory.get(slot);
-    }
-
-    public void pickUpDrop(Drop drop) {
-        for (InventoryItem invItem : inventory) {
-            if (!invItem.getItem().isTool()
-                    && invItem.getItem() == drop.getItem()
-                    && invItem.getAmount() < invItem.getItem().getParams().getMaxStack()) {
-                invItem.setAmount(invItem.getAmount() + 1);
-                drop.setPickedUp(true);
-                return;
-            }
-        }
+    public void dropCurrentItem(DropController dropController) {
+        final InventoryItem activeItem = inventory.getActiveItem();
 
-        for (int i = 0; i < inventory.size(); i++) {
-            if (inventory(i) == null || inventory(i).getParams().getKey().equals(GameItemsHolder.FALLBACK_ITEM_KEY)) {
-                inventory.set(i, drop.getItem().toInventoryItem());
-                drop.setPickedUp(true);
-                break;
-            }
-        }
     }
 
     private Vector2 getSpawnPoint(GameWorld gameWorld, GameItemsHolder itemsHolder) {
@@ -144,7 +124,7 @@ public class Player extends Mob {
     }
 
     public void setCurrentInventorySlotItem(Item item) {
-        inventory.set(slot, item.toInventoryItem());
+        inventory.getItems().set(inventory.getActiveSlot(), item.toInventoryItem());
     }
 
     @Override
@@ -154,9 +134,47 @@ public class Player extends Mob {
 
     @Override
     public void jump() {
+        if (!canJump()) {
+            if (gameMode == 1) {
+                if (isFlyMode()) {
+                    setFlyMode(false);
+                } else {
+                    getVelocity().y = 0f;
+                    setFlyMode(true);
+                }
+            }
+            return;
+        }
         mVelocity.y = JUMP_VELOCITY;
     }
 
+    private boolean checkBlockCanBeHit(Block block) {
+        return !block.isNone() && block.getParams().getHitPoints() >= 0;
+    }
+
+    /**
+     * @return true if any mob fas hit
+     */
+    private boolean hitMobs(GameItemsHolder gameItemsHolder, MobsController mobsController) {
+        if (!hitting || !hittingWithDamage) {
+            return false;
+        }
+
+        boolean result = false;
+        for (Mob mob : mobsController.getMobs()) {
+            if (overlaps(mob.getHitBox())) {
+                final Item activeItem = inventory.getActiveItem().getItem();
+                final Item.Tool tool = activeItem.isTool() ? (Item.Tool) activeItem : null;
+                if (tool != null) {
+                    decreaseCurrentItemCount(gameItemsHolder);
+                }
+                result = true;
+                mob.damage(MathUtils.floor(tool != null ? tool.getMobDamageMultiplier() : 1));
+            }
+        }
+        return result;
+    }
+
     private void hitBlock(GameWorld gameWorld, GameItemsHolder gameItemsHolder) {
         if (!hitting || !hittingWithDamage) {
             return;
@@ -165,15 +183,20 @@ public class Player extends Mob {
         final Block foregroundBlock = gameWorld.getForeMap(cursorX, cursorY);
         final Block backgroundBlock = gameWorld.getBackMap(cursorX, cursorY);
 
-        if ((!foregroundBlock.isNone() && foregroundBlock.getParams().getHitPoints() >= 0) ||
-                (foregroundBlock.isNone() && !backgroundBlock.isNone() && backgroundBlock.getParams().getHitPoints() >= 0)) {
+
+        if ((checkBlockCanBeHit(foregroundBlock)) ||
+                (foregroundBlock.isNone() && checkBlockCanBeHit(backgroundBlock))) {
             if (gameMode == 0) {
-                if (!foregroundBlock.isNone() && blockDamage >= foregroundBlock.getParams().getHitPoints()) {
-                    gameWorld.destroyForeMap(cursorX, cursorY);
-                    blockDamage = 0;
-                } else if (!backgroundBlock.isNone() && blockDamage >= backgroundBlock.getParams().getHitPoints()) {
-                    gameWorld.destroyBackMap(cursorX, cursorY);
-                    blockDamage = 0;
+                if (!foregroundBlock.isNone()) {
+                    if (blockDamage >= foregroundBlock.getParams().getHitPoints()) {
+                        gameWorld.destroyForeMap(cursorX, cursorY);
+                        blockDamage = 0;
+                    }
+                } else if (!backgroundBlock.isNone()) {
+                    if (blockDamage >= backgroundBlock.getParams().getHitPoints()) {
+                        gameWorld.destroyBackMap(cursorX, cursorY);
+                        blockDamage = 0;
+                    }
                 }
             } else {
                 if (!foregroundBlock.isNone()) {
@@ -189,9 +212,14 @@ public class Player extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta) {
         updateAnimation(delta);
-        hitBlock(gameWorld, gameItemsHolder);
+
+        if (!hitMobs(gameItemsHolder, mobsController)) {
+            hitBlock(gameWorld, gameItemsHolder);
+        } else {
+            stopHitting();
+        }
 
         if (gameMode == 1) {
             return;
@@ -201,9 +229,9 @@ public class Player extends Mob {
         final Block backgroundBlock = gameWorld.getBackMap(cursorX, cursorY);
         @CheckForNull final Block target;
 
-        if (!foregroundBlock.isNone() && foregroundBlock.getParams().getHitPoints() >= 0) {
+        if (checkBlockCanBeHit(foregroundBlock)) {
             target = foregroundBlock;
-        } else if (!backgroundBlock.isNone() && backgroundBlock.getParams().getHitPoints() >= 0) {
+        } else if (checkBlockCanBeHit(backgroundBlock)) {
             target = backgroundBlock;
         } else {
             target = null;
@@ -212,7 +240,7 @@ public class Player extends Mob {
         final boolean canHitBlock = target != null;
 
         float multiplier = 1f;
-        final Item currentItem = inventory.get(slot).getItem();
+        final Item currentItem = inventory.getActiveItem().getItem();
         if (currentItem instanceof Item.Tool && canHitBlock) {
             if (target.getParams().getToolType() == currentItem.getClass()
                     && ((Item.Tool)currentItem).getLevel() >= target.getParams().getToolLevel()) {
@@ -253,8 +281,22 @@ public class Player extends Mob {
         super.heal(heal);
     }
 
+    public void checkCursorBounds(GameWorld gameWorld) {
+        if (gameMode == 0) {
+            int minCursorX = getMapX() - SURVIVAL_CURSOR_RANGE;
+            int maxCursorX = getMapX() + SURVIVAL_CURSOR_RANGE;
+            int minCursorY = getMiddleMapY() - SURVIVAL_CURSOR_RANGE;
+            int maxCursorY = getMiddleMapY() + SURVIVAL_CURSOR_RANGE;
+
+            cursorX = MathUtils.clamp(cursorX, minCursorX, maxCursorX);
+            cursorY = MathUtils.clamp(cursorY, minCursorY, maxCursorY);
+        }
+
+        cursorY = MathUtils.clamp(cursorY, 0, gameWorld.getHeight() - 1);
+    }
+
     private void drawItem(SpriteBatch spriteBatch, float x, float y, float anim) {
-        final Item item = inventory(slot);
+        final Item item = inventory.getActiveItem().getItem();
 
         if (item == null || item.isNone()) {
             return;
@@ -372,6 +414,13 @@ public class Player extends Mob {
             frontHandAnim = -rightHandAnim;
         }
 
+        backHand.setColor(getTintColor());
+        backLeg.setColor(getTintColor());
+        frontLeg.setColor(getTintColor());
+        head.setColor(getTintColor());
+        body.setColor(getTintColor());
+        frontHand.setColor(getTintColor());
+
         SpriteUtilsKt.drawSprite(spriteBatch, backHand, x + 2, y + 8, backHandAnim);
 
         if (looksLeft()) {
index 76c6ac580c0df1a6604c447e3f1c0e930496f456..fa99bfd91ed2afa2ed63221b23374ba221ad1baa 100644 (file)
@@ -20,14 +20,14 @@ sealed class Block {
     val spriteWidth: Float get() = 16f - params.spriteMargins.left - params.spriteMargins.right
     val spriteHeight: Float get() = 16f - params.spriteMargins.top - params.spriteMargins.bottom
 
-    private var animation: Array<Sprite>? = null
+    protected var animation: Array<Sprite>? = null
 
     private var _sprite: Sprite? = null
         get() {
             return animation?.get(currentAnimationFrame) ?: field
         }
 
-    val sprite: Sprite
+    open val sprite: Sprite
         get() = requireNotNull(_sprite) { "null sprite for block '${params.key}'" }
 
     private val currentAnimationFrame: Int
@@ -111,6 +111,21 @@ sealed class Block {
         return this is Slab
     }
 
+    fun isContainer(): Boolean {
+        contract { returns(true) implies (this@Block is Container) }
+        return this is Container
+    }
+
+    fun isFurnace(): Boolean {
+        contract { returns(true) implies (this@Block is Furnace) }
+        return this is Furnace
+    }
+
+    fun isChest(): Boolean {
+        contract { returns(true) implies (this@Block is Chest) }
+        return this is Chest
+    }
+
     fun isNone(): Boolean {
         contract { returns(true) implies (this@Block is None) }
         return this is None
@@ -125,6 +140,8 @@ sealed class Block {
         )
     }
 
+    sealed class Container() : Block()
+
     data class None(
         override val params: CommonBlockParams
     ) : Block()
@@ -133,6 +150,41 @@ sealed class Block {
         override val params: CommonBlockParams,
     ) : Block()
 
+    data class Furnace(
+        override val params: CommonBlockParams,
+    ): Container() {
+
+        override val sprite: Sprite
+            get() = getSprite(false)
+
+        private fun getSprite(isActive: Boolean): Sprite {
+            return animation?.let { animation ->
+                if (isActive) {
+                    animation[1]
+                } else {
+                    animation[0]
+                }
+            } ?: sprite
+        }
+
+        fun draw(spriter: SpriteBatch, x: Float, y: Float, isActive: Boolean) {
+            getSprite(isActive).apply {
+                setBounds(
+                    /* x = */ x + params.spriteMargins.left,
+                    /* y = */ y + params.spriteMargins.top,
+                    /* width = */ spriteWidth,
+                    /* height = */ spriteHeight
+                )
+                draw(spriter)
+            }
+        }
+
+    }
+
+    data class Chest(
+        override val params: CommonBlockParams
+    ): Container()
+
     data class Slab(
         override val params: CommonBlockParams,
         val fullBlockKey: String,
index e98f1be893ae3071f7be4c54226bd767e21f8c0c..369f7523430927e4f9d5a2c55e93b0ec3fc3fa80 100644 (file)
@@ -4,7 +4,6 @@ import com.badlogic.gdx.graphics.Texture
 import ru.deadsoftware.cavedroid.game.model.item.Item
 
 data class CommonBlockParams(
-    @Deprecated("numeric id's will be removed") val id: Int?,
     val key: String,
     val collisionMargins: BlockMargins,
     val hitPoints: Int,
index eebe13f0992644fcf0a6c65335ad06845a993808..c8b2a869761f62a0fe07de152c1ce543633868dd 100644 (file)
@@ -4,7 +4,7 @@ import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
 import ru.deadsoftware.cavedroid.game.model.item.Item
 
 data class CraftingRecipe(
-    val input: List<Item>,
+    val input: List<Regex>,
     val output: CraftingResult
 )
 
index c6065b70c008586345df38c1ec323b759653a224..69b1f18379bad1a778a62fa5e979e7f16abc93b9 100644 (file)
@@ -5,7 +5,6 @@ import kotlinx.serialization.Serializable
 
 @Serializable
 data class BlockDto(
-    @Deprecated("numeric ids will be removed") @SerialName("id") val id: Int? = null,
     @SerialName("left") val left: Int = 0,
     @SerialName("top") val top: Int = 0,
     @SerialName("right") val right: Int = 0,
index fe28e6440adc30a52eadaab656d6bbb9306592f3..373db466baf14f54fb86b352be00acbb8353f3e6 100644 (file)
@@ -5,7 +5,6 @@ import kotlinx.serialization.Serializable
 
 @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 = "normal",
     @SerialName("texture") val texture: String,
@@ -18,4 +17,7 @@ data class ItemDto(
     @SerialName("bottom_slab_block") val bottomSlabBlock: String? = null,
     @SerialName("tool_level") val toolLevel: Int? = null,
     @SerialName("max_stack") val maxStack: Int = 64,
+    @SerialName("tint") val tint: String? = null,
+    @SerialName("burning_time") val burningTime: Long? = null,
+    @SerialName("smelt_product") val smeltProduct: String? = null,
 )
index 291cfb896aab462b4dda9b7eb1a8d473712dffc9..00af08b529e17caf6c89ac9051b49a56bd918824 100644 (file)
@@ -3,9 +3,10 @@ package ru.deadsoftware.cavedroid.game.model.item
 import ru.deadsoftware.cavedroid.misc.utils.SpriteOrigin
 
 data class CommonItemParams(
-    @Deprecated("numeric id's will be removed") val id: Int?,
     val key: String,
     val name: String,
     val inHandSpriteOrigin: SpriteOrigin,
     val maxStack: Int,
+    val burningTimeMs: Long?,
+    val smeltProductKey: String?,
 )
\ No newline at end of file
index 6c1598315cb5037813c8a385ddb06f5f062846ea..3cd406b731f5e2e0dc8a128926a4baef24d5a246 100644 (file)
@@ -9,26 +9,68 @@ import ru.deadsoftware.cavedroid.misc.utils.drawSprite
 import ru.deadsoftware.cavedroid.misc.utils.drawString
 import ru.deadsoftware.cavedroid.misc.utils.px
 import java.io.Serializable
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.contract
 
 class InventoryItem @JvmOverloads constructor(
     val itemKey: String,
-    var amount: Int = 1,
+    _amount: Int = 1,
 ) : Serializable {
 
+    var amount = _amount
+        set(value) {
+            field = if (value < 0) {
+                0
+            } else {
+                value
+            }
+        }
+
     @Transient
-    lateinit var item: Item
-        private set
+    private var _item: Item? = null
+
+    var item: Item
+        get() {
+            requireNotNull(_item) { "_item is null" }
+            return _item.takeIf { amount > 0 } ?: throw IllegalArgumentException("Accessing item with zero amount")
+        }
+        private set (value) {
+            _item = value
+        }
 
     @JvmOverloads
-    constructor(_item: Item, amount: Int = 1) : this(_item.params.key, amount) {
-        item = _item
+    constructor(item: Item, amount: Int = 1) : this(item.params.key, amount) {
+        _item = item
     }
 
     fun init(gameItemsHolder: GameItemsHolder) {
-        if (this::item.isInitialized) {
+        if (_item != null) {
             return
         }
-        item = gameItemsHolder.getItem(itemKey)
+        _item = gameItemsHolder.getItem(itemKey)
+    }
+
+    @JvmOverloads
+    fun add(count: Int = 1) {
+        if (count > 0 && Int.MAX_VALUE - count < amount) {
+            throw IllegalArgumentException("$amount + $count exceeds Int.MAX_VALUE")
+        }
+
+        amount += count
+    }
+
+    @JvmOverloads
+    fun subtract(count: Int = 1) {
+        if (count < 0) {
+            throw IllegalArgumentException("Can't subtract negative amount")
+        }
+
+        add(-count)
+    }
+
+    @JvmOverloads
+    fun canBeAdded(count: Int = 1): Boolean {
+        return amount + count <= item.params.maxStack
     }
 
     private fun drawAmountText(spriteBatch: SpriteBatch, text: String,  x: Float, y: Float) {
@@ -58,7 +100,9 @@ class InventoryItem @JvmOverloads constructor(
         }
 
         val sprite = item.sprite
-        spriteBatch.drawSprite(sprite, x, y)
+        val placeableMarginTop = (item as? Item.Placeable)?.block?.params?.spriteMargins?.top ?: 0
+        val placeableMarginLeft = (item as? Item.Placeable)?.block?.params?.spriteMargins?.left ?: 0
+        spriteBatch.drawSprite(sprite, x + placeableMarginLeft, y + placeableMarginTop)
 
         if (amount < 2) {
             return
@@ -87,4 +131,12 @@ class InventoryItem @JvmOverloads constructor(
         }
     }
 
+    companion object {
+
+        @OptIn(ExperimentalContracts::class)
+        fun InventoryItem?.isNoneOrNull(): Boolean {
+            contract { returns(false) implies(this@isNoneOrNull != null) }
+            return this?.item == null || this.item.isNone()
+        }
+    }
 }
index 1e6755ab7c367b8886301e3173c0664df038e3ec..bf2a55cd6a9d164870c6d4ebb8339310a0bece86 100644 (file)
@@ -46,8 +46,8 @@ sealed class Item {
     }
 
     fun isUsable(): Boolean {
-        contract { returns(true) implies (this@Item is Placeable) }
-        return this is Placeable
+        contract { returns(true) implies (this@Item is Usable) }
+        return this is Usable
     }
 
     @JvmOverloads
@@ -66,10 +66,6 @@ sealed class Item {
         abstract val level: Int
     }
 
-    sealed class Usable : Item() {
-        abstract val useActionKey: String
-    }
-
     sealed class Placeable : Item() {
         abstract val block: BlockModel
         override val sprite: Sprite get() = block.sprite
@@ -82,6 +78,12 @@ sealed class Item {
             get() = throw IllegalAccessException("Trying to get sprite of None")
     }
 
+    data class Usable(
+        override val params: CommonItemParams,
+        override val sprite: Sprite,
+        val useActionKey: String
+    ) : Item()
+
     data class Block(
         override val params: CommonItemParams,
         override val block: BlockModel
@@ -134,11 +136,5 @@ sealed class Item {
         override val blockDamageMultiplier: Float,
         override val level: Int,
     ) : Tool()
-    
-    data class Bucket(
-        override val params: CommonItemParams,
-        override val sprite: Sprite,
-        override val useActionKey: String
-    ) : Usable()
 
 }
\ No newline at end of file
index 85d8cb722d457511ba96d4f46c10e81e994a0e38..1b641ed338a420a3cf502af7a9cd50f3e92e98b0 100644 (file)
@@ -23,6 +23,8 @@ class BlockMapper @Inject constructor(
             "water" -> Water(commonBlockParams, requireNotNull(dto.state))
             "lava" -> Lava(commonBlockParams, requireNotNull(dto.state))
             "slab" -> Slab(commonBlockParams, requireNotNull(dto.fullBlock), requireNotNull(dto.otherPart))
+            "furnace" -> Furnace(commonBlockParams)
+            "chest" -> Chest(commonBlockParams)
             "none" -> None(commonBlockParams)
             else -> Normal(commonBlockParams)
         }
@@ -30,7 +32,6 @@ class BlockMapper @Inject constructor(
 
     private fun mapCommonParams(key: String, dto: BlockDto): CommonBlockParams {
         return CommonBlockParams(
-            id = dto.id,
             key = key,
             collisionMargins = BlockMargins(
                 left = dto.left,
index b951f261e795b7937d40c0c6a6f3b08272e50d72..a0949a05d2c082c427cb6ccec26524c6a87a047c 100644 (file)
@@ -11,6 +11,7 @@ import ru.deadsoftware.cavedroid.game.model.item.Item.*
 import ru.deadsoftware.cavedroid.misc.Assets
 import ru.deadsoftware.cavedroid.misc.utils.AssetLoader
 import ru.deadsoftware.cavedroid.misc.utils.SpriteOrigin
+import ru.deadsoftware.cavedroid.misc.utils.colorFromHexString
 import javax.inject.Inject
 
 @Reusable
@@ -23,7 +24,7 @@ class ItemMapper @Inject constructor(
 
         return when (dto.type) {
             "normal" -> Normal(params, requireNotNull(loadSprite(dto)))
-            "bucket" -> Bucket(params, requireNotNull(loadSprite(dto)), requireNotNull(dto.actionKey))
+            "usable" -> Usable(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))
             "pickaxe" -> Pickaxe(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier, requireNotNull(dto.toolLevel))
@@ -38,7 +39,6 @@ class ItemMapper @Inject constructor(
 
     private fun mapCommonParams(key: String, dto: ItemDto): CommonItemParams {
         return CommonItemParams(
-            id = dto.id,
             key = key,
             name = dto.name,
             inHandSpriteOrigin = SpriteOrigin(
@@ -46,6 +46,8 @@ class ItemMapper @Inject constructor(
                 y = dto.origin_y,
             ),
             maxStack = dto.maxStack,
+            burningTimeMs = dto.burningTime,
+            smeltProductKey = dto.smeltProduct,
         )
     }
 
@@ -56,7 +58,12 @@ class ItemMapper @Inject constructor(
 
         val texture = Assets.resolveItemTexture(assetLoader, dto.texture)
         return Sprite(texture)
-            .apply { flip(false, true) }
+            .apply {
+                flip(false, true)
+                dto.tint?.let {
+                    color = colorFromHexString(it)
+                }
+            }
     }
 
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/container/Chest.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/container/Chest.kt
new file mode 100644 (file)
index 0000000..be820bc
--- /dev/null
@@ -0,0 +1,14 @@
+package ru.deadsoftware.cavedroid.game.objects.container
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+
+class Chest(gameItemsHolder: GameItemsHolder) : Container(SIZE, gameItemsHolder) {
+
+    override fun update(gameItemsHolder: GameItemsHolder) {
+        // no-op
+    }
+
+    companion object {
+        private const val SIZE = 27
+    }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/container/Container.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/container/Container.kt
new file mode 100644 (file)
index 0000000..1e13c64
--- /dev/null
@@ -0,0 +1,24 @@
+package ru.deadsoftware.cavedroid.game.objects.container
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import java.io.Serializable
+import javax.annotation.OverridingMethodsMustInvokeSuper
+
+abstract class Container(
+    val size: Int,
+    gameItemsHolder: GameItemsHolder
+) : Serializable {
+
+    private val _items = Array(size) { gameItemsHolder.fallbackItem.toInventoryItem() }
+
+    val items get() = _items.asList() as MutableList<InventoryItem>
+
+    @OverridingMethodsMustInvokeSuper
+    open fun initItems(gameItemsHolder: GameItemsHolder) {
+        _items.forEach { it.init(gameItemsHolder) }
+    }
+
+    abstract fun update(gameItemsHolder: GameItemsHolder)
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/container/ContainerController.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/container/ContainerController.kt
new file mode 100644 (file)
index 0000000..3facb6f
--- /dev/null
@@ -0,0 +1,86 @@
+package ru.deadsoftware.cavedroid.game.objects.container
+
+import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem.Companion.isNoneOrNull
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+import ru.deadsoftware.cavedroid.misc.utils.px
+import java.io.Serializable
+import javax.inject.Inject
+
+@GameScope
+class ContainerController @Inject constructor(
+    _dropController: DropController,
+    _gameItemsHolder: GameItemsHolder
+) : Serializable {
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    @Transient
+    private lateinit var dropController: DropController
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    @Transient
+    private lateinit var gameItemsHolder: GameItemsHolder
+
+    private val containerMap = mutableMapOf<String, Container>()
+
+    val size get() = containerMap.size
+
+    init {
+        dropController = _dropController
+        gameItemsHolder = _gameItemsHolder
+    }
+
+    fun init(dropController: DropController, gameItemsHolder: GameItemsHolder) {
+        this.dropController = dropController
+        this.gameItemsHolder = gameItemsHolder
+        containerMap.forEach { (_, container) -> container.initItems(gameItemsHolder) }
+    }
+
+    fun getContainer(x: Int, y: Int, z: Int): Container? {
+        return containerMap["$x;$y;$z"]
+    }
+
+    fun addContainer(x: Int, y: Int, z: Int, clazz: Class<out Block.Container>) {
+        val container = when (clazz) {
+            Block.Furnace::class.java -> Furnace(gameItemsHolder)
+            Block.Chest::class.java -> Chest(gameItemsHolder)
+            else -> {
+                Gdx.app.error(TAG, "Unknown container class $clazz")
+                return
+            }
+        }
+        containerMap["$x;$y;$z"] = container
+    }
+
+    @JvmOverloads
+    fun destroyContainer(x: Int, y: Int, z: Int, dropItems: Boolean = true) {
+        val container = containerMap.remove("$x;$y;$z") ?: return
+
+        if (!dropItems) {
+            return
+        }
+
+        val xPx = (x + .5f).px
+        val yPx = (y + .5f).px
+
+        container.items.forEach { item ->
+            if (!item.isNoneOrNull()) {
+                dropController.addDrop(xPx, yPx, item)
+            }
+        }
+    }
+
+    fun update() {
+        containerMap.forEach { (_, container) ->
+            container.update(gameItemsHolder)
+        }
+    }
+
+    companion object {
+        private const val TAG = "ContainerController"
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/container/Furnace.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/container/Furnace.kt
new file mode 100644 (file)
index 0000000..1a75e89
--- /dev/null
@@ -0,0 +1,146 @@
+package ru.deadsoftware.cavedroid.game.objects.container
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.math.MathUtils
+import com.badlogic.gdx.utils.TimeUtils
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem.Companion.isNoneOrNull
+import ru.deadsoftware.cavedroid.game.model.item.Item
+
+class Furnace(gameItemsHolder: GameItemsHolder) : Container(SIZE, gameItemsHolder) {
+    
+    var fuel: InventoryItem
+        get() = items[FUEL_INDEX]
+        set(value) {
+            items[FUEL_INDEX] = value
+        }
+
+    var input: InventoryItem
+        get() = items[INPUT_INDEX]
+        set(value) {
+            items[INPUT_INDEX] = value
+        }
+
+    var result: InventoryItem
+        get() = items[RESULT_INDEX]
+        set(value) {
+            items[RESULT_INDEX] = value
+        }
+
+    val isActive: Boolean get() = currentFuel != null
+
+    @Transient
+    var currentFuel: Item? = null
+        set(value) {
+            currentFuelKey = value?.params?.key
+            field = value
+        }
+
+    var currentFuelKey: String? = null
+
+    private var startBurnTimeMs = 0L
+    private var smeltStarTimeMs = 0L
+
+    var burnProgress = 0f
+        private set(value) {
+            field = MathUtils.clamp(value, 0f, 1f)
+        }
+    var smeltProgress = 0f
+        private set(value) {
+            field = MathUtils.clamp(value, 0f, 1f)
+        }
+
+    fun init(gameItemsHolder: GameItemsHolder) {
+        currentFuel = currentFuelKey?.let { gameItemsHolder.getItem(it) }
+        items.forEach { it.init(gameItemsHolder) }
+    }
+
+    fun canSmelt(): Boolean {
+        return (result.isNoneOrNull() || (result.item.params.key == input.item.params.smeltProductKey) )&&
+                !input.isNoneOrNull() && input.item.params.smeltProductKey != null &&
+                (!fuel.isNoneOrNull() || burnProgress > 0f)
+    }
+
+    private fun startBurning(gameItemsHolder: GameItemsHolder) {
+        requireNotNull(fuel.item.params.burningTimeMs) { "Cant start burning without fuel" }
+        currentFuel = fuel.item
+        fuel.subtract()
+        if (fuel.amount <= 0) {
+            fuel = gameItemsHolder.fallbackItem.toInventoryItem()
+        }
+        startBurnTimeMs = TimeUtils.millis()
+        burnProgress = 0f
+    }
+
+    override fun update(gameItemsHolder: GameItemsHolder) {
+        if (currentFuel?.isNone() == true) {
+            currentFuel = null
+        }
+
+        currentFuel?.let { curFuel ->
+            val burningTimeMs = curFuel.params.burningTimeMs ?: run {
+                Gdx.app.error(TAG, "Burning item has no burning time. Item : ${curFuel.params.key}")
+                return
+            }
+
+            if (TimeUtils.timeSinceMillis(startBurnTimeMs).toDouble() / burningTimeMs >= 0.01) {
+                burnProgress += 0.01f
+                startBurnTimeMs = TimeUtils.millis()
+            }
+
+        }
+
+        if (currentFuel?.isNone() == false && burnProgress >= 1f) {
+            if (canSmelt()) {
+                startBurning(gameItemsHolder)
+            } else {
+                currentFuel = null
+                burnProgress = 0f
+                smeltProgress = 0f
+            }
+        }
+
+        if (!canSmelt()) {
+            return
+        }
+        if (currentFuel == null && !fuel.isNoneOrNull()) {
+            startBurning(gameItemsHolder)
+            smeltStarTimeMs = startBurnTimeMs
+            smeltProgress = 0f
+        }
+
+        if ((TimeUtils.timeSinceMillis(smeltStarTimeMs).toDouble() / SMELTING_TIME_MS) >= 0.01) {
+            smeltProgress += 0.01f
+            smeltStarTimeMs = TimeUtils.millis()
+        }
+
+        if (isActive && smeltProgress >= 1f) {
+            val productKey = requireNotNull(input.item.params.smeltProductKey)
+            val res = gameItemsHolder.getItem(productKey)
+            if (result.isNoneOrNull())  {
+                result = res.toInventoryItem()
+            } else {
+                result.add()
+            }
+            input.subtract()
+            if (input.amount <= 0) {
+                input = gameItemsHolder.fallbackItem.toInventoryItem()
+            }
+            smeltStarTimeMs = TimeUtils.millis()
+            smeltProgress = 0f
+        }
+    }
+
+    companion object {
+        private const val SIZE = 3
+        private const val TAG = "Furnace"
+
+        const val FUEL_INDEX = 0
+        const val INPUT_INDEX = 1
+        const val RESULT_INDEX = 2
+
+        const val SMELTING_TIME_MS = 10000L
+    }
+
+}
\ No newline at end of file
similarity index 72%
rename from core/src/ru/deadsoftware/cavedroid/game/objects/Drop.kt
rename to core/src/ru/deadsoftware/cavedroid/game/objects/drop/Drop.kt
index 151e48609fbc98a1c4bd95671b4530790f9abc9a..edb3c81ff657a63fd41b865e4dbaea9a7dc01230 100644 (file)
@@ -1,4 +1,4 @@
-package ru.deadsoftware.cavedroid.game.objects
+package ru.deadsoftware.cavedroid.game.objects.drop
 
 import com.badlogic.gdx.math.Intersector
 import com.badlogic.gdx.math.Rectangle
@@ -6,16 +6,21 @@ import com.badlogic.gdx.math.Vector2
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.model.item.Item
 
-class Drop(
+class Drop @JvmOverloads constructor(
     x: Float,
     y: Float,
     _item: Item,
+    _amount: Int = 1,
 ) : Rectangle(x, y, DROP_SIZE, DROP_SIZE) {
 
+    var amount: Int = _amount
+        private set
+
     val itemKey = _item.params.key
     val velocity = getInitialVelocity()
     var pickedUp = false
 
+    @Suppress("UNNECESSARY_LATEINIT")
     @Transient
     lateinit var item: Item
         private set
@@ -37,6 +42,15 @@ class Drop(
         return Intersector.overlaps(magnetArea, rectangle)
     }
 
+    @JvmOverloads
+    fun subtract(count: Int = 1) {
+        if (count < 0) {
+            throw IllegalArgumentException("Can't subtract negative amount")
+        }
+
+        amount -= count
+    }
+
     private fun getMagnetArea(): Rectangle {
         return Rectangle(
             /* x = */ x - MAGNET_DISTANCE,
@@ -47,9 +61,9 @@ class Drop(
     }
 
     companion object {
-        private const val MAGNET_DISTANCE = 16f
+        private const val MAGNET_DISTANCE = 8f
 
-        const val MAGNET_VELOCITY = 128f
+        const val MAGNET_VELOCITY = 256f
         const val DROP_SIZE = 8f
 
         private fun getInitialVelocity(): Vector2 = Vector2(0f, -1f)
similarity index 68%
rename from core/src/ru/deadsoftware/cavedroid/game/objects/DropController.java
rename to core/src/ru/deadsoftware/cavedroid/game/objects/drop/DropController.java
index 798a84ce5b77415bbf88f87187c39d51e636ff04..6666fc8f64b7edcc75fddf22ea701403b7ea5b5f 100644 (file)
@@ -1,7 +1,9 @@
-package ru.deadsoftware.cavedroid.game.objects;
+package ru.deadsoftware.cavedroid.game.objects.drop;
 
+import org.jetbrains.annotations.NotNull;
 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.GameScope;
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
 import ru.deadsoftware.cavedroid.game.model.item.Item;
 
 import javax.inject.Inject;
@@ -27,10 +29,18 @@ public class DropController implements Serializable {
     }
 
     public void addDrop(float x, float y, Item item) {
+        addDrop(x, y, item, 1);
+    }
+
+    public void addDrop(float x, float y, Item item, int count) {
         if (item.isNone()) {
             return;
         }
-        mDrops.add(new Drop(x, y, item));
+        mDrops.add(new Drop(x, y, item, count));
+    }
+
+    public void addDrop(float x, float y, @NotNull InventoryItem invItem) {
+        addDrop(x, y, invItem.getItem(), invItem.getAmount());
     }
 
     public int getSize() {
index 378ed7b9191c048884db9d80fdc953e7273cb155..68af67b1242e23b979cd3e9b37cd445b8044ac80 100644 (file)
@@ -2,7 +2,6 @@ package ru.deadsoftware.cavedroid.game.render
 
 import com.badlogic.gdx.graphics.g2d.Sprite
 import com.badlogic.gdx.graphics.g2d.SpriteBatch
-import com.badlogic.gdx.graphics.g2d.TextureRegion
 import com.badlogic.gdx.graphics.glutils.ShapeRenderer
 import com.badlogic.gdx.math.MathUtils
 import com.badlogic.gdx.math.Rectangle
@@ -87,7 +86,11 @@ abstract class BlocksRenderer(
         if (foregroundBlock.canSeeThrough && !backgroundBlock.isNone()) {
             val drawX = x.px - viewport.x
             val drawY = y.px - viewport.y
-            backgroundBlock.draw(spriteBatch, drawX, drawY)
+            if (backgroundBlock is Block.Furnace) {
+                backgroundBlock.draw(spriteBatch, drawX, drawY, gameWorld.getBackgroundFurnace(x, y)?.isActive ?: false)
+            } else {
+                backgroundBlock.draw(spriteBatch, drawX, drawY)
+            }
         }
     }
 
@@ -97,7 +100,12 @@ abstract class BlocksRenderer(
         if (!foregroundBlock.isNone() && foregroundBlock.params.isBackground == background) {
             val drawX = x.px - viewport.x
             val drawY = y.px - viewport.y
-            foregroundBlock.draw(spriteBatch, drawX, drawY)
+
+            if (foregroundBlock is Block.Furnace) {
+                foregroundBlock.draw(spriteBatch, drawX, drawY, gameWorld.getForegroundFurnace(x, y)?.isActive ?: false)
+            } else {
+                foregroundBlock.draw(spriteBatch, drawX, drawY)
+            }
         }
     }
 
index a17bd613002ddb1922151bb6d3df31b92f92c969..f14d36b943a7389d546824d48f7e786d320fe4fc 100644 (file)
@@ -4,7 +4,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
 import com.badlogic.gdx.graphics.glutils.ShapeRenderer
 import com.badlogic.gdx.math.Rectangle
 import ru.deadsoftware.cavedroid.game.GameScope
-import ru.deadsoftware.cavedroid.game.objects.DropController
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
 import ru.deadsoftware.cavedroid.game.world.GameWorld
 import ru.deadsoftware.cavedroid.misc.utils.cycledInsideWorld
 import ru.deadsoftware.cavedroid.misc.utils.drawSprite
index b3f89909e4620db19e5b177cbe132319bad0bc96..a2570e25f3120f8208233038f3b85e4738409dc4 100644 (file)
@@ -5,9 +5,12 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer
 import com.badlogic.gdx.math.Rectangle
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.mobs.Player.ControlMode
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
+import ru.deadsoftware.cavedroid.game.mobs.player.Player.ControlMode
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager
 import ru.deadsoftware.cavedroid.game.world.GameWorld
 import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.utils.drawString
 import ru.deadsoftware.cavedroid.misc.utils.px
 import javax.inject.Inject
 
@@ -15,6 +18,7 @@ import javax.inject.Inject
 class HudRenderer @Inject constructor(
     private val gameWorld: GameWorld,
     private val mobsController: MobsController,
+    private val tooltipManager: TooltipManager,
 ) : IGameRenderer {
 
     override val renderLayer = RENDER_LAYER
@@ -23,6 +27,7 @@ class HudRenderer @Inject constructor(
     private val hotbarTexture get() = requireNotNull(Assets.textureRegions[HOTBAR_KEY])
     private val hotbarSelectorTexture get() = requireNotNull(Assets.textureRegions[HOTBAR_SELECTOR_KEY])
     private val wholeHeartTexture get() = requireNotNull(Assets.textureRegions[WHOLE_HEART_KEY])
+    private val emptyHeartTexture get() = requireNotNull(Assets.textureRegions[EMPTY_HEART_KEY])
     private val halfHeartTexture get() = requireNotNull(Assets.textureRegions[HALF_HEART_KEY])
 
     private fun drawCursor(spriteBatch: SpriteBatch, viewport: Rectangle) {
@@ -45,19 +50,29 @@ class HudRenderer @Inject constructor(
         }
 
         val wholeHeart = wholeHeartTexture
+        val halfHeart = halfHeartTexture
+        val emptyHeart = emptyHeartTexture
+
+        val totalHearts = Player.MAX_HEALTH / 2
         val wholeHearts = player.health / 2
 
-        for (i in 0..<wholeHearts) {
-            spriteBatch.draw(wholeHeart, x + i * wholeHeart.regionWidth, y)
+        for (i in 0..< totalHearts) {
+            if (i < wholeHearts) {
+                spriteBatch.draw(wholeHeart, x + i * wholeHeart.regionWidth, y)
+            } else if (i == wholeHearts && player.health % 2 == 1) {
+                spriteBatch.draw(halfHeart, x + i * wholeHeart.regionWidth, y)
+            } else {
+                spriteBatch.draw(emptyHeart, x + i * wholeHeart.regionWidth, y)
+            }
         }
 
-        if (player.health % 2 == 1) {
-            spriteBatch.draw(halfHeartTexture, x + wholeHearts * wholeHeart.regionWidth, y)
-        }
+
+
+
     }
 
     private fun drawHotbarItems(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer,  hotbarX: Float) {
-        mobsController.player.inventory.asSequence().take(HotbarConfig.hotbarCells)
+        mobsController.player.inventory.items.asSequence().take(HotbarConfig.hotbarCells)
             .forEachIndexed { index, item ->
                 if (item.item.isNone()) {
                     return@forEachIndexed
@@ -77,7 +92,7 @@ class HudRenderer @Inject constructor(
         spriteBatch.draw(
             /* region = */ hotbarSelectorTexture,
             /* x = */ hotbarX - HotbarSelectorConfig.horizontalPadding
-                    + mobsController.player.slot * (HotbarConfig.itemSeparatorWidth + HotbarConfig.itemSlotSpace),
+                    + mobsController.player.inventory.activeSlot * (HotbarConfig.itemSeparatorWidth + HotbarConfig.itemSlotSpace),
             /* y = */ -HotbarSelectorConfig.verticalPadding
         )
     }
@@ -90,6 +105,15 @@ class HudRenderer @Inject constructor(
         drawHealth(spriteBatch, hotbarX, hotbarTexture.regionHeight.toFloat())
         drawHotbarSelector(spriteBatch, hotbarX)
         drawHotbarItems(spriteBatch, shapeRenderer, hotbarX)
+
+        val tooltip = tooltipManager.currentHotbarTooltip
+        if (tooltip.isNotBlank()) {
+            spriteBatch.drawString(
+                str = tooltip,
+                x = viewport.width / 2 - Assets.getStringWidth(tooltip) / 2,
+                y = hotbarTexture.regionHeight.toFloat()
+            )
+        }
     }
 
     override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
@@ -105,6 +129,7 @@ class HudRenderer @Inject constructor(
         private const val HOTBAR_SELECTOR_KEY = "hotbar_selector"
         private const val WHOLE_HEART_KEY = "heart_whole"
         private const val HALF_HEART_KEY = "heart_half"
+        private const val EMPTY_HEART_KEY = "heart_empty"
 
         private data object HotbarConfig {
             const val horizontalMargin = 3f
index 5c95d696c675cadba52dcaf7fd1d8186e9a79491..63aa09fd74fce303e6d45b7d976ee6edab53c8ce 100644 (file)
@@ -6,12 +6,14 @@ 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.input.Joystick
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.mobs.Player.ControlMode
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.mobs.player.Player.ControlMode
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import ru.deadsoftware.cavedroid.misc.Assets
 import ru.deadsoftware.cavedroid.misc.utils.ArrayMapExtensions.component1
 import ru.deadsoftware.cavedroid.misc.utils.ArrayMapExtensions.component2
+import ru.deadsoftware.cavedroid.misc.utils.drawSprite
 import javax.inject.Inject
 
 @GameScope
@@ -25,6 +27,26 @@ class TouchControlsRenderer @Inject constructor(
 
     private val shadeTexture get() = Assets.textureRegions[SHADE_KEY]
 
+    private fun drawJoystick(spriteBatch: SpriteBatch) {
+        val joystick = mainConfig.joystick?.takeIf { it.active } ?: return
+
+        spriteBatch.drawSprite(
+            sprite = Assets.joyBackground,
+            x = joystick.centerX - Joystick.RADIUS,
+            y = joystick.centerY - Joystick.RADIUS,
+            width = Joystick.SIZE,
+            height = Joystick.SIZE
+        )
+
+        spriteBatch.drawSprite(
+            sprite = Assets.joyStick,
+            x = joystick.activeX - Joystick.STICK_SIZE / 2,
+            y = joystick.activeY - Joystick.STICK_SIZE / 2,
+            width = Joystick.STICK_SIZE,
+            height = Joystick.STICK_SIZE
+        )
+    }
+
     override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
         if (!mainConfig.isTouch || gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE) {
             return
@@ -48,6 +70,8 @@ class TouchControlsRenderer @Inject constructor(
             val altKeyRect = touchControlsMap.get("alt").rect
             spriteBatch.draw(shadeTexture, altKeyRect.x, altKeyRect.y, altKeyRect.width, altKeyRect.height)
         }
+
+        drawJoystick(spriteBatch)
     }
 
     companion object {
index 2fa093f61719d8564637e5ce4e456b2e7153b201..fa37f8dc3d71798056ae72378bcca3e49991cb35 100644 (file)
@@ -6,10 +6,8 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer
 import com.badlogic.gdx.math.Rectangle
 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 ru.deadsoftware.cavedroid.game.render.windows.*
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import javax.inject.Inject
 
 @GameScope
@@ -18,6 +16,8 @@ class WindowsRenderer @Inject constructor(
     private val survivalWindowRenderer: SurvivalWindowRenderer,
     private val craftingWindowRenderer: CraftingWindowRenderer,
     private val gameWindowsManager: GameWindowsManager,
+    private val furnaceWindowRenderer: FurnaceWindowRenderer,
+    private val chestWindowRenderer: ChestWindowRenderer,
 ) : IGameRenderer {
 
     override val renderLayer get() = RENDER_LAYER
@@ -27,6 +27,8 @@ class WindowsRenderer @Inject constructor(
             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.FURNACE -> furnaceWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta)
+            GameUiWindow.CHEST -> chestWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta)
             GameUiWindow.NONE -> return
             else -> Gdx.app.error(TAG, "Cannot draw window: ${windowType.name}")
         }
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/windows/ChestWindowRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/windows/ChestWindowRenderer.kt
new file mode 100644 (file)
index 0000000..78178bc
--- /dev/null
@@ -0,0 +1,93 @@
+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.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
+import ru.deadsoftware.cavedroid.game.render.IGameRenderer
+import ru.deadsoftware.cavedroid.game.render.WindowsRenderer
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.ChestInventoryWindow
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.SurvivalInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+import kotlin.math.atan
+
+@GameScope
+class ChestWindowRenderer @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 chestWindowTexture get() = requireNotNull(Assets.textureRegions[CHEST_WINDOW_KEY])
+    
+    
+    override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+        val windowTexture = chestWindowTexture
+        val window = gameWindowsManager.currentWindow as ChestInventoryWindow
+
+        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.Chest.contentsMarginLeft,
+            gridY = windowY + GameWindowsConfigs.Chest.contentsMarginTop,
+            items = window.chest.items,
+            itemsInRow = GameWindowsConfigs.Chest.itemsInRow,
+            cellWidth = GameWindowsConfigs.Chest.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Chest.itemsGridRowHeight,
+        )
+        
+        drawItemsGrid(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            gridX = windowX + GameWindowsConfigs.Chest.itemsGridMarginLeft,
+            gridY = windowY + GameWindowsConfigs.Chest.itemsGridMarginTop,
+            items = mobsController.player.inventory.items.asSequence()
+                .drop(GameWindowsConfigs.Chest.hotbarCells)
+                .take(GameWindowsConfigs.Chest.itemsInCol * GameWindowsConfigs.Chest.itemsInRow)
+                .asIterable(),
+            itemsInRow = GameWindowsConfigs.Chest.itemsInRow,
+            cellWidth = GameWindowsConfigs.Chest.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Chest.itemsGridRowHeight,
+        )
+
+        drawItemsGrid(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            gridX = windowX + GameWindowsConfigs.Chest.itemsGridMarginLeft,
+            gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Chest.hotbarOffsetFromBottom,
+            items = mobsController.player.inventory.items.asSequence()
+                .take(GameWindowsConfigs.Chest.hotbarCells)
+                .asIterable(),
+            itemsInRow = GameWindowsConfigs.Chest.hotbarCells,
+            cellWidth = GameWindowsConfigs.Chest.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Chest.itemsGridRowHeight,
+        )
+
+        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 CHEST_WINDOW_KEY = "chest"
+    }
+}
\ No newline at end of file
index 3b08ed7b4fcd712bc60ac1e2fbedc6d0683b06d7..6772282b15b18968ef3d0d82a27d1f1f8fb3651e 100644 (file)
@@ -10,9 +10,9 @@ 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.game.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.CraftingInventoryWindow
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 
@@ -42,7 +42,7 @@ class CraftingWindowRenderer @Inject constructor(
             shapeRenderer = shapeRenderer,
             gridX = windowX + GameWindowsConfigs.Crafting.itemsGridMarginLeft,
             gridY = windowY + GameWindowsConfigs.Crafting.itemsGridMarginTop,
-            items = mobsController.player.inventory.asSequence()
+            items = mobsController.player.inventory.items.asSequence()
                 .drop(GameWindowsConfigs.Crafting.hotbarCells)
                 .take(GameWindowsConfigs.Crafting.itemsInCol * GameWindowsConfigs.Crafting.itemsInRow)
                 .asIterable(),
@@ -56,7 +56,7 @@ class CraftingWindowRenderer @Inject constructor(
             shapeRenderer = shapeRenderer,
             gridX = windowX + GameWindowsConfigs.Crafting.itemsGridMarginLeft,
             gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Crafting.hotbarOffsetFromBottom,
-            items = mobsController.player.inventory.asSequence()
+            items = mobsController.player.inventory.items.asSequence()
                 .take(GameWindowsConfigs.Crafting.hotbarCells)
                 .asIterable(),
             itemsInRow = GameWindowsConfigs.Crafting.hotbarCells,
index 4c43600959ee866972f56b68c909c011c0c6b0f4..6bfac428fc32d3c76ab8b33cf9845306e3057138 100644 (file)
@@ -6,11 +6,11 @@ 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.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 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.ui.windows.GameWindowsConfigs
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 import kotlin.math.min
@@ -69,7 +69,7 @@ class CreativeWindowRenderer @Inject constructor(
             shapeRenderer = shapeRenderer,
             gridX = windowX + GameWindowsConfigs.Creative.itemsGridMarginLeft,
             gridY = windowY + creativeWindow.regionHeight - GameWindowsConfigs.Creative.playerInventoryOffsetFromBottom,
-            items = mobsController.player.inventory.asSequence().take(GameWindowsConfigs.Creative.invItems).asIterable(),
+            items = mobsController.player.inventory.items.asSequence().take(GameWindowsConfigs.Creative.invItems).asIterable(),
             itemsInRow = GameWindowsConfigs.Creative.invItems,
             cellWidth = GameWindowsConfigs.Creative.itemsGridColWidth,
             cellHeight = GameWindowsConfigs.Creative.itemsGridRowHeight,
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/windows/FurnaceWindowRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/windows/FurnaceWindowRenderer.kt
new file mode 100644 (file)
index 0000000..3037009
--- /dev/null
@@ -0,0 +1,137 @@
+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.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.FurnaceInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.utils.drawSprite
+import ru.deadsoftware.cavedroid.misc.utils.withScissors
+import javax.inject.Inject
+
+@GameScope
+class FurnaceWindowRenderer @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 furnaceWindowTexture get() = requireNotNull(Assets.textureRegions[FURNACE_WINDOW_KEY])
+
+    
+    override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+        val windowTexture = furnaceWindowTexture
+        
+        val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow
+
+        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.Furnace.itemsGridMarginLeft,
+            gridY = windowY + GameWindowsConfigs.Furnace.itemsGridMarginTop,
+            items = mobsController.player.inventory.items.asSequence()
+                .drop(GameWindowsConfigs.Furnace.hotbarCells)
+                .take(GameWindowsConfigs.Furnace.itemsInCol * GameWindowsConfigs.Furnace.itemsInRow)
+                .asIterable(),
+            itemsInRow = GameWindowsConfigs.Furnace.itemsInRow,
+            cellWidth = GameWindowsConfigs.Furnace.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Furnace.itemsGridRowHeight,
+        )
+
+        drawItemsGrid(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            gridX = windowX + GameWindowsConfigs.Furnace.itemsGridMarginLeft,
+            gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Furnace.hotbarOffsetFromBottom,
+            items = mobsController.player.inventory.items.asSequence()
+                .take(GameWindowsConfigs.Furnace.hotbarCells)
+                .asIterable(),
+            itemsInRow = GameWindowsConfigs.Furnace.hotbarCells,
+            cellWidth = GameWindowsConfigs.Furnace.itemsGridColWidth,
+            cellHeight = GameWindowsConfigs.Furnace.itemsGridRowHeight,
+        )
+
+        window.furnace.fuel?.draw(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            x = windowX + GameWindowsConfigs.Furnace.smeltFuelMarginLeft,
+            y = windowY + GameWindowsConfigs.Furnace.smeltFuelMarginTop
+        )
+
+        window.furnace.input?.draw(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            x = windowX + GameWindowsConfigs.Furnace.smeltInputMarginLeft,
+            y = windowY + GameWindowsConfigs.Furnace.smeltInputMarginTop
+        )
+
+        window.furnace.result?.draw(
+            spriteBatch = spriteBatch,
+            shapeRenderer = shapeRenderer,
+            x = windowX + GameWindowsConfigs.Furnace.smeltResultOffsetX,
+            y = windowY + GameWindowsConfigs.Furnace.smeltResultOffsetY
+        )
+
+        if (window.furnace.isActive) {
+            val burn = GameWindowsConfigs.Furnace.fuelBurnHeight * window.furnace.burnProgress
+
+            spriteBatch.withScissors(
+                mainConfig = mainConfig,
+                x = windowX + GameWindowsConfigs.Furnace.fuelBurnMarginLeft,
+                y = windowY + GameWindowsConfigs.Furnace.fuelBurnMarginTop + burn,
+                width = Assets.furnaceBurn.width,
+                height = GameWindowsConfigs.Furnace.fuelBurnHeight,
+            ) {
+                spriteBatch.drawSprite(
+                    sprite = Assets.furnaceBurn,
+                    x = windowX + GameWindowsConfigs.Furnace.fuelBurnMarginLeft,
+                    y = windowY + GameWindowsConfigs.Furnace.fuelBurnMarginTop
+                )
+            }
+
+            if (window.furnace.canSmelt()) {
+                val progress = GameWindowsConfigs.Furnace.progressWidth * window.furnace.smeltProgress
+
+                spriteBatch.withScissors(
+                    mainConfig = mainConfig,
+                    x = windowX + GameWindowsConfigs.Furnace.progressMarginLeft,
+                    y = windowY + GameWindowsConfigs.Furnace.progressMarginTop,
+                    width = progress,
+                    height = Assets.furnaceProgress.height
+                ) {
+                    spriteBatch.drawSprite(
+                        sprite = Assets.furnaceProgress,
+                        x = windowX + GameWindowsConfigs.Furnace.progressMarginLeft,
+                        y = windowY + GameWindowsConfigs.Furnace.progressMarginTop,
+                    )
+                }
+            }
+        }
+
+        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 FURNACE_WINDOW_KEY = "furnace"
+    }
+}
\ No newline at end of file
index 56df2cac3ad173f16bc9eeafc2808dbc0f69ce19..7ee3b27087eb76bd0b75f2549786b54b5eac241c 100644 (file)
@@ -12,9 +12,9 @@ import ru.deadsoftware.cavedroid.game.mobs.Mob
 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.SurvivalInventoryWindow
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.SurvivalInventoryWindow
 import ru.deadsoftware.cavedroid.misc.Assets
 import javax.inject.Inject
 import kotlin.math.atan
@@ -78,7 +78,7 @@ class SurvivalWindowRenderer @Inject constructor(
             shapeRenderer = shapeRenderer,
             gridX = windowX + GameWindowsConfigs.Survival.itemsGridMarginLeft,
             gridY = windowY + GameWindowsConfigs.Survival.itemsGridMarginTop,
-            items = mobsController.player.inventory.asSequence()
+            items = mobsController.player.inventory.items.asSequence()
                 .drop(GameWindowsConfigs.Survival.hotbarCells)
                 .take(GameWindowsConfigs.Survival.itemsInCol * GameWindowsConfigs.Survival.itemsInRow)
                 .asIterable(),
@@ -92,7 +92,7 @@ class SurvivalWindowRenderer @Inject constructor(
             shapeRenderer = shapeRenderer,
             gridX = windowX + GameWindowsConfigs.Survival.itemsGridMarginLeft,
             gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Survival.hotbarOffsetFromBottom,
-            items = mobsController.player.inventory.asSequence()
+            items = mobsController.player.inventory.items.asSequence()
                 .take(GameWindowsConfigs.Survival.hotbarCells)
                 .asIterable(),
             itemsInRow = GameWindowsConfigs.Survival.hotbarCells,
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/TooltipManager.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/TooltipManager.kt
new file mode 100644 (file)
index 0000000..a8f5a9b
--- /dev/null
@@ -0,0 +1,41 @@
+package ru.deadsoftware.cavedroid.game.ui
+
+import com.badlogic.gdx.utils.Timer
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import javax.inject.Inject
+
+@GameScope
+class TooltipManager @Inject constructor(
+    private val mainConfig: MainConfig
+) {
+
+    private val resetTask = object : Timer.Task() {
+        override fun run() {
+            currentHotbarTooltip = ""
+        }
+    }
+
+    var currentHotbarTooltip: String = ""
+        private set
+
+    var currentMouseTooltip: String = ""
+        private set
+
+    fun showHotbarTooltip(tooltip: String) {
+        currentHotbarTooltip = tooltip
+        if (resetTask.isScheduled) {
+            resetTask.cancel()
+        }
+        Timer.schedule(resetTask, TOOLTIP_TIME_S)
+    }
+
+    fun showMouseTooltip(tooltip: String) {
+        currentMouseTooltip = tooltip
+    }
+
+    companion object {
+        private const val TOOLTIP_TIME_S = 2f
+    }
+
+}
\ No newline at end of file
similarity index 55%
rename from core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsConfigs.kt
rename to core/src/ru/deadsoftware/cavedroid/game/ui/windows/GameWindowsConfigs.kt
index a6cff63b08c049c3dc646c11ffdee28593ff05fc..2f1d2645aac11d7bd84b33e76eaf784b7025de60 100644 (file)
@@ -1,4 +1,4 @@
-package ru.deadsoftware.cavedroid.game.windows
+package ru.deadsoftware.cavedroid.game.ui.windows
 
 object GameWindowsConfigs {
     data object Creative {
@@ -67,7 +67,58 @@ object GameWindowsConfigs {
         const val craftOffsetX = 30f
         const val craftOffsetY = 18f
 
-        const val craftResultOffsetX = 128f
+        const val craftResultOffsetX = 124f
         const val craftResultOffsetY = 36f
     }
+
+    data object Furnace {
+        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 smeltInputMarginLeft = 56f
+        const val smeltInputMarginTop = 18f
+
+        const val smeltFuelMarginLeft = 56f
+        const val smeltFuelMarginTop = 54f
+
+        const val smeltResultOffsetX = 116f
+        const val smeltResultOffsetY = 36f
+
+        const val fuelBurnMarginLeft = 56f
+        const val fuelBurnMarginTop = 36f
+        const val fuelBurnHeight = 14f
+
+        const val progressMarginLeft = 79f
+        const val progressMarginTop = 34f
+        const val progressWidth = 24f
+    }
+
+    data object Chest {
+        const val itemsGridMarginLeft = 8f
+        const val itemsGridMarginTop = 86f
+
+        const val itemsGridRowHeight = 18f
+        const val itemsGridColWidth = 18f
+
+        const val hotbarCells = 9
+        const val hotbarOffsetFromBottom = 24f
+
+        const val itemsInRow = 9
+        const val itemsInCol = 5
+
+        const val contentsMarginLeft = 8f
+        const val contentsMarginTop = 18f
+
+        const val contentsInRow = 9
+        const val contentsInCol = 3
+    }
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/windows/GameWindowsManager.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/windows/GameWindowsManager.kt
new file mode 100644 (file)
index 0000000..8d32f94
--- /dev/null
@@ -0,0 +1,63 @@
+package ru.deadsoftware.cavedroid.game.ui.windows
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.objects.container.Chest
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+import ru.deadsoftware.cavedroid.game.objects.container.Furnace
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager
+import ru.deadsoftware.cavedroid.game.ui.windows.inventory.*
+import javax.inject.Inject
+
+@GameScope
+class GameWindowsManager @Inject constructor(
+    private val tooltipManager: TooltipManager,
+    private val mobsController: MobsController,
+    private val dropController: DropController,
+    private val gameItemsHolder: GameItemsHolder,
+) {
+
+    var creativeScrollAmount = 0
+    var isDragging = false
+
+    var currentWindow: AbstractInventoryWindow? = null
+
+    @JvmName("getCurrentWindowType")
+    fun getCurrentWindow(): GameUiWindow {
+        return currentWindow?.type ?: GameUiWindow.NONE
+    }
+
+    fun openInventory() {
+        if (mobsController.player.gameMode == 1) {
+            currentWindow = CreativeInventoryWindow()
+        } else {
+            currentWindow = SurvivalInventoryWindow(gameItemsHolder)
+        }
+    }
+
+    fun openFurnace(furnace: Furnace) {
+        currentWindow = FurnaceInventoryWindow(furnace)
+    }
+
+    fun openChest(chest: Chest) {
+        currentWindow = ChestInventoryWindow(chest)
+    }
+
+    fun openCrafting() {
+        currentWindow = CraftingInventoryWindow(gameItemsHolder)
+    }
+
+    fun closeWindow() {
+        (currentWindow as? AbstractInventoryWindowWithCraftGrid)?.let { window ->
+            window.craftingItems.forEach { item ->
+                dropController.addDrop(mobsController.player.x, mobsController.player.y, item)
+            }
+        }
+
+        currentWindow = null
+        tooltipManager.showMouseTooltip("")
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/AbstractInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/AbstractInventoryWindow.kt
new file mode 100644 (file)
index 0000000..ed15bac
--- /dev/null
@@ -0,0 +1,81 @@
+package ru.deadsoftware.cavedroid.game.ui.windows.inventory
+
+import com.badlogic.gdx.math.MathUtils
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem.Companion.isNoneOrNull
+
+abstract class AbstractInventoryWindow {
+
+    abstract val type: GameUiWindow
+
+    abstract var selectedItem: InventoryItem?
+
+    var selectItemPointer: Int = -1
+
+    fun onLeftCLick(
+        items: MutableList<InventoryItem>,
+        gameItemsHolder: GameItemsHolder,
+        index: Int,
+        pointer: Int = -1
+    ) {
+        if (selectedItem != null &&
+            selectedItem?.item?.isNone() != true &&
+            pointer >= 0 && selectItemPointer >= 0 &&
+            pointer != selectItemPointer
+        ) {
+            return
+        }
+
+        val clickedItem = items[index]
+
+        selectedItem?.let { selectedItem ->
+            if (!clickedItem.isNoneOrNull() && items[index].item == selectedItem.item &&
+                items[index].amount + selectedItem.amount <= selectedItem.item.params.maxStack
+            ) {
+                items[index].amount += selectedItem.amount
+                this@AbstractInventoryWindow.selectedItem = null
+                selectItemPointer = -1
+                return
+            }
+        }
+
+        val item = items[index]
+        items[index] = selectedItem ?: gameItemsHolder.fallbackItem.toInventoryItem()
+        selectedItem = item
+        selectItemPointer = pointer
+    }
+
+    fun onRightClick(items: MutableList<InventoryItem>, gameItemsHolder: GameItemsHolder, index: Int) {
+        val clickedItem = items[index]
+        val selectedItem = selectedItem
+
+        if (selectedItem.isNoneOrNull() && !clickedItem.isNoneOrNull()) {
+            val half = InventoryItem(clickedItem.item, MathUtils.ceil(clickedItem.amount.toFloat() / 2f))
+            this.selectedItem = half
+            clickedItem.subtract(half.amount)
+            if (clickedItem.amount == 0) {
+                items[index] = gameItemsHolder.fallbackItem.toInventoryItem()
+            }
+            return
+        }
+
+        if (selectedItem == null ||
+            (!clickedItem.isNoneOrNull() && selectedItem.item != clickedItem.item) ||
+            !clickedItem.canBeAdded()) {
+            return
+        }
+
+        val newItem = selectedItem.item.toInventoryItem(
+            (clickedItem.takeIf { !it.item.isNone() }?.amount ?: 0) + 1
+        )
+        items[index] = newItem
+        selectedItem.amount--
+
+        if (selectedItem.amount <= 0) {
+            this@AbstractInventoryWindow.selectedItem = null
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/AbstractInventoryWindowWithCraftGrid.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/AbstractInventoryWindowWithCraftGrid.kt
new file mode 100644 (file)
index 0000000..066ca37
--- /dev/null
@@ -0,0 +1,24 @@
+package ru.deadsoftware.cavedroid.game.ui.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+abstract class AbstractInventoryWindowWithCraftGrid(
+    gameItemsHolder: GameItemsHolder,
+) : AbstractInventoryWindow() {
+
+    private val _items = Array(10) { gameItemsHolder.fallbackItem.toInventoryItem() }
+
+    val items get() = _items.asList()
+
+    val craftingItems get() = items.subList(0, 9) as MutableList<InventoryItem>
+
+    val craftResultList get() = items.subList(9, 10) as MutableList<InventoryItem>
+
+    var craftResult: InventoryItem
+        get() = craftResultList[0]
+        set(value) {
+            craftResultList[0] = value
+        }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/ChestInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/ChestInventoryWindow.kt
new file mode 100644 (file)
index 0000000..6f4a4fe
--- /dev/null
@@ -0,0 +1,13 @@
+package ru.deadsoftware.cavedroid.game.ui.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.objects.container.Chest
+
+class ChestInventoryWindow(val chest: Chest) : AbstractInventoryWindow() {
+
+    override val type = GameUiWindow.CHEST
+
+    override var selectedItem: InventoryItem? = null
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/CraftingInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/CraftingInventoryWindow.kt
new file mode 100644 (file)
index 0000000..cd7c0ff
--- /dev/null
@@ -0,0 +1,14 @@
+package ru.deadsoftware.cavedroid.game.ui.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class CraftingInventoryWindow(
+    gameItemsHolder: GameItemsHolder
+) : AbstractInventoryWindowWithCraftGrid(gameItemsHolder) {
+
+    override val type = GameUiWindow.CRAFTING_TABLE
+
+    override var selectedItem: InventoryItem? = null
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/CreativeInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/CreativeInventoryWindow.kt
new file mode 100644 (file)
index 0000000..a38fd29
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.game.ui.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class CreativeInventoryWindow() : AbstractInventoryWindow() {
+
+    override val type = GameUiWindow.CREATIVE_INVENTORY
+
+    override var selectedItem: InventoryItem? = null
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/FurnaceInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/FurnaceInventoryWindow.kt
new file mode 100644 (file)
index 0000000..42f7038
--- /dev/null
@@ -0,0 +1,15 @@
+package ru.deadsoftware.cavedroid.game.ui.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.objects.container.Furnace
+
+class FurnaceInventoryWindow(
+    val furnace: Furnace,
+) : AbstractInventoryWindow() {
+
+    override val type = GameUiWindow.FURNACE
+
+    override var selectedItem: InventoryItem? = null
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/SurvivalInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/ui/windows/inventory/SurvivalInventoryWindow.kt
new file mode 100644 (file)
index 0000000..ce250fc
--- /dev/null
@@ -0,0 +1,14 @@
+package ru.deadsoftware.cavedroid.game.ui.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class SurvivalInventoryWindow(
+    gameItemsHolder: GameItemsHolder
+) : AbstractInventoryWindowWithCraftGrid(gameItemsHolder) {
+
+    override val type = GameUiWindow.SURVIVAL_INVENTORY
+
+    override var selectedItem: InventoryItem? = null
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsManager.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsManager.kt
deleted file mode 100644 (file)
index b07f1fd..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-package ru.deadsoftware.cavedroid.game.windows
-
-import ru.deadsoftware.cavedroid.MainConfig
-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
-class GameWindowsManager @Inject constructor(
-    private val mainConfig: MainConfig,
-    private val mobsController: MobsController,
-) {
-
-    var creativeScrollAmount = 0
-    var isDragging = false
-
-    var currentWindow: AbstractInventoryWindow? = null
-
-    @JvmName("getCurrentWindowType")
-    fun getCurrentWindow(): GameUiWindow {
-        return currentWindow?.type ?: GameUiWindow.NONE
-    }
-
-    fun openInventory() {
-        if (mobsController.player.gameMode == 1) {
-            currentWindow = CreativeInventoryWindow(GameUiWindow.CREATIVE_INVENTORY)
-        } else {
-            currentWindow = SurvivalInventoryWindow(GameUiWindow.SURVIVAL_INVENTORY)
-        }
-    }
-
-    fun openCrafting() {
-        currentWindow = CraftingInventoryWindow(GameUiWindow.CRAFTING_TABLE)
-    }
-
-    fun closeWindow() {
-        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
deleted file mode 100644 (file)
index 0660604..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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
deleted file mode 100644 (file)
index 03808f1..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-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
deleted file mode 100644 (file)
index 5f72243..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-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
deleted file mode 100644 (file)
index 4eeba3c..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-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
index f2fd27dd486fa4a3425bd5edce078769350deb19..05a7d5f0f6aa14802eb11fc7b05dcd8b96ba95a4 100644 (file)
@@ -1,14 +1,19 @@
 package ru.deadsoftware.cavedroid.game.world;
 
+import com.badlogic.gdx.math.MathUtils;
 import kotlin.Pair;
 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.GameScope;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
+import ru.deadsoftware.cavedroid.game.mobs.Pig;
 import ru.deadsoftware.cavedroid.game.model.block.Block;
 import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
 import ru.deadsoftware.cavedroid.game.model.item.Item;
 import ru.deadsoftware.cavedroid.game.model.world.generator.WorldGeneratorConfig;
-import ru.deadsoftware.cavedroid.game.objects.DropController;
+import ru.deadsoftware.cavedroid.game.objects.container.Container;
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController;
+import ru.deadsoftware.cavedroid.game.objects.container.Furnace;
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController;
 import ru.deadsoftware.cavedroid.misc.utils.MeasureUnitsUtilsKt;
 
 import javax.annotation.CheckForNull;
@@ -17,26 +22,32 @@ import javax.inject.Inject;
 @GameScope
 public class GameWorld {
 
+    private static final int FOREGROUND_Z = 0;
+    private static final int BACKGROUND_Z = 1;
+
     private final DropController mDropController;
     private final MobsController mMobsController;
     private final GameItemsHolder mGameItemsHolder;
+    private final ContainerController mContainerController;
 
     private final int mWidth;
     private final int mHeight;
     private final Block[][] mForeMap;
     private final Block[][] mBackMap;
 
-    private final WorldGeneratorConfig mWorldConfig =  WorldGeneratorConfig.Companion.getDefault();
+    private final WorldGeneratorConfig mWorldConfig = WorldGeneratorConfig.Companion.getDefault();
 
     @Inject
     public GameWorld(DropController dropController,
                      MobsController mobsController,
                      GameItemsHolder gameItemsHolder,
+                     ContainerController containerController,
                      @CheckForNull Block[][] foreMap,
                      @CheckForNull Block[][] backMap) {
         mDropController = dropController;
         mMobsController = mobsController;
         mGameItemsHolder = gameItemsHolder;
+        mContainerController = containerController;
 
         boolean isNewGame = foreMap == null || backMap == null;
 
@@ -46,6 +57,7 @@ public class GameWorld {
             Pair<Block[][], Block[][]> maps = new GameWorldGenerator(mWorldConfig, mGameItemsHolder).generate();
             mForeMap = maps.getFirst();
             mBackMap = maps.getSecond();
+            spawnInitialMobs();
             mMobsController.getPlayer().respawn(this, mGameItemsHolder);
         } else {
             mForeMap = foreMap;
@@ -97,23 +109,43 @@ public class GameWorld {
 
     private Block getMap(int x, int y, int layer) {
         Block map = mGameItemsHolder.getFallbackBlock();
-        try {
-            x = transformX(x);
-            map = (layer == 0) ? mForeMap[x][y] : mBackMap[x][y];
-        } catch (ArrayIndexOutOfBoundsException ignored) {
+
+        if (y < 0 || y >= getHeight()) {
+            return map;
+        }
+
+        x = transformX(x);
+
+        if (x < 0 || x >= getWidth()) {
+            return map;
         }
+
+        map = (layer == 0) ? mForeMap[x][y] : mBackMap[x][y];
+
         return map;
     }
 
     private void setMap(int x, int y, int layer, Block value) {
-        try {
-            x = transformX(x);
-            if (layer == 0) {
-                mForeMap[x][y] = value;
-            } else {
-                mBackMap[x][y] = value;
-            }
-        } catch (ArrayIndexOutOfBoundsException ignored) {
+        if (y < 0 || y >= getHeight()) {
+            return;
+        }
+
+        x = transformX(x);
+
+        if (x < 0 || x >= getWidth()) {
+            return;
+        }
+
+        mContainerController.destroyContainer(x, y, layer, false);
+
+        if (value.isContainer()) {
+            mContainerController.addContainer(x, y, layer, (Class<? extends Block.Container>) value.getClass());
+        }
+
+        if (layer == 0) {
+            mForeMap[x][y] = value;
+        } else {
+            mBackMap[x][y] = value;
         }
     }
 
@@ -127,19 +159,19 @@ public class GameWorld {
     }
 
     public boolean hasForeAt(int x, int y) {
-        return getMap(x, y, 0) != mGameItemsHolder.getFallbackBlock();
+        return getMap(x, y, FOREGROUND_Z) != mGameItemsHolder.getFallbackBlock();
     }
 
     public boolean hasBackAt(int x, int y) {
-        return getMap(x, y, 1) != mGameItemsHolder.getFallbackBlock();
+        return getMap(x, y, BACKGROUND_Z) != mGameItemsHolder.getFallbackBlock();
     }
 
     public Block getForeMap(int x, int y) {
-        return getMap(x, y, 0);
+        return getMap(x, y, FOREGROUND_Z);
     }
 
     public void setForeMap(int x, int y, Block block) {
-        setMap(x, y, 0, block);
+        setMap(x, y, FOREGROUND_Z, block);
     }
 
     public void resetForeMap(int x, int y) {
@@ -147,15 +179,19 @@ public class GameWorld {
     }
 
     public Block getBackMap(int x, int y) {
-        return getMap(x, y, 1);
+        return getMap(x, y, BACKGROUND_Z);
     }
 
     public void setBackMap(int x, int y, Block block) {
-        setMap(x, y, 1, block);
+        setMap(x, y, BACKGROUND_Z, block);
+    }
+
+    public boolean canPlaceToForeground(int x, int y, Block value) {
+        return !hasForeAt(x, y) || value == mGameItemsHolder.getFallbackBlock() || !getForeMap(x, y).hasCollision();
     }
 
     public boolean placeToForeground(int x, int y, Block value) {
-        if (!hasForeAt(x, y) || value == mGameItemsHolder.getFallbackBlock() || !getForeMap(x, y).hasCollision()) {
+        if (canPlaceToForeground(x, y, value)) {
             setForeMap(x, y, value);
             return true;
         } else if (value instanceof Block.Slab && isSameSlab(value, getForeMap(x, y))) {
@@ -167,7 +203,7 @@ public class GameWorld {
 
     public boolean placeToBackground(int x, int y, Block value) {
         if (value == mGameItemsHolder.getFallbackBlock() || (getBackMap(x, y) == mGameItemsHolder.getFallbackBlock() && value.hasCollision()) &&
-                (!value.isTransparent() || value == mGameItemsHolder.getBlock("glass"))) {
+                (!value.isTransparent() || value == mGameItemsHolder.getBlock("glass") || value.isChest() || value.isSlab())) {
             setBackMap(x, y, value);
             return true;
         }
@@ -175,23 +211,41 @@ public class GameWorld {
     }
 
     private void playerDurateTool() {
-        final InventoryItem playerCurrentItem = mMobsController.getPlayer().getCurrentItem();
-        if (mMobsController.getPlayer().getCurrentItem().getItem().isTool()) {
+        final InventoryItem playerCurrentItem = mMobsController.getPlayer().inventory.getActiveItem();
+        if (playerCurrentItem.getItem().isTool()) {
             mMobsController.getPlayer().decreaseCurrentItemCount(mGameItemsHolder);
         }
     }
 
     private boolean shouldDrop(Block block) {
-        final Item item = mMobsController.getPlayer().getCurrentItem().getItem();
-        int toolLevel = item.isTool() ? ((Item.Tool)item).getLevel() : 0;
+        final Item item = mMobsController.getPlayer().inventory.getActiveItem().getItem();
+        int toolLevel = item.isTool() ? ((Item.Tool) item).getLevel() : 0;
         if (item.isTool() && block.getParams().getToolType() != item.getClass()) {
             toolLevel = 0;
         }
         return toolLevel >= block.getParams().getToolLevel();
     }
 
+    private void spawnInitialMobs() {
+        for (int x = 0; x < getWidth(); x++) {
+            int y = 0;
+            while (y < getWorldConfig().getSeaLevel()) {
+                if (getForeMap(x, y) == mGameItemsHolder.getBlock("grass")) {
+                    if (MathUtils.randomBoolean(.125f)) {
+                        mMobsController.addMob(new Pig(MeasureUnitsUtilsKt.getPx(x), MeasureUnitsUtilsKt.getPx(y)));
+                    }
+                    break;
+                }
+                y++;
+            }
+        }
+    }
+
     public void destroyForeMap(int x, int y) {
         Block block = getForeMap(x, y);
+        if (block.isContainer()) {
+            mContainerController.destroyContainer(x, y, FOREGROUND_Z, true);
+        }
         if (block.hasDrop() && shouldDrop(block)) {
             for (int i = 0; i < block.getParams().getDropInfo().getCount(); i++) {
                 mDropController.addDrop(transformX(x) * 16 + 4, y * 16 + 4, mGameItemsHolder.getItem(block.getDrop()));
@@ -207,6 +261,9 @@ public class GameWorld {
 
     public void destroyBackMap(int x, int y) {
         Block block = getBackMap(x, y);
+        if (block.isContainer()) {
+            mContainerController.destroyContainer(x, y, BACKGROUND_Z, true);
+        }
         if (block.hasDrop() && shouldDrop(block)) {
             for (int i = 0; i < block.getParams().getDropInfo().getCount(); i++) {
                 mDropController.addDrop(transformX(x) * 16 + 4, y * 16 + 4, mGameItemsHolder.getItem(block.getDrop()));
@@ -215,4 +272,43 @@ public class GameWorld {
         playerDurateTool();
         placeToBackground(x, y, mGameItemsHolder.getFallbackBlock());
     }
+
+    @CheckForNull
+    private Container getContainerAt(int x, int y, int z) {
+        return mContainerController.getContainer(transformX(x), y, z);
+    }
+
+    @CheckForNull
+    public Container getForegroundContainer(int x, int y) {
+        return getContainerAt(x, y, FOREGROUND_Z);
+    }
+
+    @CheckForNull
+    public Container getBackgroundContainer(int x, int y) {
+        return getContainerAt(x, y, BACKGROUND_Z);
+    }
+
+    @CheckForNull
+    public Furnace getForegroundFurnace(int x, int y) {
+        @CheckForNull
+        final Container container = getForegroundContainer(x, y);
+
+        if (container instanceof Furnace) {
+            return (Furnace) container;
+        }
+
+        return null;
+    }
+
+    @CheckForNull
+    public Furnace getBackgroundFurnace(int x, int y) {
+        @CheckForNull
+        final Container container = getBackgroundContainer(x, y);
+
+        if (container instanceof Furnace) {
+            return (Furnace) container;
+        }
+
+        return null;
+    }
 }
\ No newline at end of file
index e00a0ea4dd2cd3397b632650b69b635cdc1ec24f..004a56b01a945ec581dd084a5e26c390da3de190 100644 (file)
@@ -72,12 +72,17 @@ class GameWorldGenerator(
         val bedrock = gameItemsHolder.getBlock("bedrock")
         val dirt = gameItemsHolder.getBlock("dirt")
         val stone = gameItemsHolder.getBlock("stone")
+        val snow = gameItemsHolder.getBlock("snow")
 
         foreMap[x][surfaceHeight] = grass
         foreMap[x][config.height - 1] = bedrock
         backMap[x][surfaceHeight] = grass
         backMap[x][config.height - 1] = bedrock
 
+        if (surfaceHeight - 1 < config.seaLevel) {
+            foreMap[x][surfaceHeight - 1] = snow
+        }
+
         for (y in min(surfaceHeight + 1, config.seaLevel) ..< config.height - 1) {
             if (y <= surfaceHeight) {
                 backMap[x][y] = dirt
@@ -90,6 +95,13 @@ class GameWorldGenerator(
             }
             backMap[x][y] = foreMap[x][y]
         }
+
+        val plant = random.nextInt(100)
+        if (surfaceHeight < config.seaLevel) {
+            if (plant < 10) {
+                generateSpruce(x)
+            }
+        }
     }
 
     private fun plainsBiome(x: Int) {
@@ -222,6 +234,37 @@ class GameWorldGenerator(
         }
     }
 
+    private fun generateSpruce(x: Int) {
+        val log = gameItemsHolder.getBlock("log_spruce")
+        val leaves = gameItemsHolder.getBlock("leaves_spruce")
+        val h = heights[x] - 1
+        val treeH = random.nextInt(7, 9)
+        val height = max(0, h - treeH)
+
+        val top = height - 1
+        if (top >= 0) {
+            foreMap[x][top] = leaves
+            backMap[x][top] = leaves
+        }
+
+        for (x1 in max(0, x - 1) .. min(config.width - 1, x + 1)) {
+            val y = height
+            foreMap[x1][y] = leaves
+            backMap[x1][y] = leaves
+        }
+
+        for (y in 1..2) {
+            for (x1 in max(0, x - y) .. min(config.width - 1, x + y)) {
+                foreMap[x1][height + 1 + y] = leaves
+                backMap[x1][height + 1 + y] = leaves
+            }
+        }
+
+        for (y in h downTo height) {
+            backMap[x][y] = log
+        }
+    }
+
     private fun generateTallGrass(x: Int) {
         val tallGrass = gameItemsHolder.getBlock(plainsPlants.random(random))
         val h = heights[x] - 1
index 8dc39b93b8b8142261db7886e72d46a244bb8a6a..f016c678a484b35e530058a1e2bc7a5107ea9596 100644 (file)
@@ -24,7 +24,10 @@ class GameWorldMobDamageControllerTask @Inject constructor(
                 val foregroundBlock = gameWorld.getForeMap(x, y)
                 val backgroundBlock = gameWorld.getBackMap(x, y)
 
-                mob.damage(max(foregroundBlock.params.damage, backgroundBlock.params.damage))
+                val damage = max(foregroundBlock.params.damage, backgroundBlock.params.damage)
+                if (damage > 0) {
+                    mob.damage(damage)
+                }
             }
         }
 
index 6accb5726147700119cb351f1d327d7760df4937..5314809e4d483deb763b266c7158df91487e91a9 100644 (file)
@@ -5,13 +5,17 @@ import com.badlogic.gdx.utils.ObjectMap;
 import ru.deadsoftware.cavedroid.CaveGame;
 import ru.deadsoftware.cavedroid.MainConfig;
 import ru.deadsoftware.cavedroid.menu.objects.Button;
-import ru.deadsoftware.cavedroid.menu.submenus.Menu;
-import ru.deadsoftware.cavedroid.menu.submenus.MenuMain;
-import ru.deadsoftware.cavedroid.menu.submenus.MenuNewGame;
+import ru.deadsoftware.cavedroid.menu.submenus.*;
 import ru.deadsoftware.cavedroid.misc.Renderer;
 
 import javax.inject.Inject;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import static ru.deadsoftware.cavedroid.misc.Assets.*;
 
 @MenuScope
@@ -30,6 +34,10 @@ public class MenuProc extends Renderer {
             mMainConfig.getCaveGame().loadGame();
         }
 
+        public void optionsClicked() {
+            mCurrentMenu = mMenuOptions;
+        }
+
         public void quitClicked() {
             Gdx.app.exit();
         }
@@ -45,20 +53,24 @@ public class MenuProc extends Renderer {
         public void backClicked() {
             mCurrentMenu = mMenuMain;
         }
+
+        public void toggleDynamicCamera() {
+            mMainConfig.setUseDynamicCamera(!mMainConfig.isUseDynamicCamera());
+        }
     }
 
     private final MainConfig mMainConfig;
 
     private final MenuMain mMenuMain;
     private final MenuNewGame mMenuNewGame;
+    private final MenuOptions mMenuOptions;
 
     private Menu mCurrentMenu;
 
     @Inject
     public MenuProc(
             MainConfig mainConfig,
-            MenuMain.Factory menuMainFactory,
-            MenuNewGame.Factory menuNewGameFactory
+            MenusFactory menusFactory
     ) {
         super(mainConfig.getWidth(), mainConfig.getHeight());
 
@@ -66,18 +78,42 @@ public class MenuProc extends Renderer {
 
         Input menuInput = new Input();
 
-        mMenuMain = menuMainFactory.get(getWidth(), getHeight(), this::drawButton, menuInput);
-        mMenuNewGame = menuNewGameFactory.get(getWidth(), getHeight(), this::drawButton, menuInput);
+        mMenuMain = menusFactory.getMainMenu(getWidth(), getHeight(), this::drawButton, menuInput);
+        mMenuNewGame = menusFactory.getMenuNewGame(getWidth(), getHeight(), this::drawButton, menuInput);
+        mMenuOptions = menusFactory.getMenuOptions(getWidth(), getHeight(), this::drawButton, menuInput);
 
         mCurrentMenu = mMenuMain;
     }
 
+    private String processVariables(String raw) {
+        final Pattern pattern = Pattern.compile("%%([A-Za-z]+)%%", Pattern.CASE_INSENSITIVE);
+        final Matcher matcher = pattern.matcher(raw);
+        while (matcher.find()) {
+            for (int i = 0; i < matcher.groupCount(); i++) {
+                try {
+                    final String group = matcher.group(i);
+                    final String name = group.replaceAll("%%", "");
+                    final Method method = mMainConfig.getClass().getMethod(name);
+                    final String value = method.invoke(mMainConfig).toString();
+                    raw = raw.replace(group, value);
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+
+        return raw;
+    }
+
     private void drawButton(Button button) {
         spriter.draw(textureRegions.get("button_" + button.getType()), button.getX(), button.getY());
         setFontColor(255, 255, 255);
-        drawString(button.getLabel(),
-                (button.getX() + button.getWidth() / 2) - (float) getStringWidth(button.getLabel()) / 2,
-                (button.getY() + button.getHeight() / 2) - (float) getStringHeight(button.getLabel()) / 2);
+
+        String label = processVariables(button.getLabel());
+
+        drawString(label,
+                (button.getX() + button.getWidth() / 2) - (float) getStringWidth(label) / 2,
+                (button.getY() + button.getHeight() / 2) - (float) getStringHeight(label) / 2);
     }
 
     @Override
index f71183a0583ce95b4c78fa760cb081e5323188e0..6a40fc534b1a5d04e5fb5d6d80c4ae94b8487777 100644 (file)
@@ -28,6 +28,7 @@ public class MenuMain extends Menu {
         HashMap<String, ButtonEventListener> map = new HashMap<>();
         map.put("new_game", mMenuInput::newGameClicked);
         map.put("load_game", mMenuInput::loadGameClicked);
+        map.put("options", mMenuInput::optionsClicked);
         map.put("quit", mMenuInput::quitClicked);
         return map;
     }
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuOptions.kt b/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuOptions.kt
new file mode 100644 (file)
index 0000000..2b72bd6
--- /dev/null
@@ -0,0 +1,28 @@
+package ru.deadsoftware.cavedroid.menu.submenus
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.menu.MenuProc
+import ru.deadsoftware.cavedroid.menu.objects.ButtonEventListener
+import ru.deadsoftware.cavedroid.menu.objects.ButtonRenderer
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader
+
+class MenuOptions(
+    width: Float,
+    height: Float,
+    buttonRenderer: ButtonRenderer,
+    mainConfig: MainConfig,
+    menuInput: MenuProc.Input,
+    assetLoader: AssetLoader,
+) : Menu(width, height, buttonRenderer, mainConfig, menuInput, assetLoader) {
+
+    override fun getButtonEventListeners(): HashMap<String, ButtonEventListener> {
+        val map = HashMap<String, ButtonEventListener>()
+        map["dyncam"] = ButtonEventListener { mMenuInput.toggleDynamicCamera() }
+        map["back"] = ButtonEventListener { mMenuInput.backClicked() }
+        return map
+    }
+
+    override fun initButtons() {
+        loadButtonsFromJson(mAssetLoader.getAssetHandle("json/menu_options_buttons.json"))
+    }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenusFactory.kt b/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenusFactory.kt
new file mode 100644 (file)
index 0000000..e31a9ad
--- /dev/null
@@ -0,0 +1,43 @@
+package ru.deadsoftware.cavedroid.menu.submenus
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.menu.MenuProc
+import ru.deadsoftware.cavedroid.menu.MenuScope
+import ru.deadsoftware.cavedroid.menu.objects.ButtonRenderer
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader
+import javax.inject.Inject
+
+@MenuScope
+class MenusFactory @Inject constructor(
+    private val mainConfig: MainConfig,
+    private val assetLoader: AssetLoader,
+) {
+
+    fun getMainMenu(
+        width: Float,
+        height: Float,
+        buttonRenderer: ButtonRenderer,
+        menuInput: MenuProc.Input,
+    ): MenuMain {
+        return MenuMain(width, height, buttonRenderer, mainConfig, menuInput, assetLoader)
+    }
+
+    fun getMenuNewGame(
+        width: Float,
+        height: Float,
+        buttonRenderer: ButtonRenderer,
+        menuInput: MenuProc.Input,
+    ): MenuNewGame {
+        return MenuNewGame(width, height, buttonRenderer, mainConfig, menuInput, assetLoader)
+    }
+
+    fun getMenuOptions(
+        width: Float,
+        height: Float,
+        buttonRenderer: ButtonRenderer,
+        menuInput: MenuProc.Input,
+    ): MenuOptions {
+        return MenuOptions(width, height, buttonRenderer, mainConfig, menuInput, assetLoader)
+    }
+
+}
\ No newline at end of file
index b7fd03e132063167fe37cc0ee2d97ac5f2b6090e..71cad7a2d69eac9226114ef607735d099bd579ca 100644 (file)
@@ -41,6 +41,12 @@ public class Assets {
     public static Map<String, Texture> blockTextures = new HashMap<>();
     public static Map<String, Texture> itemTextures = new HashMap<>();
 
+    public static Sprite joyBackground;
+    public static Sprite joyStick;
+
+    public static Sprite furnaceBurn;
+    public static Sprite furnaceProgress;
+
     public static void dispose() {
         minecraftFont.dispose();
         loadedTextures.forEach(Texture::dispose);
@@ -73,14 +79,14 @@ public class Assets {
         for (int i = 0; i < sprite.length; i++) {
             for (int j = 0; j < sprite[i].length; j++) {
                 sprite[i][j] = flippedSprite(loadTexture(
-                        assetLoader.getAssetHandle("mobs/" + mob + "/" + i + "_" + j + ".png")));
+                        assetLoader.getAssetHandle("pp/mobs/" + mob + "/" + i + "_" + j + ".png")));
                 sprite[i][j].setOrigin(sprite[i][j].getWidth() / 2, 0);
             }
         }
     }
 
     private static void loadBlockDamage(AssetLoader assetLoader) {
-        final Texture blockDamageTexture = loadTexture(assetLoader.getAssetHandle("break.png"));
+        final Texture blockDamageTexture = loadTexture(assetLoader.getAssetHandle("pp/break.png"));
         for (int i = 0; i < BLOCK_DAMAGE_STAGES; i++) {
             blockDamageSprites[i] = new Sprite(flippedRegion(blockDamageTexture, i * 16, 0, 16, 16));
         }
@@ -100,9 +106,10 @@ public class Assets {
         JsonValue json = jsonReader.parse(assetLoader.getAssetHandle("json/texture_regions.json"));
         for (JsonValue file = json.child(); file != null; file = file.next()) {
             Texture texture = loadTexture(assetLoader.getAssetHandle(file.name() + ".png"));
+            final String[] pathSegments = file.name().split("/");
+            final String name = pathSegments[pathSegments.length - 1];
             if (file.size == 0) {
-                textureRegions.put(file.name(),
-                        flippedRegion(texture, 0, 0, texture.getWidth(), texture.getHeight()));
+                textureRegions.put(name, flippedRegion(texture, 0, 0, texture.getWidth(), texture.getHeight()));
             } else {
                 for (JsonValue key = file.child(); key != null; key = key.next()) {
                     int x = getIntFromJson(key, "x", 0);
@@ -165,11 +172,11 @@ public class Assets {
     }
 
     public static Texture resolveItemTexture(AssetLoader assetLoader, String textureName) {
-        return resolveTexture(assetLoader, textureName, "textures/items", itemTextures);
+        return resolveTexture(assetLoader, textureName, "pp/textures/items", itemTextures);
     }
 
     public static Texture resolveBlockTexture(AssetLoader assetLoader, String textureName) {
-        return resolveTexture(assetLoader, textureName, "textures/blocks", blockTextures);
+        return resolveTexture(assetLoader, textureName, "pp/textures/blocks", blockTextures);
     }
 
     private static void loadAllPngsFromDirInto(FileHandle dir, Map<String, Texture> loadInto) {
@@ -179,15 +186,25 @@ public class Assets {
     }
 
     private static void loadItems(AssetLoader assetLoader) {
-        final FileHandle itemsDir = assetLoader.getAssetHandle("textures/items");
+        final FileHandle itemsDir = assetLoader.getAssetHandle("pp/textures/items");
         loadAllPngsFromDirInto(itemsDir, itemTextures);
     }
 
     private static void loadBlocks(AssetLoader assetLoader) {
-        final FileHandle blocksDir = assetLoader.getAssetHandle("textures/blocks");
+        final FileHandle blocksDir = assetLoader.getAssetHandle("pp/textures/blocks");
         loadAllPngsFromDirInto(blocksDir, blockTextures);
     }
 
+    private static void loadJoystick(AssetLoader assetLoader) {
+        joyStick = new Sprite(loadTexture(assetLoader.getAssetHandle("joy_stick.png")));
+        joyBackground = new Sprite(loadTexture(assetLoader.getAssetHandle("joy_background.png")));
+    }
+
+    private static void loadFurnace(AssetLoader assetLoader) {
+        furnaceBurn = new Sprite(textureRegions.get("furnace_burn"));
+        furnaceProgress = new Sprite(textureRegions.get("furnace_progress"));
+    }
+
     public static void load(final AssetLoader assetLoader) {
         loadMob(assetLoader, playerSprite, "char");
         loadMob(assetLoader, pigSprite, "pig");
@@ -196,9 +213,12 @@ public class Assets {
         loadBlocks(assetLoader);
         loadItems(assetLoader);
         loadTouchButtonsFromJSON(assetLoader);
+        loadJoystick(assetLoader);
+        loadFurnace(assetLoader);
         setPlayerHeadOrigin();
         minecraftFont = new BitmapFont(assetLoader.getAssetHandle("font.fnt"), true);
         minecraftFont.getData().setScale(.375f);
+        minecraftFont.setUseIntegerPositions(false);
     }
 
     /**
index ecf1ce03298afa5cbb7a24f05a79d30e3d4b2db8..24103e7009601080eecffde9de2d6eeebff7f72f 100644 (file)
@@ -15,4 +15,4 @@ val Int.px get() = this * 16f
 /**
  * Converts this value in PIXELS into blocks
  */
-val Float.bl get() = MathUtils.floor(this / 16)
\ No newline at end of file
+val Float.bl get() = MathUtils.floor(this / 16f)
\ No newline at end of file
index 8976c9963cd8935d64a373ee54db088513537662..917f69b591933ee9a6bfc83354e5f012c0894363 100644 (file)
@@ -1,9 +1,12 @@
 package ru.deadsoftware.cavedroid.misc.utils
 
+import com.badlogic.gdx.Gdx
 import com.badlogic.gdx.graphics.Color
+import com.badlogic.gdx.graphics.GL20
 import com.badlogic.gdx.graphics.g2d.GlyphLayout
 import com.badlogic.gdx.graphics.g2d.SpriteBatch
 import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.MainConfig
 import ru.deadsoftware.cavedroid.misc.Assets
 
 private fun Rectangle.shifted(shift: Float) = Rectangle(x + shift, y, width, height)
@@ -69,3 +72,27 @@ fun colorFromHexString(hex: String): Color {
     rgba = (rgba shl 8) or 0xFF
     return Color(rgba)
 }
+
+fun SpriteBatch.withScissors(
+    mainConfig: MainConfig,
+    x: Float,
+    y: Float,
+    width: Float,
+    height: Float,
+    block: () -> Unit
+) {
+    val scaleX = Gdx.graphics.width / mainConfig.width
+    val scaleY = Gdx.graphics.height / mainConfig.height
+
+    flush()
+    Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST)
+    Gdx.gl.glScissor(
+        /* x = */ (x * scaleX).toInt(),
+        /* y = */ ((mainConfig.height - y - height) * scaleY).toInt(),
+        /* width = */ (width * scaleX).toInt(),
+        /* height = */ (height * scaleY).toInt()
+    )
+    block.invoke()
+    flush()
+    Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST)
+}
index 7b75e3893be9f6a49b7e588aa67eb565738e62ad..a26c5b95dd8b3699fb4bf8362b8e7c6ed890520d 100644 (file)
@@ -1,5 +1,6 @@
 package ru.deadsoftware.cavedroid.misc.utils
 
+import com.badlogic.gdx.graphics.Color
 import com.badlogic.gdx.graphics.g2d.Sprite
 import com.badlogic.gdx.graphics.g2d.SpriteBatch
 
@@ -14,14 +15,20 @@ fun SpriteBatch.drawSprite(
     rotation: Float = 0f,
     width: Float = sprite.regionWidth.toFloat(),
     height: Float = sprite.regionHeight.toFloat(),
+    tint: Color? = null,
 ) {
+    val oldColor = sprite.color
+
     sprite.setPosition(x, y)
     sprite.setSize(width, height)
     sprite.rotation = rotation
+    tint?.let(sprite::setColor)
+
     sprite.draw(this)
 
     sprite.setSize(sprite.regionWidth.toFloat(), sprite.regionHeight.toFloat())
     sprite.rotation = 0f
+    sprite.color = oldColor
 }
 
 fun Sprite.applyOrigin(origin: SpriteOrigin) {
index 5e3f71a9e1a0c79ea7cfbb523a22deec02d2ed48..23acc649eaa478eb4110207ebb0822de6c9b5879 100755 (executable)
@@ -5,7 +5,7 @@ if [[ ! $1 ]]; then
   exit
 fi
 
-./require_clean_work_tree "$0"
+./require-clean-work-tree.sh "$0" || exit 1
 
 release_dir="release-$1"
 
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from require-celan-work-tree.sh
rename to require-clean-work-tree.sh