DEADSOFTWARE

CaveGame in kotlin master
authorfredboy <fredboy@protonmail.com>
Wed, 22 May 2024 13:46:11 +0000 (20:46 +0700)
committerfredboy <fredboy@protonmail.com>
Wed, 22 May 2024 13:46:11 +0000 (20:46 +0700)
365 files changed:
.gitignore
COPYING
README.md
android/assets/health.png [deleted file]
android/assets/icons/icon128.png
android/assets/icons/icon256.png
android/assets/icons/icon512.png [new file with mode: 0644]
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/porkchop_cooked.png [new file with mode: 0644]
android/assets/pp/textures/items/porkchop_raw.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
android/debug/res/drawable-anydpi-v26/ic_launcher_foreground.xml [deleted file]
android/debug/res/drawable-hdpi/ic_launcher.png
android/debug/res/drawable-mdpi/ic_launcher.png
android/debug/res/drawable-xhdpi/ic_launcher.png
android/debug/res/drawable-xxhdpi/ic_launcher.png
android/debug/res/drawable-xxxhdpi/ic_launcher.png
android/debug/res/drawable/ic_launcher_foreground.xml [new file with mode: 0644]
android/ic_launcher-web.png
android/res/drawable-anydpi-v26/ic_launcher_background.xml [deleted file]
android/res/drawable-anydpi-v26/ic_launcher_foreground.xml [deleted file]
android/res/drawable-hdpi/ic_launcher.png
android/res/drawable-mdpi/ic_launcher.png
android/res/drawable-xhdpi/ic_launcher.png
android/res/drawable-xxhdpi/ic_launcher.png
android/res/drawable-xxxhdpi/ic_launcher.png
android/res/drawable/ic_launcher_background.xml [new file with mode: 0644]
android/res/drawable/ic_launcher_foreground.xml [new file with mode: 0644]
android/src/ru/deadsoftware/cavedroid/AndroidLauncher.java
android/src/ru/deadsoftware/cavedroid/AndroidPreferencesStore.kt [new file with mode: 0644]
build.gradle
core/build.gradle
core/src/ru/deadsoftware/cavedroid/CaveGame.java [deleted file]
core/src/ru/deadsoftware/cavedroid/CaveGame.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/MainComponent.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 [deleted file]
core/src/ru/deadsoftware/cavedroid/game/GameScreen.java
core/src/ru/deadsoftware/cavedroid/game/actions/PlaceBlockActionsModule.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/actions/UpdateBlockActionsModule.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/actions/UseItemActionsModule.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToBackgroundAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToForegroundAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceSlabAction.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/UpdateGravelAction.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateRequiresBlockAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSandAction.kt [deleted file]
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/UseLavaBucketAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UsePigSpawnEggAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseWaterBucketAction.kt
core/src/ru/deadsoftware/cavedroid/game/debug/DebugInfoStringsProvider.kt
core/src/ru/deadsoftware/cavedroid/game/input/IGameInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/Joystick.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/input/KeyboardInputHandlersModule.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/input/MouseInputHandlersModule.kt [deleted file]
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/OpenCraftingKeyboardInputHandler.kt [deleted file]
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 [new file with mode: 0644]
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/ToggleDebugInfoKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleGameModeKeyboardInputHandler.kt
core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleMinimapKeyboardInputHandler.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/FallingBlock.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/mobs/FallingGravel.java [deleted file]
core/src/ru/deadsoftware/cavedroid/game/mobs/FallingSand.java [deleted file]
core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java
core/src/ru/deadsoftware/cavedroid/game/mobs/MobSaveDataMapper.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.kt
core/src/ru/deadsoftware/cavedroid/game/mobs/PeacefulMob.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.kt
core/src/ru/deadsoftware/cavedroid/game/mobs/package-info.java [deleted file]
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 57% 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/dto/SaveDataDto.kt [new file with mode: 0644]
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/Drop.kt [deleted file]
core/src/ru/deadsoftware/cavedroid/game/objects/DropController.java [deleted file]
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 [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/objects/drop/DropController.java [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/objects/package-info.java [deleted file]
core/src/ru/deadsoftware/cavedroid/game/package-info.java [deleted file]
core/src/ru/deadsoftware/cavedroid/game/render/BackgroundBlocksRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/BlocksRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/DebugRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/DropsRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/ForegroundBlocksRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/HudRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/MobsRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/render/RenderModule.kt [deleted file]
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/save/GameSaveData.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/game/save/GameSaveLoader.kt [new file with mode: 0644]
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/GameWorldBlocksLogicControllerTask.kt
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java [deleted file]
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.kt [new file with mode: 0644]
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/objects/BooleanOptionButton.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/menu/objects/package-info.java [deleted file]
core/src/ru/deadsoftware/cavedroid/menu/package-info.java [deleted file]
core/src/ru/deadsoftware/cavedroid/menu/submenus/Menu.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/menu/submenus/package-info.java [deleted file]
core/src/ru/deadsoftware/cavedroid/misc/Assets.java
core/src/ru/deadsoftware/cavedroid/misc/Saveable.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/annotations/NonnullByDefault.java [deleted file]
core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindKeyboardInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindMouseInputHandler.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindPlaceBlockAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindRenderer.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUpdateBlockAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUseBlockAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUseItemAction.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/MultibindingConfig.kt [new file with mode: 0644]
core/src/ru/deadsoftware/cavedroid/misc/package-info.java [deleted file]
core/src/ru/deadsoftware/cavedroid/misc/utils/GdxExtensions.kt [new file with mode: 0644]
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
core/src/ru/deadsoftware/cavedroid/package-info.java [deleted file]
core/src/ru/deadsoftware/cavedroid/prefs/PreferencesStore.kt [new file with mode: 0644]
desktop/build.gradle
desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.java
desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopPreferencesStore.kt [new file with mode: 0644]
gradle.properties
make-release.sh
require-clean-work-tree.sh [moved from require-celan-work-tree.sh with 100% similarity, mode: 0755]
settings.gradle

index e03f5587f9ef9b42b0be788ce28a0aa394291c60..b24a8edfa256f29fdaa63c65c53aa04125be7892 100644 (file)
@@ -119,3 +119,5 @@ Thumbs.db
 
 release-*/
 keystore.properties
+
+*/build/
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..bf5e3713ed7512add61b4810b1329da1eeedbe7e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -3,10 +3,18 @@
 [![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/>
 ## Build instructions
+You need to publish [my ksp processor](https://github.com/fredboy/automultibind) to mavenLocal repository first.
 ### Android
 To build for Android use <br>
 `./gradlew android:assemble` <br>
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
index 19e45326fa08578225d7d92a3ba04a13b4aec611..08d0b7401fa8cfbcb93b395b99127fd8353c2951 100644 (file)
Binary files a/android/assets/icons/icon128.png and b/android/assets/icons/icon128.png differ
index 3b8b92b7a7a1fd042ca55e527917fe972a72741a..4e369641fc1cdff4c6ef0ac0eaafb4ecca175b63 100644 (file)
Binary files a/android/assets/icons/icon256.png and b/android/assets/icons/icon256.png differ
diff --git a/android/assets/icons/icon512.png b/android/assets/icons/icon512.png
new file mode 100644 (file)
index 0000000..35aa45b
Binary files /dev/null and b/android/assets/icons/icon512.png 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..349daf3f752dfb4a5caeb70f9a8c2c6dbd6e1589 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"
       "drop": "sand",
       "texture": "sand",
       "tool_level": 0,
-      "tool_type": "shovel"
+      "tool_type": "shovel",
+      "fallable": true
     },
     "gravel": {
       "hp": 54,
       "drop": "gravel",
       "texture": "gravel",
       "tool_level": 0,
-      "tool_type": "shovel"
+      "tool_type": "shovel",
+      "fallable": true
     },
     "gold_ore": {
       "hp": 900,
       "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": "lava"
+      "texture": "chest"
+    },
+    "sapling_spruce": {
+      "name": "Spruce Sapling",
+      "type": "block",
+      "texture": "sapling_spruce",
+      "burning_time": 5000
+    },
+    "bedrock": {
+      "name": "Bedrock",
+      "type": "block",
+      "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
+    },
+    "porkchop_raw": {
+      "name": "Raw Porkchop",
+      "type": "food",
+      "texture": "porkchop_raw",
+      "heal": 3,
+      "smelt_product": "porkchop_cooked"
+    },
+    "porkchop_cooked": {
+      "name": "Raw Porkchop",
+      "type": "food",
+      "texture": "porkchop_cooked",
+      "heal": 8
     }
   }
 }
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..bb956cd
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "dyncam": {
+    "option_type": "boolean",
+    "label": "Dynamic Camera: %%value%%"
+  },
+  "fullscreen": {
+    "option_type": "boolean",
+    "label": "Fullscreen: %%value%%",
+    "visible_on_android": false
+  },
+  "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/porkchop_cooked.png b/android/assets/pp/textures/items/porkchop_cooked.png
new file mode 100644 (file)
index 0000000..dbd02c5
Binary files /dev/null and b/android/assets/pp/textures/items/porkchop_cooked.png differ
diff --git a/android/assets/pp/textures/items/porkchop_raw.png b/android/assets/pp/textures/items/porkchop_raw.png
new file mode 100644 (file)
index 0000000..6c83dd0
Binary files /dev/null and b/android/assets/pp/textures/items/porkchop_raw.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..d52b9e92d650e7802b3bdfd545bedb0fbbeb6301 100644 (file)
@@ -1,4 +1,9 @@
+buildscript {
+    configurations { natives }
+}
+
 plugins {
+    id "com.android.application"
     id "kotlin-android"
 }
 
@@ -32,10 +37,12 @@ android {
     }
     defaultConfig {
         applicationId "ru.deadsoftware.cavedroid"
-        minSdkVersion 24
+        minSdkVersion 19
         targetSdkVersion 34
-        versionCode 19
-        versionName "alpha0.6.2"
+        versionCode 25
+        versionName "alpha0.9.2"
+
+        multiDexEnabled true
     }
     applicationVariants.all { variant ->
         variant.outputs.all {
@@ -123,4 +130,18 @@ task run(type: Exec) {
 
     def adb = path + "/platform-tools/adb"
     commandLine "$adb", 'shell', 'am', 'start', '-n', 'ru.deadsoftware.cavedroid/ru.deadsoftware.cavedroid.AndroidLauncher'
+}
+
+dependencies {
+    implementation project(":core")
+    implementation platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")
+    api "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
+    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
+    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
+    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
+    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
+
+    configurations.implementation {
+        exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
+    }
 }
\ No newline at end of file
diff --git a/android/debug/res/drawable-anydpi-v26/ic_launcher_foreground.xml b/android/debug/res/drawable-anydpi-v26/ic_launcher_foreground.xml
deleted file mode 100644 (file)
index 4535da3..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="512dp"
-    android:height="512dp"
-    android:viewportWidth="512"
-    android:viewportHeight="512">
-  <path
-      android:pathData="M160,160L160,176L144,176L144,224L160,224L160,240L176,240L176,256L192,256L192,272L208,272L208,256L192,256L192,240L176,240L176,224L160,224L160,176L176,176L176,160L160,160z"
-      android:fillColor="#20180a"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M176,160L176,176L208,176L208,192L224,192L224,208L240,208L240,224L272,224L272,208L288,208L288,192L304,192L304,176L336,176L336,160L304,160L304,176L288,176L288,192L272,192L272,208L240,208L240,192L224,192L224,176L208,176L208,160L176,160z"
-      android:fillColor="#372910"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M336,160L336,176L352,176L352,224L336,224L336,240L320,240L320,256L304,256L304,272L320,272L320,256L336,256L336,240L352,240L352,224L368,224L368,176L352,176L352,160L336,160z"
-      android:fillColor="#20180a"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M160,176L160,192L176,192L176,208L192,208L192,224L208,224L208,240L224,240L224,224L208,224L208,208L192,208L192,192L176,192L176,176L160,176z"
-      android:fillColor="#6b511f"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M176,176L176,192L208,192L208,208L224,208L224,224L240,224L240,208L224,208L224,192L208,192L208,176L176,176M304,176L304,192L288,192L288,208L272,208L272,224L288,224L288,208L304,208L304,192L336,192L336,224L320,224L320,240L304,240L304,256L320,256L320,240L336,240L336,224L352,224L352,192L336,192L336,176L304,176z"
-      android:fillColor="#866526"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M336,176L336,192L352,192L352,176L336,176z"
-      android:fillColor="#6b511f"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M160,192L160,224L176,224L176,240L192,240L192,256L208,256L208,240L192,240L192,224L176,224L176,192L160,192z"
-      android:fillColor="#866526"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M192,192L192,208L208,208L208,192L192,192M304,192L304,208L288,208L288,224L304,224L304,240L320,240L320,224L336,224L336,208L320,208L320,192L304,192z"
-      android:fillColor="#755821"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M320,192L320,208L336,208L336,192L320,192z"
-      android:fillColor="#6b511f"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M176,208L176,224L192,224L192,208L176,208M208,208L208,224L224,224L224,208L208,208z"
-      android:fillColor="#755821"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M304,208L304,224L320,224L320,208L304,208z"
-      android:fillColor="#6b511f"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M192,224L192,240L208,240L208,224L192,224z"
-      android:fillColor="#755821"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M224,224L224,240L240,240L240,256L256,256L256,272L272,272L272,288L288,288L288,304L304,304L304,320L320,320L320,336L336,336L336,352L352,352L352,320L320,320L320,304L304,304L304,288L288,288L288,272L272,272L272,240L288,240L288,224L272,224L272,240L240,240L240,224L224,224z"
-      android:fillColor="#493615"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M240,224L240,240L272,240L272,224L240,224z"
-      android:fillColor="#000000"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M288,224L288,240L304,240L304,224L288,224z"
-      android:fillColor="#6b511f"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M208,240L208,256L224,256L224,272L240,272L240,304L224,304L224,320L208,320L208,336L192,336L192,352L176,352L176,368L208,368L208,336L224,336L224,320L240,320L240,304L272,304L272,320L288,320L288,336L304,336L304,368L336,368L336,352L320,352L320,336L304,336L304,320L288,320L288,304L272,304L272,288L256,288L256,272L240,272L240,256L224,256L224,240L208,240z"
-      android:fillColor="#281e0b"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M224,240L224,256L240,256L240,240L224,240M272,240L272,256L288,256L288,240L272,240z"
-      android:fillColor="#896727"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M288,240L288,256L304,256L304,240L288,240z"
-      android:fillColor="#281e0b"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M240,256L240,272L256,272L256,256L240,256z"
-      android:fillColor="#896727"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M272,256L272,272L288,272L288,256L272,256z"
-      android:fillColor="#281e0b"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M224,272L224,288L208,288L208,304L192,304L192,320L160,320L160,352L176,352L176,336L192,336L192,320L208,320L208,304L224,304L224,288L240,288L240,272L224,272z"
-      android:fillColor="#493615"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M256,272L256,288L272,288L272,272L256,272z"
-      android:fillColor="#684e1e"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M224,288L224,304L240,304L240,288L224,288M272,288L272,304L288,304L288,288L272,288z"
-      android:fillColor="#896727"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M208,304L208,320L224,320L224,304L208,304M288,304L288,320L304,320L304,304L288,304z"
-      android:fillColor="#684e1e"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M192,320L192,336L208,336L208,320L192,320M304,320L304,336L320,336L320,320L304,320M176,336L176,352L192,352L192,336L176,336M320,336L320,352L336,352L336,336L320,336z"
-      android:fillColor="#896727"
-      android:strokeColor="#00000000"/>
-</vector>
index 9c5bde7a6ec143bdd8458d732e362630fa2eaa18..595b5c6516bba7f95193cccf8ca615756058bf2a 100644 (file)
Binary files a/android/debug/res/drawable-hdpi/ic_launcher.png and b/android/debug/res/drawable-hdpi/ic_launcher.png differ
index 7e8a490da5483a2c6b5b6b8a2f4d9b17110b5043..76e15c3bd61b854e4f0827ef368fe47739a3b9f7 100644 (file)
Binary files a/android/debug/res/drawable-mdpi/ic_launcher.png and b/android/debug/res/drawable-mdpi/ic_launcher.png differ
index 88f3a2165a36b59a5733339b0a8d4ba8b2352e32..1c55208392d2c6b540c6463ab8d87d69d19d34e2 100644 (file)
Binary files a/android/debug/res/drawable-xhdpi/ic_launcher.png and b/android/debug/res/drawable-xhdpi/ic_launcher.png differ
index ca5ae82ab948b9b8ae0bf88f74cdc04ceefac486..94f1374abf9f9b3010977924b693b18e295f5ebf 100644 (file)
Binary files a/android/debug/res/drawable-xxhdpi/ic_launcher.png and b/android/debug/res/drawable-xxhdpi/ic_launcher.png differ
index 89ce1056cced58b6f4f3740958c4cf612c7c4708..88ddb6771cef18355f619cd6556a48865098f0c3 100644 (file)
Binary files a/android/debug/res/drawable-xxxhdpi/ic_launcher.png and b/android/debug/res/drawable-xxxhdpi/ic_launcher.png differ
diff --git a/android/debug/res/drawable/ic_launcher_foreground.xml b/android/debug/res/drawable/ic_launcher_foreground.xml
new file mode 100644 (file)
index 0000000..c963cce
--- /dev/null
@@ -0,0 +1,162 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+  <path
+      android:pathData="m19,10c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM19,11c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1zM21,12c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM21,13c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#715a47"/>
+  <path
+      android:pathData="m21,10c0,0.333 0,0.667 0,1 -0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 -0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 -0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.667,0 1.333,0 2,0 0,-0.333 0,-0.667 0,-1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 0.333,0 0.667,0 1,0 0,-0.667 0,-1.333 0,-2 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5d493c"/>
+  <path
+      android:pathData="m18,12c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#41352b"/>
+  <path
+      android:pathData="m19,11c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#41352b"/>
+  <path
+      android:pathData="m21,9c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM22,10c0,1 0,2 0,3 0.333,0 0.667,0 1,0 0,-1 0,-2 0,-3 -0.333,0 -0.667,0 -1,0zM22,13c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1zM21,14c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1zM21,10c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1zM17,13c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM18,14c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m20,9c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM17,12c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM19,14c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+  <path
+      android:pathData="m16,15c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM18,15c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM14,18c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#b49883"/>
+  <path
+      android:pathData="m17,14c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM15,18c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#d6ceb9"/>
+  <path
+      android:pathData="m17,15c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM14,17c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#e8e3d4"/>
+  <path
+      android:pathData="m17,16c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#c5b79e"/>
+  <path
+      android:pathData="m14,19c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM14,20c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1zM13,21c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1zM12,22c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1zM11,23c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#41352b"/>
+  <path
+      android:pathData="m9,22c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m10,21c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m11,20c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m12,19c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m13,18c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5d493c"/>
+  <path
+      android:pathData="m13,19c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM13,20c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#715a47"/>
+  <path
+      android:pathData="m11,21c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM11,22c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1zM10,23c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+  <path
+      android:pathData="m12,10c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM13,11c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM10,12c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM11,13c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#715a47"/>
+  <path
+      android:pathData="m10,10c0,0.667 0,1.333 0,2 0.333,0 0.667,0 1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,0.333 0,0.667 0,1 0.667,0 1.334,0 2,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5d493c"/>
+  <path
+      android:pathData="m13,12c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#41352b"/>
+  <path
+      android:pathData="m12,11c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#41352b"/>
+  <path
+      android:pathData="m10,9c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM11,10c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM10,10c-0.333,0 -0.667,0 -1,0 0,1 0,2 0,3 0.333,0 0.667,0 1,0 0,-1 0,-2 0,-3zM10,13c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM11,14c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM14,13c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM14,14c-0.333,0 -0.667,0 -1,0 0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m11,9c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM14,12c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM12,14c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+  <path
+      android:pathData="m13,15c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM15,15c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM16,17c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM17,18c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#b49883"/>
+  <path
+      android:pathData="m14,14c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM16,18c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#d6ceb9"/>
+  <path
+      android:pathData="m14,15c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM17,17c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#e8e3d4"/>
+  <path
+      android:pathData="m14,16c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#c5b79e"/>
+  <path
+      android:pathData="m15,16c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#715a47"/>
+  <path
+      android:pathData="m15,17c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m16,16c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+  <path
+      android:pathData="m17,19c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM18,20c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM19,21c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM20,22c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM21,23c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#41352b"/>
+  <path
+      android:pathData="m22,22c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m21,21c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m20,20c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m19,19c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m18,18c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5d493c"/>
+  <path
+      android:pathData="m18,19c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM19,20c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#715a47"/>
+  <path
+      android:pathData="m20,21c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM21,22c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0zM22,23c0,0.333 0,0.667 0,1 0.333,0 0.667,0 1,0 0,-0.333 0,-0.667 0,-1 -0.333,0 -0.667,0 -1,0z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+</vector>
index f6a837e7495b0b09b20084b62ccce3af13546625..9d5fc15cdfa1fbbd7b48aff3c039d25ddb73bfa2 100644 (file)
Binary files a/android/ic_launcher-web.png and b/android/ic_launcher-web.png differ
diff --git a/android/res/drawable-anydpi-v26/ic_launcher_background.xml b/android/res/drawable-anydpi-v26/ic_launcher_background.xml
deleted file mode 100644 (file)
index b398624..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-<vector android:height="512dp" android:viewportHeight="256"
-    android:viewportWidth="256" android:width="512dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M0,0L0,64L80,64L80,48L16,48L16,32L32,32L32,16L16,16L16,0L0,0z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M16,0L16,16L80,16L80,0L16,0z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M80,0L80,16L96,16L96,32L112,32L112,16L128,16L128,0L80,0z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M128,0L128,16L144,16L144,0L128,0z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M144,0L144,16L176,16L176,0L144,0z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M176,0L176,16L240,16L240,32L256,32L256,16L240,16L240,0L176,0z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M240,0L240,16L256,16L256,0L240,0z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M32,16L32,32L96,32L96,16L32,16z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M112,16L112,32L80,32L80,48L128,48L128,32L144,32L144,48L160,48L160,64L48,64L48,80L80,80L80,112L48,112L48,128L128,128L128,112L96,112L96,96L128,96L128,80L160,80L160,64L240,64L240,48L160,48L160,32L144,32L144,16L112,16z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M144,16L144,32L192,32L192,16L144,16z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M192,16L192,32L208,32L208,16L192,16z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M208,16L208,32L160,32L160,48L208,48L208,32L224,32L224,48L240,48L240,80L256,80L256,32L240,32L240,16L208,16z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M16,32L16,48L32,48L32,32L16,32z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M32,32L32,48L64,48L64,32L32,32z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M64,32L64,48L80,48L80,32L64,32z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M128,32L128,48L144,48L144,64L160,64L160,48L144,48L144,32L128,32z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M208,32L208,48L224,48L224,32L208,32z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M80,48L80,64L96,64L96,48L80,48z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M96,48L96,64L112,64L112,48L96,48z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M112,48L112,64L144,64L144,48L112,48M0,64L0,80L48,80L48,64L0,64M160,64L160,80L240,80L240,64L160,64z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M0,80L0,96L32,96L32,80L0,80z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M32,80L32,96L0,96L0,112L32,112L32,128L48,128L48,96L80,96L80,80L32,80M128,80L128,96L144,96L144,80L128,80z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M144,80L144,96L160,96L160,80L144,80z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M160,80L160,96L192,96L192,112L208,112L208,128L192,128L192,144L208,144L208,128L256,128L256,112L224,112L224,96L192,96L192,80L160,80z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M192,80L192,96L224,96L224,112L240,112L240,96L256,96L256,80L192,80z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M48,96L48,112L80,112L80,96L48,96z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M96,96L96,112L112,112L112,96L96,96z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M112,96L112,112L192,112L192,96L112,96z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M240,96L240,112L256,112L256,96L240,96z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M0,112L0,128L32,128L32,112L0,112z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M128,112L128,128L176,128L176,112L128,112z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M176,112L176,128L208,128L208,112L176,112z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M0,128L0,176L16,176L16,192L0,192L0,240L48,240L48,224L16,224L16,192L64,192L64,176L32,176L32,160L64,160L64,144L32,144L32,160L16,160L16,128L0,128z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M16,128L16,144L48,144L48,128L16,128z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M48,128L48,144L64,144L64,128L48,128z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M64,128L64,144L80,144L80,128L64,128z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M80,128L80,144L112,144L112,160L128,160L128,144L144,144L144,160L176,160L176,144L144,144L144,128L80,128z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M144,128L144,144L192,144L192,128L144,128M208,128L208,144L224,144L224,160L240,160L240,144L256,144L256,128L208,128z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M16,144L16,160L32,160L32,144L16,144M64,144L64,160L112,160L112,176L128,176L128,160L144,160L144,176L160,176L160,160L144,160L144,144L128,144L128,160L112,160L112,144L64,144M176,144L176,160L224,160L224,144L176,144z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M240,144L240,160L208,160L208,176L160,176L160,192L128,192L128,176L80,176L80,192L128,192L128,208L160,208L160,192L208,192L208,176L256,176L256,144L240,144z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M32,160L32,176L112,176L112,160L32,160z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M128,160L128,176L144,176L144,160L128,160z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M160,160L160,176L208,176L208,160L160,160z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M0,176L0,192L16,192L16,176L0,176z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M64,176L64,192L80,192L80,176L64,176z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M128,176L128,192L160,192L160,176L128,176M208,176L208,192L240,192L240,176L208,176z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M240,176L240,192L256,192L256,176L240,176z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M16,192L16,208L48,208L48,192L16,192z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M48,192L48,208L96,208L96,192L48,192z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M96,192L96,208L112,208L112,224L48,224L48,240L0,240L0,256L64,256L64,240L128,240L128,224L160,224L160,208L176,208L176,224L192,224L192,208L240,208L240,192L160,192L160,208L128,208L128,192L96,192z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M240,192L240,208L256,208L256,192L240,192z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M16,208L16,224L96,224L96,208L16,208z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M96,208L96,224L112,224L112,208L96,208z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M160,208L160,224L176,224L176,208L160,208z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M192,208L192,240L208,240L208,224L224,224L224,208L192,208z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M224,208L224,224L256,224L256,208L224,208z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M128,224L128,240L112,240L112,256L176,256L176,240L160,240L160,224L128,224z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M160,224L160,240L192,240L192,224L160,224z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#747474"
-        android:pathData="M208,224L208,240L176,240L176,256L224,256L224,240L240,240L240,224L208,224z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#7f7f7f"
-        android:pathData="M240,224L240,240L224,240L224,256L256,256L256,224L240,224z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#686868"
-        android:pathData="M64,240L64,256L80,256L80,240L64,240z" android:strokeColor="#00000000"/>
-    <path android:fillColor="#8f8f8f"
-        android:pathData="M80,240L80,256L112,256L112,240L80,240z" android:strokeColor="#00000000"/>
-</vector>
diff --git a/android/res/drawable-anydpi-v26/ic_launcher_foreground.xml b/android/res/drawable-anydpi-v26/ic_launcher_foreground.xml
deleted file mode 100644 (file)
index ce92ea1..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="512dp"
-    android:height="512dp"
-    android:viewportWidth="512"
-    android:viewportHeight="512">
-  <path
-      android:pathData="M160,160L160,176L144,176L144,224L160,224L160,240L176,240L176,256L192,256L192,272L208,272L208,256L192,256L192,240L176,240L176,224L160,224L160,176L176,176L176,160L160,160z"
-      android:fillColor="#17172d"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M176,160L176,176L208,176L208,192L224,192L224,208L240,208L240,224L272,224L272,208L288,208L288,192L304,192L304,176L336,176L336,160L304,160L304,176L288,176L288,192L272,192L272,208L240,208L240,192L224,192L224,176L208,176L208,160L176,160z"
-      android:fillColor="#0e3f36"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M336,160L336,176L352,176L352,224L336,224L336,240L320,240L320,256L304,256L304,272L320,272L320,256L336,256L336,240L352,240L352,224L368,224L368,176L352,176L352,160L336,160z"
-      android:fillColor="#17172d"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M160,176L160,192L176,192L176,208L192,208L192,224L208,224L208,240L224,240L224,224L208,224L208,208L192,208L192,192L176,192L176,176L160,176z"
-      android:fillColor="#27b29a"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M176,176L176,192L208,192L208,208L224,208L224,224L240,224L240,208L224,208L224,192L208,192L208,176L176,176M304,176L304,192L288,192L288,208L272,208L272,224L288,224L288,208L304,208L304,192L336,192L336,224L320,224L320,240L304,240L304,256L320,256L320,240L336,240L336,224L352,224L352,192L336,192L336,176L304,176z"
-      android:fillColor="#33ebcb"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M336,176L336,192L352,192L352,176L336,176z"
-      android:fillColor="#27b29a"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M160,192L160,224L176,224L176,240L192,240L192,256L208,256L208,240L192,240L192,224L176,224L176,192L160,192z"
-      android:fillColor="#33ebcb"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M192,192L192,208L208,208L208,192L192,192M304,192L304,208L288,208L288,224L304,224L304,240L320,240L320,224L336,224L336,208L320,208L320,192L304,192z"
-      android:fillColor="#2bc7ac"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M320,192L320,208L336,208L336,192L320,192z"
-      android:fillColor="#27b29a"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M176,208L176,224L192,224L192,208L176,208M208,208L208,224L224,224L224,208L208,208z"
-      android:fillColor="#2bc7ac"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M304,208L304,224L320,224L320,208L304,208z"
-      android:fillColor="#27b29a"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M192,224L192,240L208,240L208,224L192,224z"
-      android:fillColor="#2bc7ac"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M224,224L224,240L240,240L240,256L256,256L256,272L272,272L272,288L288,288L288,304L304,304L304,320L320,320L320,336L336,336L336,352L352,352L352,320L320,320L320,304L304,304L304,288L288,288L288,272L272,272L272,240L288,240L288,224L272,224L272,240L240,240L240,224L224,224z"
-      android:fillColor="#493615"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M240,224L240,240L272,240L272,224L240,224z"
-      android:fillColor="#000000"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M288,224L288,240L304,240L304,224L288,224z"
-      android:fillColor="#27b29a"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M208,240L208,256L224,256L224,272L240,272L240,304L224,304L224,320L208,320L208,336L192,336L192,352L176,352L176,368L208,368L208,336L224,336L224,320L240,320L240,304L272,304L272,320L288,320L288,336L304,336L304,368L336,368L336,352L320,352L320,336L304,336L304,320L288,320L288,304L272,304L272,288L256,288L256,272L240,272L240,256L224,256L224,240L208,240z"
-      android:fillColor="#281e0b"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M224,240L224,256L240,256L240,240L224,240M272,240L272,256L288,256L288,240L272,240z"
-      android:fillColor="#896727"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M288,240L288,256L304,256L304,240L288,240z"
-      android:fillColor="#281e0b"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M240,256L240,272L256,272L256,256L240,256z"
-      android:fillColor="#896727"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M272,256L272,272L288,272L288,256L272,256z"
-      android:fillColor="#281e0b"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M224,272L224,288L208,288L208,304L192,304L192,320L160,320L160,352L176,352L176,336L192,336L192,320L208,320L208,304L224,304L224,288L240,288L240,272L224,272z"
-      android:fillColor="#493615"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M256,272L256,288L272,288L272,272L256,272z"
-      android:fillColor="#684e1e"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M224,288L224,304L240,304L240,288L224,288M272,288L272,304L288,304L288,288L272,288z"
-      android:fillColor="#896727"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M208,304L208,320L224,320L224,304L208,304M288,304L288,320L304,320L304,304L288,304z"
-      android:fillColor="#684e1e"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M192,320L192,336L208,336L208,320L192,320M304,320L304,336L320,336L320,320L304,320M176,336L176,352L192,352L192,336L176,336M320,336L320,352L336,352L336,336L320,336z"
-      android:fillColor="#896727"
-      android:strokeColor="#00000000"/>
-</vector>
index ace457e0a422932704581b8a71906576aede191f..2b9989e2f36065b18a963cddb7949f925f0eef97 100644 (file)
Binary files a/android/res/drawable-hdpi/ic_launcher.png and b/android/res/drawable-hdpi/ic_launcher.png differ
index ed098264906ed7bf7212f59eca7fa028187390fa..465726826838795e7fb4e91058c10887b90609d2 100644 (file)
Binary files a/android/res/drawable-mdpi/ic_launcher.png and b/android/res/drawable-mdpi/ic_launcher.png differ
index 190d16dfa68d6e5c0bebfd299121e381622fd63e..739b6ce82745dd172c6ed26db009b0a2a79d1203 100644 (file)
Binary files a/android/res/drawable-xhdpi/ic_launcher.png and b/android/res/drawable-xhdpi/ic_launcher.png differ
index b58006bd4b9e4d0a80dbf1086265c7f0ef48ead2..1c1aa9054a0fa6751dffc61f94391636f10ab060 100644 (file)
Binary files a/android/res/drawable-xxhdpi/ic_launcher.png and b/android/res/drawable-xxhdpi/ic_launcher.png differ
index a4b677542ccf2652c3838fa5298e00a8ae49f7fc..0f64585d54d6c0403a2e48f6a08bb4258563e536 100644 (file)
Binary files a/android/res/drawable-xxxhdpi/ic_launcher.png and b/android/res/drawable-xxxhdpi/ic_launcher.png differ
diff --git a/android/res/drawable/ic_launcher_background.xml b/android/res/drawable/ic_launcher_background.xml
new file mode 100644 (file)
index 0000000..bae566f
--- /dev/null
@@ -0,0 +1,54 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="16"
+    android:viewportHeight="16">
+  <path
+      android:pathData="m7,0v1h1L8,0ZM13,0v1h1,1L15,0L14,0ZM0,1L0,2 0,3L1,3L1,2 1,1ZM9,1L9,2h1L10,1ZM10,2v1h1v-1zM6,2v1h1v1h1v-1,-1h-1zM12,2v1,1h1v-1h1L14,2L13,2ZM0,4L0,5L1,5L1,4ZM4,4v1h1v1h1L6,5 6,4h-1zM9,4v1,1h1L10,5 10,4ZM4,7v1h1v1h1L6,8 6,7h-1zM8,7v1h-1v1h0.004,0.996 0.004,0.996L9,8 9,7ZM14,7v1,1h1L15,8 15,7ZM11,10v1,1h1L12,11 12,10ZM14,10v1,1h1L15,11 15,10ZM8,12v1h-1v1h1,1L9,13 9,12ZM3,13v1,1h1,1L5,14L4,14L4,13ZM11,14v1h1v-1zM7,15v1h1L8,15ZM4,11v1h1v-1zM2,11v1h1L3,11ZM1,10L1,11L2,11L2,10ZM2,10v1h1L3,10Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#938983"/>
+  <path
+      android:pathData="M1,0L1,1L2,1L2,2L1,2v1L0,3v1L1,4L1,5L0,5v1,1L1,7L1,6L2,6L2,5 2,4 2,3h1L3,2 3,1 3,0L2,0ZM2,6v1,1h1v-1h1,1 1L6,6L5,6 4,6 3,6ZM4,1v1,1 1h1,1 1L7,3L6,3 5,3L5,2 5,1ZM10,1L10,2L11,2L11,1ZM10,2h-1v1,1h1,1L11,3L10,3ZM9,4L8,4L8,5L9,5ZM12,1L12,2h1,1 1L15,1L14,1 13,1ZM13,3v1h-1v1h1,0.996L13.996,4h0.004L14,3ZM7.002,6v1h0.998,1v1h1L10,7 10,6L9,6 8,6ZM10.998,6v1h0.002v1h1v-1h-0.002L11.998,6h-0.998zM12.998,6v1,1h0.002,0.998L13.998,7 13.998,6ZM14,6v1h1L15,6ZM15,7v1,1h-1v1h1,1v-1h-0.004L15.996,8h0.004L16,7ZM4,8v1,1h1,1L6,9L5,9L5,8ZM1,9v1h1,1v-1h-1zM7.004,9v1h-0.004v1h1,1L9,10h-0.996v-1h-0.004zM12,9v1,1L13,11L13,10 13,9ZM1,11v1,1 1L2,14L2,13h1,1 1L5,12L4,12 3,12 2,12v-1zM15,11v1h-1v1h1,1v-1,-1zM7,12v1h1v-1zM11,12v1,1h1,1L13,13h-1v-1zM11,14L10,14v1,1h1,1v-1h-1zM7,14v1h1v1L9,16L9,15 9,14L8,14ZM14,14v1h1v1h1L16,15 16,14L15,14ZM3,15v1h1,1L5,15L4,15ZM6,0L6,1L7,1L7,0Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#87807c"/>
+  <path
+      android:pathData="M1,1L1,2L2,2L2,1ZM8,5v1h1v-1zM7,7v1h1v-1zM15,10v1h1L16,10ZM12,11v1h1v-1zM6,15v1L7,16L7,15ZM4,13v1L5,14L5,13Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#a59790"/>
+  <path
+      android:pathData="m7,4v1h1L8,4ZM7,5v1h1L8,5ZM0,0L0,1L1,1L1,0ZM4,0L4,1L5,1L5,2h1,1 1v1,1L9,4L9,3 9,2 9,1h1,1L11,0L10,0 9,0 8,0L8,1L7,1 6,1L6,0L5,0ZM15,0v1,1 1,1 1h0.002v1h-0.002v1h1L16,6h0.002v-1h-0.002L16,4 16,3 16,2 16,1 16,0ZM11,3v1L10,4v1h1v1h0.998,0.002v1,1h-1v-1h-0.002,-0.998v1h-1v1h0.002v1,1h0.998,0.002L10.002,10h0.998,1v-1h1L13,8h-0.002L12.998,7 12.998,6h1v-1h-0.002,-0.996 -1L12,4 12,3ZM9,9L8.004,9v1L9,10ZM3,7v1,1 1,1 1h1v-1h1v1,1 1,1 1h1L6,15L7,15L7,14 7,13L6,13L6,12 6,11L7,11L7,10 7,9 7,8 7,7L6,7v1,1 1L5,10 4,10L4,9 4,8 4,7ZM0,11v1,1 1,1 1L1,16 2,16L2,15L1,15L1,14 1,13 1,12 1,11ZM8,11v1h1v-1zM13,11v1h-1v1h1v1h-1v1,1h1,1 1v-1h-1v-1h1,1L16,13h-1,-1v-1,-1zM10,12v1,1L11,14L11,13 11,12ZM2,13v1h1L3,13ZM9,15v1h1L10,15ZM4,5v1h1v-1zM3,5v1h1v-1zM2,5v1h1v-1zM2,4v1h1L3,4ZM0,7v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#7e7471"/>
+  <path
+      android:pathData="M3,1L3,2h1L4,1ZM13.996,4v1h0.004,0.996L14.996,4L14,4ZM6,5v1L7,6L7,5ZM1,8L1,9L2,9L2,8ZM1,9L0,9v1L1,10ZM9,13v1h1L10,13Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#6b6161"/>
+  <path
+      android:pathData="m3,0v1h1L4,0ZM11,0v1,1 1h1L12,2 12,1L13,1L13,0L12,0ZM3,2v1h-1v1h1v1h1L4,4 4,3 4,2ZM5,2v1h1v-1zM14,2v1,1h0.996,0.004L15,3 15,2ZM6,4L6,5L7,5L7,4ZM10,5v1,1L10.998,7L10.998,6h0.002v-1zM14,5v1h1v-1h-0.004zM1,6v1,1L2,8L2,7 2,6ZM2,8v1h1L3,8ZM1,8L0,8L0,9L1,9ZM6,6L6,7L7,7L7,6ZM13,8v1,1 1h1L14,10h-0.002L13.998,9 13.998,8ZM0,10L0,11L1,11L1,10ZM10.002,10v1h-0.002,-0.998 -0.002v1,1h1L10,12L11,12L11,11 11,10ZM6,11v1,1h1v-1h1v-1h-1zM1,14v1L2,15v1h1v-1,-1h-1zM9,14v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#706c66"/>
+  <path
+      android:pathData="m7,0v1h1L8,0ZM13,0v1h1,1L15,0L14,0ZM0,1L0,2 0,3L1,3L1,2 1,1ZM9,1L9,2h1L10,1ZM10,2v1h1v-1zM6,2v1h1v1h1v-1,-1h-1zM12,2v1,1h1v-1h1L14,2L13,2ZM0,4L0,5L1,5L1,4ZM4,4v1h1v1h1L6,5 6,4h-1zM9,4v1,1h1L10,5 10,4ZM4,7v1h1v1h1L6,8 6,7h-1zM8,7v1h-1v1h0.004,0.996 0.004,0.996L9,8 9,7ZM14,7v1,1h1L15,8 15,7ZM11,10v1,1h1L12,11 12,10ZM14,10v1,1h1L15,11 15,10ZM8,12v1h-1v1h1,1L9,13 9,12ZM3,13v1,1h1,1L5,14L4,14L4,13ZM11,14v1h1v-1zM7,15v1h1L8,15ZM4,11v1h1v-1zM2,11v1h1L3,11ZM1,10L1,11L2,11L2,10ZM2,10v1h1L3,10Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#938983"/>
+  <path
+      android:pathData="M1,0L1,1L2,1L2,2L1,2v1L0,3v1L1,4L1,5L0,5v1,1L1,7L1,6L2,6L2,5 2,4 2,3h1L3,2 3,1 3,0L2,0ZM2,6v1,1h1v-1h1,1 1L6,6L5,6 4,6 3,6ZM4,1v1,1 1h1,1 1L7,3L6,3 5,3L5,2 5,1ZM10,1L10,2L11,2L11,1ZM10,2h-1v1,1h1,1L11,3L10,3ZM9,4L8,4L8,5L9,5ZM12,1L12,2h1,1 1L15,1L14,1 13,1ZM13,3v1h-1v1h1,0.996L13.996,4h0.004L14,3ZM7.002,6v1h0.998,1v1h1L10,7 10,6L9,6 8,6ZM10.998,6v1h0.002v1h1v-1h-0.002L11.998,6h-0.998zM12.998,6v1,1h0.002,0.998L13.998,7 13.998,6ZM14,6v1h1L15,6ZM15,7v1,1h-1v1h1,1v-1h-0.004L15.996,8h0.004L16,7ZM4,8v1,1h1,1L6,9L5,9L5,8ZM1,9v1h1,1v-1h-1zM7.004,9v1h-0.004v1h1,1L9,10h-0.996v-1h-0.004zM12,9v1,1L13,11L13,10 13,9ZM1,11v1,1 1L2,14L2,13h1,1 1L5,12L4,12 3,12 2,12v-1zM15,11v1h-1v1h1,1v-1,-1zM7,12v1h1v-1zM11,12v1,1h1,1L13,13h-1v-1zM11,14L10,14v1,1h1,1v-1h-1zM7,14v1h1v1L9,16L9,15 9,14L8,14ZM14,14v1h1v1h1L16,15 16,14L15,14ZM3,15v1h1,1L5,15L4,15ZM6,0L6,1L7,1L7,0Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#87807c"/>
+  <path
+      android:pathData="M1,1L1,2L2,2L2,1ZM8,5v1h1v-1zM7,7v1h1v-1zM15,10v1h1L16,10ZM12,11v1h1v-1zM6,15v1L7,16L7,15ZM4,13v1L5,14L5,13Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#a59790"/>
+  <path
+      android:pathData="m7,4v1h1L8,4ZM7,5v1h1L8,5ZM0,0L0,1L1,1L1,0ZM4,0L4,1L5,1L5,2h1,1 1v1,1L9,4L9,3 9,2 9,1h1,1L11,0L10,0 9,0 8,0L8,1L7,1 6,1L6,0L5,0ZM15,0v1,1 1,1 1h0.002v1h-0.002v1h1L16,6h0.002v-1h-0.002L16,4 16,3 16,2 16,1 16,0ZM11,3v1L10,4v1h1v1h0.998,0.002v1,1h-1v-1h-0.002,-0.998v1h-1v1h0.002v1,1h0.998,0.002L10.002,10h0.998,1v-1h1L13,8h-0.002L12.998,7 12.998,6h1v-1h-0.002,-0.996 -1L12,4 12,3ZM9,9L8.004,9v1L9,10ZM3,7v1,1 1,1 1h1v-1h1v1,1 1,1 1h1L6,15L7,15L7,14 7,13L6,13L6,12 6,11L7,11L7,10 7,9 7,8 7,7L6,7v1,1 1L5,10 4,10L4,9 4,8 4,7ZM0,11v1,1 1,1 1L1,16 2,16L2,15L1,15L1,14 1,13 1,12 1,11ZM8,11v1h1v-1zM13,11v1h-1v1h1v1h-1v1,1h1,1 1v-1h-1v-1h1,1L16,13h-1,-1v-1,-1zM10,12v1,1L11,14L11,13 11,12ZM2,13v1h1L3,13ZM9,15v1h1L10,15ZM4,5v1h1v-1zM3,5v1h1v-1zM2,5v1h1v-1zM2,4v1h1L3,4ZM0,7v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#7e7471"/>
+  <path
+      android:pathData="M3,1L3,2h1L4,1ZM13.996,4v1h0.004,0.996L14.996,4L14,4ZM6,5v1L7,6L7,5ZM1,8L1,9L2,9L2,8ZM1,9L0,9v1L1,10ZM9,13v1h1L10,13Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#6b6161"/>
+  <path
+      android:pathData="m3,0v1h1L4,0ZM11,0v1,1 1h1L12,2 12,1L13,1L13,0L12,0ZM3,2v1h-1v1h1v1h1L4,4 4,3 4,2ZM5,2v1h1v-1zM14,2v1,1h0.996,0.004L15,3 15,2ZM6,4L6,5L7,5L7,4ZM10,5v1,1L10.998,7L10.998,6h0.002v-1zM14,5v1h1v-1h-0.004zM1,6v1,1L2,8L2,7 2,6ZM2,8v1h1L3,8ZM1,8L0,8L0,9L1,9ZM6,6L6,7L7,7L7,6ZM13,8v1,1 1h1L14,10h-0.002L13.998,9 13.998,8ZM0,10L0,11L1,11L1,10ZM10.002,10v1h-0.002,-0.998 -0.002v1,1h1L10,12L11,12L11,11 11,10ZM6,11v1,1h1v-1h1v-1h-1zM1,14v1L2,15v1h1v-1,-1h-1zM9,14v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#706c66"/>
+</vector>
diff --git a/android/res/drawable/ic_launcher_foreground.xml b/android/res/drawable/ic_launcher_foreground.xml
new file mode 100644 (file)
index 0000000..a30acde
--- /dev/null
@@ -0,0 +1,126 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+  <path
+      android:pathData="m22,10v1h1v-1zM22,11h-1v1h1zM21,12h-1v1h1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#457d9e"/>
+  <path
+      android:pathData="m20,9v1h1L21,9ZM20,10h-1v1h1zM19,11h-1v1h1zM23,12v1h1v-1zM23,13h-1v1h1zM22,14h-1v1h1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#dbeefc"/>
+  <path
+      android:pathData="m23,11v1h1v-1zM21,12v1,1h1v-1,-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#77cefb"/>
+  <path
+      android:pathData="m20,10v1h1v-1zM20,11h-1v1,1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5397c1"/>
+  <path
+      android:pathData="m21,9v1h1L22,9ZM22,11v1,1h1v-1,-1zM18,12v1h1v-1zM20,14v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#a8e2ff"/>
+  <path
+      android:pathData="m22,9v1h1v1h1L24,10 24,9h-1zM22,10h-1v1h1zM21,11h-1v1h1zM20,13v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5faed8"/>
+  <path
+      android:pathData="m17,15v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+  <path
+      android:pathData="m16,15v1h1v-1zM14,17v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5d493c"/>
+  <path
+      android:pathData="m14,18v1h1v-1zM14,19h-1v1h1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#715a47"/>
+  <path
+      android:pathData="m12,20v1h1v-1zM12,21L11,21v1L12,22ZM11,22h-1v1h1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+  <path
+      android:pathData="m15,18v1h1v-1zM15,19h-1v1h1zM14,20h-1v1h1zM13,21L12,21v1h1zM12,22L11,22v1L12,23Z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#41352b"/>
+  <path
+      android:pathData="m17,14v1h1v-1zM18,15v1h1v-1zM18,16h-1v1h1zM13,18v1h1v-1zM13,19L12,19v1h1zM12,20L11,20v1L12,21ZM11,21h-1v1h1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m18,13v1h1v-1zM19,14v1h1v-1zM10,23v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#2c2c2c"/>
+  <path
+      android:pathData="m19,13v1h1v-1zM19,14h-1v1h1zM9,23v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#494949"/>
+  <path
+      android:pathData="m9,22v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#3d3d3d"/>
+  <path
+      android:pathData="m9,10v1h1v-1zM10,11v1h1v-1zM11,12v1L12,13v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#457d9e"/>
+  <path
+      android:pathData="M11,9L11,10L12,10L12,9ZM12,10v1h1v-1zM13,11v1h1v-1zM8,12v1h1v-1zM9,13v1h1v-1zM10,14v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#dbeefc"/>
+  <path
+      android:pathData="m8,11v1h1v-1zM10,12v1,1h1v-1,-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#77cefb"/>
+  <path
+      android:pathData="m11,10v1L12,11v-1zM12,11v1,1h1v-1,-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5397c1"/>
+  <path
+      android:pathData="m10,9v1h1L11,9ZM9,11v1,1h1v-1,-1zM13,12v1h1v-1zM11,14v1L12,15v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#a8e2ff"/>
+  <path
+      android:pathData="m8,9v1,1h1v-1h1L10,9h-1zM10,10v1h1v-1zM11,11v1L12,12v-1zM11,13v1L12,14v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5faed8"/>
+  <path
+      android:pathData="m14,15v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+  <path
+      android:pathData="m15,15v1h1v-1zM16,16v1h1v-1zM17,17v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#5d493c"/>
+  <path
+      android:pathData="m15,16v1h1v-1zM16,17v1h1v-1zM17,18v1h1v-1zM18,19v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#715a47"/>
+  <path
+      android:pathData="m19,20v1h1v-1zM20,21v1h1v-1zM21,22v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#675244"/>
+  <path
+      android:pathData="m16,18v1h1v-1zM17,19v1h1v-1zM18,20v1h1v-1zM19,21v1h1v-1zM20,22v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#41352b"/>
+  <path
+      android:pathData="m14,14v1h1v-1zM14,15h-1v1h1zM14,16v1h1v-1zM15,17v1h1v-1zM18,18v1h1v-1zM19,19v1h1v-1zM20,20v1h1v-1zM21,21v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#514035"/>
+  <path
+      android:pathData="m13,13v1h1v-1zM13,14L12,14v1h1zM21,23v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#2c2c2c"/>
+  <path
+      android:pathData="m12,13v1h1v-1zM13,14v1h1v-1zM22,23v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#494949"/>
+  <path
+      android:pathData="m22,22v1h1v-1z"
+      android:strokeWidth="0.264583"
+      android:fillColor="#3d3d3d"/>
+</vector>
index e7aad50df8c3df8deab19206bf22d5fee456f3ef..714c8df376cb75a903808730a66c037efd4a6edb 100644 (file)
@@ -18,8 +18,8 @@ public class AndroidLauncher extends AndroidApplication {
             e.printStackTrace();
             exit();
         }
-        CaveGame caveGame = new CaveGame(gameFolder, true, null);
-        caveGame.setDebug(BuildConfig.DEBUG);
+        CaveGame caveGame = new CaveGame(gameFolder, true, BuildConfig.DEBUG,
+                new AndroidPreferencesStore(getApplicationContext()));
         initialize(caveGame, config);
     }
 
diff --git a/android/src/ru/deadsoftware/cavedroid/AndroidPreferencesStore.kt b/android/src/ru/deadsoftware/cavedroid/AndroidPreferencesStore.kt
new file mode 100644 (file)
index 0000000..4b16659
--- /dev/null
@@ -0,0 +1,26 @@
+package ru.deadsoftware.cavedroid
+
+import android.content.Context
+import ru.deadsoftware.cavedroid.prefs.PreferencesStore
+
+class AndroidPreferencesStore(
+    private val context: Context
+) : PreferencesStore {
+
+    private val sharedPreferences by lazy { context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) }
+
+    override fun getPreference(key: String): String? {
+        return sharedPreferences.getString(key, null)
+    }
+
+    override fun setPreference(key: String, value: String?) {
+        with(sharedPreferences.edit()) {
+            putString(key, value)
+            apply()
+        }
+    }
+
+    private companion object {
+        private const val SHARED_PREFS_NAME = "cavedroid_prefs"
+    }
+}
\ No newline at end of file
index dddba92f5feeb17f84c52887f85a19ccd68f6fcc..60a1bb36473273a246bf966c6b5d46c0122dd6e7 100644 (file)
@@ -1,12 +1,18 @@
 buildscript {
-
     ext {
         appName = "CaveDroid"
+
         gdxVersion = '1.12.0'
+
         guavaVersion = '28.1'
+
         daggerVersion = '2.51.1'
-        kotlinVersion = '1.9.23'
+
+        kotlinVersion = '1.9.24'
+        kspVersion = '1.9.24-1.0.20'
         kotlinSerializationVersion = '1.6.3'
+
+        kotlinpoetKspVersion = '1.16.0'
     }
 
     repositories {
@@ -24,8 +30,7 @@ buildscript {
 }
 
 allprojects {
-
-    version = 'alpha0.6.2'
+    version = 'alpha0.9.2'
 
     repositories {
         mavenLocal()
@@ -34,51 +39,6 @@ allprojects {
         maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
         maven { url "https://oss.sonatype.org/content/repositories/releases/" }
         maven { url "https://jitpack.io" }
+        maven { url "https://mvn.fredboy.ru/releases/" }
     }
 }
-
-project(":desktop") {
-    apply plugin: "java-library"
-
-    dependencies {
-        implementation project(":core")
-        implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion"
-        api "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
-        api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
-    }
-}
-
-project(":android") {
-    apply plugin: "android"
-
-    configurations { natives }
-
-    dependencies {
-        implementation project(":core")
-        implementation platform("org.jetbrains.kotlin:kotlin-bom:1.9.23")
-        api "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
-        natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
-        natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
-        natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
-        natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
-
-        configurations.implementation {
-            exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
-        }
-    }
-}
-
-project(":core") {
-    apply plugin: "java-library"
-
-
-    dependencies {
-        api "com.badlogicgames.gdx:gdx:$gdxVersion"
-        api "com.google.guava:guava:$guavaVersion-android"
-        api "com.google.dagger:dagger:$daggerVersion"
-        implementation 'org.jetbrains:annotations:23.1.0'
-        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
-        implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion"
-        annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
-    }
-}
\ No newline at end of file
index cdf2ee7e4708ffec3ab49447cc63298a1f77caab..bd932c35eec2bad922d71a23a6bb5e46ef337863 100644 (file)
@@ -1,12 +1,26 @@
 plugins {
+    id "java-library"
     id "org.jetbrains.kotlin.jvm"
     id "kotlin"
     id "idea"
     id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlinVersion"
+    id 'com.google.devtools.ksp' version "$kspVersion"
 }
 
-sourceCompatibility = 17
+java.targetCompatibility = JavaVersion.VERSION_17
+java.sourceCompatibility = JavaVersion.VERSION_17
 
-sourceSets.main.java.srcDirs = [ "src/" ]
+sourceSets.main.java.srcDirs = ["src/"]
 
-java.targetCompatibility = JavaVersion.VERSION_17
\ No newline at end of file
+dependencies {
+    implementation "ru.fredboy:automultibind-annotations:1.0.0"
+    ksp "ru.fredboy:automultibind-ksp:1.0.0"
+
+    api "com.badlogicgames.gdx:gdx:$gdxVersion"
+    api "com.google.dagger:dagger:$daggerVersion"
+    implementation 'org.jetbrains:annotations:23.1.0'
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
+    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion"
+    implementation "org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinSerializationVersion"
+    ksp "com.google.dagger:dagger-compiler:$daggerVersion"
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/CaveGame.java b/core/src/ru/deadsoftware/cavedroid/CaveGame.java
deleted file mode 100644 (file)
index 3960176..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-package ru.deadsoftware.cavedroid;
-
-import com.badlogic.gdx.Application;
-import com.badlogic.gdx.Game;
-import com.badlogic.gdx.Gdx;
-import ru.deadsoftware.cavedroid.game.GameScreen;
-import ru.deadsoftware.cavedroid.misc.Assets;
-import ru.deadsoftware.cavedroid.misc.utils.AssetLoader;
-
-import javax.annotation.Nullable;
-
-public class CaveGame extends Game {
-
-    private static final String TAG = "CaveGame";
-
-    public static final String VERSION = "alpha 0.6.2";
-
-    private final MainConfig mMainConfig;
-    private final MainComponent mMainComponent;
-    private final AssetLoader mAssetLoader;
-
-    private final String mGameFolder;
-    private final boolean mTouch;
-    private boolean mDebug;
-
-    @Nullable
-    private final String mAssetsPackPath;
-
-    public CaveGame(String gameFolder, boolean touch, @Nullable String assetsPackPath) {
-        mGameFolder = gameFolder;
-        mTouch = touch;
-        mAssetsPackPath = assetsPackPath;
-
-        mMainComponent = DaggerMainComponent.builder().caveGame(this).build();
-
-        mMainConfig = mMainComponent.getMainConfig();
-        mAssetLoader = mMainComponent.getAssetLoader();
-    }
-
-    public void setDebug(boolean debug) {
-        mDebug = debug;
-    }
-
-    private void initConfig() {
-        int width = mTouch ? 320 : 480;
-        int height = (int) (width * ((float) Gdx.graphics.getHeight() / Gdx.graphics.getWidth()));
-
-        mMainConfig.setMainComponent(mMainComponent);
-        mMainConfig.setGameFolder(mGameFolder);
-        mMainConfig.setTouch(mTouch);
-        mMainConfig.setWidth(width);
-        mMainConfig.setHeight(height);
-        mMainConfig.setShowInfo(mDebug);
-        mMainConfig.setAssetsPackPath(mAssetsPackPath);
-
-        if (mDebug) {
-            Gdx.app.setLogLevel(Application.LOG_DEBUG);
-        } else {
-            Gdx.app.setLogLevel(Application.LOG_ERROR);
-        }
-    }
-
-    public void newGame(int gameMode) {
-        GameScreen gameScreen = mMainComponent.getGameScreen();
-        gameScreen.newGame(gameMode);
-        setScreen(gameScreen);
-    }
-
-    public void loadGame() {
-        GameScreen gameScreen = mMainComponent.getGameScreen();
-        gameScreen.loadGame();
-        setScreen(gameScreen);
-    }
-
-    public void quitGame() {
-        if (screen != null) {
-            screen.dispose();
-        }
-        setScreen(mMainComponent.getMenuScreen());
-    }
-
-    @Override
-    public void create() {
-        Gdx.files.absolute(mGameFolder).mkdirs();
-        initConfig();
-
-        Gdx.app.debug(TAG, mGameFolder);
-        Assets.load(mAssetLoader);
-        setScreen(mMainComponent.getMenuScreen());
-    }
-
-    @Override
-    public void dispose() {
-        if (screen != null) {
-            screen.dispose();
-        }
-        Assets.dispose();
-    }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/CaveGame.kt b/core/src/ru/deadsoftware/cavedroid/CaveGame.kt
new file mode 100644 (file)
index 0000000..225dd3a
--- /dev/null
@@ -0,0 +1,89 @@
+package ru.deadsoftware.cavedroid
+
+import com.badlogic.gdx.Application
+import com.badlogic.gdx.Game
+import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader
+import ru.deadsoftware.cavedroid.misc.utils.ratio
+import ru.deadsoftware.cavedroid.prefs.PreferencesStore
+
+class CaveGame(
+    private val gameDataDirectoryPath: String,
+    private val isTouchScreen: Boolean,
+    private val isDebug: Boolean,
+    private val preferencesStore: PreferencesStore,
+) : Game() {
+
+    private val mainComponent: MainComponent
+    private val mainConfig: MainConfig
+
+    private val assetLoader: AssetLoader
+
+    init {
+        mainComponent = DaggerMainComponent.builder()
+            .caveGame(this)
+            .preferencesStore(preferencesStore)
+            .build()
+
+        mainConfig = mainComponent.mainConfig
+        assetLoader = mainComponent.assetLoader
+    }
+
+    private fun initMainConfig() {
+        val width = DEFAULT_VIEWPORT_WIDTH
+        val height = width / Gdx.graphics.ratio
+
+        mainConfig.mainComponent = mainComponent
+        mainConfig.gameFolder = gameDataDirectoryPath
+        mainConfig.isTouch = isTouchScreen
+        mainConfig.width = width
+        mainConfig.height = height
+        mainConfig.isShowInfo = isDebug
+
+        Gdx.app.logLevel = if (isDebug) Application.LOG_DEBUG else Application.LOG_ERROR
+
+        mainConfig.setFullscreenToggleListener { isFullscreen ->
+            if (isFullscreen) {
+                Gdx.graphics.setFullscreenMode(Gdx.graphics.displayMode);
+            } else {
+                Gdx.graphics.setWindowedMode(width.toInt(), height.toInt());
+            }
+        }
+    }
+
+    fun newGame(gameMode: Int) {
+        setScreen(mainComponent.gameScreen.apply { newGame(gameMode) })
+    }
+
+    fun loadGame() {
+        setScreen(mainComponent.gameScreen.apply { loadGame() })
+    }
+
+    fun quitGame() {
+        screen?.dispose()
+        setScreen(mainComponent.menuScreen)
+    }
+
+    override fun create() {
+        Gdx.files.absolute(gameDataDirectoryPath).mkdirs()
+        initMainConfig()
+
+        Assets.load(assetLoader)
+        setScreen(mainComponent.menuScreen)
+    }
+
+    override fun dispose() {
+        screen?.dispose()
+        Assets.dispose()
+    }
+
+
+    companion object {
+        private const val TAG = "CaveGame"
+        private const val DEFAULT_VIEWPORT_WIDTH = 480f
+
+        const val VERSION = "alpha 0.9.2"
+    }
+
+}
\ No newline at end of file
index c640fb6c2abae2e5b5d12bc4574622419ab2e9b1..b487c6ddafef2901801f1cdd44855f46a45c8fe1 100644 (file)
@@ -4,11 +4,12 @@ import dagger.Component;
 import ru.deadsoftware.cavedroid.game.GameScreen;
 import ru.deadsoftware.cavedroid.menu.MenuScreen;
 import ru.deadsoftware.cavedroid.misc.utils.AssetLoader;
+import ru.deadsoftware.cavedroid.prefs.PreferencesStore;
 
 import javax.inject.Singleton;
 
 @Singleton
-@Component(dependencies = CaveGame.class)
+@Component(dependencies = {CaveGame.class, PreferencesStore.class})
 public interface MainComponent {
     GameScreen getGameScreen();
 
index 6577f297254f82928b2e18d28f29911d32764750..61cef0a3c858b356da7cdc7a6f850da6d86b2b02 100644 (file)
@@ -1,20 +1,31 @@
 package ru.deadsoftware.cavedroid;
 
+import org.jetbrains.annotations.Nullable;
 import ru.deadsoftware.cavedroid.game.GameUiWindow;
+import ru.deadsoftware.cavedroid.game.input.Joystick;
+import ru.deadsoftware.cavedroid.prefs.PreferencesStore;
 
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import java.util.HashMap;
 
 @Singleton
 public class MainConfig {
 
+    private final HashMap<String, String> mPreferencesCache = new HashMap<>();
+
+    @Nullable
+    private FullscreenToggleListener mFullscreenToggleListener = null;
+
     private final CaveGame mCaveGame;
+    private final PreferencesStore mPreferencesStore;
 
-    @CheckForNull
+    @Nullable
     private MainComponent mMainComponent;
 
+    @Nullable
+    private Joystick mJoystick;
+
     private GameUiWindow mGameUiWindow;
     private String mGameFolder;
 
@@ -29,8 +40,9 @@ public class MainConfig {
     private String mAssetsPackPath = null;
 
     @Inject
-    public MainConfig(CaveGame caveGame) {
+    public MainConfig(CaveGame caveGame, PreferencesStore preferencesStore) {
         mCaveGame = caveGame;
+        mPreferencesStore = preferencesStore;
 
         mGameUiWindow = GameUiWindow.NONE;
         mGameFolder = "";
@@ -113,4 +125,46 @@ public class MainConfig {
     public void setAssetsPackPath(@Nullable String assetsPackPath) {
         mAssetsPackPath = assetsPackPath;
     }
+
+    @Nullable
+    public Joystick getJoystick() {
+        return mJoystick;
+    }
+
+    public void setJoystick(@Nullable Joystick joystick) {
+        mJoystick = joystick;
+    }
+
+    @Nullable
+    public String getPreference(String key) {
+        if (mPreferencesCache.containsKey(key)) {
+            return mPreferencesCache.get(key);
+        }
+
+        String value = mPreferencesStore.getPreference(key);
+        mPreferencesCache.put(key, value);
+
+        return value;
+    }
+
+    public void setPreference(String key, String value) {
+        mPreferencesCache.put(key, value);
+        mPreferencesStore.setPreference(key, value);
+
+        if (mFullscreenToggleListener != null && key.equals("fullscreen")) {
+            mFullscreenToggleListener.onFullscreenToggled(Boolean.parseBoolean(value));
+        }
+    }
+
+    public void setFullscreenToggleListener(@Nullable FullscreenToggleListener fullscreenToggleListener) {
+        mFullscreenToggleListener = fullscreenToggleListener;
+    }
+
+    public boolean isUseDynamicCamera() {
+        return Boolean.parseBoolean(getPreference("dyncam"));
+    }
+
+    public interface FullscreenToggleListener {
+        void onFullscreenToggled(boolean value);
+    }
 }
index 2dafcd5e0346fb00a7d2fec91d132f7bce74a664..94d37d4be47f7808d7c8997c51cc98215a1f4743 100644 (file)
@@ -2,12 +2,7 @@ package ru.deadsoftware.cavedroid.game;
 
 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.UseItemActionsModule;
-import ru.deadsoftware.cavedroid.game.input.KeyboardInputHandlersModule;
-import ru.deadsoftware.cavedroid.game.input.MouseInputHandlersModule;
-import ru.deadsoftware.cavedroid.game.render.RenderModule;
+import ru.deadsoftware.cavedroid.generated.module.*;
 
 @GameScope
 @Component(dependencies = MainComponent.class,
@@ -17,7 +12,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..6c71d78c4fe7441f8ab3df12617ca2f013bf8019 100644 (file)
@@ -2,27 +2,30 @@ package ru.deadsoftware.cavedroid.game;
 
 import dagger.Module;
 import dagger.Provides;
+import org.jetbrains.annotations.Nullable;
 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.save.GameSaveData;
+import ru.deadsoftware.cavedroid.game.save.GameSaveLoader;
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
-import javax.annotation.CheckForNull;
-
 @Module
 public class GameModule {
 
-    @CheckForNull
-    private static GameSaver.Data data;
+    @Nullable
+    private static GameSaveData data;
 
     public static boolean loaded = false;
 
-    private static void load(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
+    private static void load(MainConfig mainConfig, GameItemsHolder gameItemsHolder, TooltipManager tooltipManager) {
         if (loaded) {
             return;
         }
-        data = GameSaver.load(mainConfig, gameItemsHolder);
+        data = GameSaveLoader.INSTANCE.load(mainConfig, gameItemsHolder, tooltipManager);
         loaded = true;
     }
 
@@ -34,8 +37,10 @@ public class GameModule {
 
     @Provides
     @GameScope
-    public static DropController provideDropController(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
-        load(mainConfig, gameItemsHolder);
+    public static DropController provideDropController(MainConfig mainConfig,
+                                                       GameItemsHolder gameItemsHolder,
+                                                       TooltipManager tooltipManager) {
+        load(mainConfig, gameItemsHolder, tooltipManager);
         DropController controller = data != null ? data.retrieveDropController() : new DropController();
         makeDataNullIfEmpty();
         controller.initDrops(gameItemsHolder);
@@ -44,11 +49,30 @@ public class GameModule {
 
     @Provides
     @GameScope
-    public static MobsController provideMobsController(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
-        load(mainConfig, gameItemsHolder);
-        MobsController controller = data != null ? data.retrieveMobsController() : new MobsController(gameItemsHolder);
+    public static ContainerController provideFurnaceController(MainConfig mainConfig,
+                                                               DropController dropController,
+                                                               GameItemsHolder gameItemsHolder,
+                                                               TooltipManager tooltipManager) {
+        load(mainConfig, gameItemsHolder, tooltipManager);
+        ContainerController controller = data != null
+                ? data.retrieveContainerController()
+                : new ContainerController(dropController, gameItemsHolder);
+        makeDataNullIfEmpty();
+        controller.init(dropController, gameItemsHolder);
+        return controller;
+    }
+
+    @Provides
+    @GameScope
+    public static MobsController provideMobsController(MainConfig mainConfig,
+                                                       GameItemsHolder gameItemsHolder,
+                                                       TooltipManager tooltipManager) {
+        load(mainConfig, gameItemsHolder, tooltipManager);
+        MobsController controller = data != null
+                ? data.retrieveMobsController()
+                : new MobsController(gameItemsHolder, tooltipManager);
         makeDataNullIfEmpty();
-        controller.getPlayer().initInventory(gameItemsHolder);
+        controller.getPlayer().initInventory(gameItemsHolder, tooltipManager);
         return controller;
     }
 
@@ -57,12 +81,14 @@ public class GameModule {
     public static GameWorld provideGameWorld(MainConfig mainConfig,
                                              DropController dropController,
                                              MobsController mobsController,
-                                             GameItemsHolder gameItemsHolder) {
-        load(mainConfig, gameItemsHolder);
+                                             GameItemsHolder gameItemsHolder,
+                                             ContainerController containerController,
+                                             TooltipManager tooltipManager) {
+        load(mainConfig, gameItemsHolder, tooltipManager);
         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..406ee346b461859f3d436d82ab8f155cf286c8ee 100644 (file)
@@ -4,16 +4,17 @@ import com.badlogic.gdx.math.Intersector;
 import com.badlogic.gdx.math.MathUtils;
 import com.badlogic.gdx.math.Rectangle;
 import com.badlogic.gdx.math.Vector2;
+import org.jetbrains.annotations.Nullable;
 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.model.item.InventoryItem;
+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;
 import javax.inject.Inject;
 import java.util.Iterator;
 
@@ -56,7 +57,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 +65,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
+     */
+    @Nullable
+    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 +91,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) {
@@ -109,10 +115,14 @@ public class GamePhysics {
     /**
      * @return Rectangle representing magneting target for this drop
      */
-    @CheckForNull
+    @Nullable
     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 +144,7 @@ public class GamePhysics {
         final Player player = mMobsController.getPlayer();
 
         if (Intersector.overlaps(shiftedPlayerTarget, drop)) {
-            player.pickUpDrop(drop);
+            player.inventory.pickDrop(drop);
         }
     }
 
@@ -158,11 +168,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 +181,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;
+        }
+
+        @Nullable 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 +204,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 +225,8 @@ public class GamePhysics {
     }
 
     private void mobYColl(Mob mob) {
-        if (checkColl(mob)) {
+        @Nullable final Rectangle collidingRect = checkColl(mob);
+        if (collidingRect != null) {
             int d = -1;
 
             if (mob.getVelocity().y < 0) {
@@ -222,17 +243,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,16 +355,24 @@ 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()) {
+                for (InventoryItem invItem : mob.getDrop(mGameItemsHolder)) {
+                    mDropController.addDrop(mob.x, mob.y, invItem);
+                }
+
                 it.remove();
             }
         }
 
         playerPhy(player, delta);
-        player.ai(mGameWorld, mGameItemsHolder, delta);
+        player.ai(mGameWorld, mGameItemsHolder, mMobsController, delta);
         if (player.isDead()) {
+            for (InventoryItem invItem : player.inventory.getItems()) {
+                mDropController.addDrop(player.x, player.y, invItem);
+            }
+            player.inventory.clear();
             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..e063e09af073cb21899d389e6613f7fae6bdcf34 100644 (file)
@@ -1,11 +1,18 @@
 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 org.jetbrains.annotations.Nullable;
 import ru.deadsoftware.cavedroid.MainConfig;
-import ru.deadsoftware.cavedroid.game.input.IGameInputHandler;
+import ru.deadsoftware.cavedroid.game.input.IKeyboardInputHandler;
+import ru.deadsoftware.cavedroid.game.input.IMouseInputHandler;
+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,14 +20,17 @@ 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;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -30,50 +40,159 @@ 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;
     private final KeyboardInputActionMapper mKeyboardInputActionMapper;
-    private final Set<IGameInputHandler<MouseInputAction>> mMouseInputHandlers;
-    private final Set<IGameInputHandler<KeyboardInputAction>> mKeyboardInputHandlers;
+    private final Set<IMouseInputHandler> mMouseInputHandlers;
+    private final Set<IKeyboardInputHandler> 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) {
+                 Set<IMouseInputHandler> mouseInputHandlers,
+                 Set<IKeyboardInputHandler> keyboardInputHandlers,
+                 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));
+        kotlin.collections.CollectionsKt.sortWith(mRenderers, new Comparator<IGameRenderer>() {
+            @Override
+            public int compare(IGameRenderer o1, IGameRenderer o2) {
+                return o1.getRenderLayer() - o2.getRenderLayer();
+            }
+        });
         mCursorMouseInputHandler = cursorMouseInputHandler;
         mMouseInputActionMapper = mouseInputActionMapper;
         mKeyboardInputActionMapper = keyboardInputActionMapper;
         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,23 +205,32 @@ 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) {
+    private boolean handleMouseAction(@Nullable MouseInputAction action) {
         if (action == null) {
             return false;
         }
 
         boolean anyProcessed = false;
 
-        for (IGameInputHandler<MouseInputAction> handler : mMouseInputHandlers) {
+        for (IMouseInputHandler handler : mMouseInputHandlers) {
             final boolean conditions = handler.checkConditions(action);
             if (conditions) {
                 anyProcessed = true;
@@ -114,9 +242,9 @@ public class GameRenderer extends Renderer {
         return anyProcessed;
     }
 
-    private boolean onMouseActionEvent(int mouseX, int mouseY, int button, boolean touchUp) {
-        @CheckForNull MouseInputAction action = mMouseInputActionMapper
-                .map((float) mouseX, (float) mouseY, getCameraViewport(), button, touchUp);
+    private boolean onMouseActionEvent(int mouseX, int mouseY, int button, boolean touchUp, int pointer) {
+        @Nullable MouseInputAction action = mMouseInputActionMapper
+                .map((float) mouseX, (float) mouseY, getCameraViewport(), button, touchUp, pointer);
         return handleMouseAction(action);
     }
 
@@ -125,16 +253,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 +281,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 +304,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,24 +318,24 @@ 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());
+        @Nullable MouseInputAction action =
+                mMouseInputActionMapper.mapDragged(screenX, screenY, getCameraViewport(), pointer);
         return handleMouseAction(action);
     }
 
     @Override
     public boolean scrolled(float amountX, float amountY) {
-        @CheckForNull MouseInputAction action = mMouseInputActionMapper
+        @Nullable MouseInputAction action = mMouseInputActionMapper
                 .mapScrolled(Gdx.input.getX(), Gdx.input.getY(), amountX, amountY, getCameraViewport());
         return handleMouseAction(action);
     }
 
     private boolean handleKeyboardAction(int keycode, boolean isKeyDown) {
-        @CheckForNull final KeyboardInputAction action = mKeyboardInputActionMapper
+        @Nullable final KeyboardInputAction action = mKeyboardInputActionMapper
                 .map(keycode, isKeyDown);
 
         if (action == null) {
@@ -201,7 +344,7 @@ public class GameRenderer extends Renderer {
 
         boolean anyProcessed = false;
 
-        for (IGameInputHandler<KeyboardInputAction> handler : mKeyboardInputHandlers) {
+        for (IKeyboardInputHandler handler : mKeyboardInputHandlers) {
             final boolean conditions = handler.checkConditions(action);
             if (conditions) {
                 anyProcessed = true;
@@ -225,15 +368,24 @@ 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));
+        for (IGameRenderer iGameRenderer : mRenderers) {
+            iGameRenderer.draw(spriter, shaper, getCameraViewport(), delta);
+        }
+        handleMousePosition();
         spriter.end();
+
     }
 
 }
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java b/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java
deleted file mode 100644 (file)
index cb52b3c..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-package ru.deadsoftware.cavedroid.game;
-
-import com.badlogic.gdx.Gdx;
-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.world.GameWorld;
-
-import javax.annotation.CheckForNull;
-import java.io.*;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map;
-
-public class GameSaver {
-
-    private static final String TAG = "GameSaver";
-
-    public static class Data {
-        @CheckForNull
-        private MobsController mMobsController;
-        @CheckForNull
-        private DropController mDropController;
-        @CheckForNull
-        private Block[][] mForeMap, mBackMap;
-
-        public Data(MobsController mobsController, DropController dropController, Block[][] foreMap, Block[][] backMap) {
-            mMobsController = mobsController;
-            mDropController = dropController;
-            mForeMap = foreMap;
-            mBackMap = backMap;
-        }
-
-        public MobsController retrieveMobsController() {
-            assert mMobsController != null;
-            MobsController mobsController = mMobsController;
-            mMobsController = null;
-            return mobsController;
-        }
-
-        public DropController retrieveDropController() {
-            assert mDropController != null;
-            DropController dropController = mDropController;
-            mDropController = null;
-            return dropController;
-        }
-
-        public Block[][] retrieveForeMap() {
-            assert mForeMap != null;
-            Block[][] foreMap = mForeMap;
-            mForeMap = null;
-            return foreMap;
-        }
-
-        public Block[][] retrieveBackMap() {
-            assert mBackMap != null;
-            Block[][] backMap = mBackMap;
-            mBackMap = null;
-            return backMap;
-        }
-
-        public boolean isEmpty() {
-            return mMobsController == null && mDropController == null && mForeMap == null && mBackMap == null;
-        }
-    }
-
-    private static final int SAVE_VERSION = 1;
-
-    private static byte[] intToBytes(int i) {
-        return ByteBuffer.allocate(4).putInt(i).array();
-    }
-
-    private static Map<String, Integer> buildBlocksDictionary(Block[][] foreMap, Block[][] backMap) {
-        final HashMap<String, Integer> dict = new HashMap<>();
-
-        int id = 0;
-        for (int i = 0; i < foreMap.length; i++) {
-            for (int j = 0; j < foreMap[i].length; j++) {
-                for (int k = 0; k < 2; k++) {
-                    final Block block = k == 0 ? foreMap[i][j] : backMap[i][j];
-                    final String key = block.getParams().getKey();
-                    if (!dict.containsKey(key)) {
-                        dict.put(key, id++);
-                    }
-                }
-            }
-        }
-
-        return dict;
-    }
-
-    private static void saveDict(FileHandle file, Map<String, Integer> dict) {
-        final String[] arr = new String[dict.size()];
-
-        for (Map.Entry<String, Integer> entry : dict.entrySet()) {
-            arr[entry.getValue()] = entry.getKey();
-        }
-
-        final StringBuilder builder = new StringBuilder();
-        for (String key : arr) {
-            builder.append(key);
-            builder.append('\n');
-        }
-
-        file.writeString(builder.toString(), false);
-    }
-
-    private static String[] loadDict(FileHandle file) {
-        return file.readString().split("\n");
-    }
-
-    private static void saveMap(FileHandle file, Block[][] map, Map<String, Integer> dict) throws IOException {
-        int run, block;
-        int width = map.length;
-        int height = map[0].length;
-
-        BufferedOutputStream out = new BufferedOutputStream(file.write(false));
-
-        out.write(SAVE_VERSION);
-        out.write(intToBytes(width));
-        out.write(intToBytes(height));
-
-        for (int y = 0; y < height; y++) {
-            block = dict.get(map[0][y].getParams().getKey());
-            run = 0;
-            for (Block[] blocks : map) {
-                int newValue = dict.get(blocks[y].getParams().getKey());
-                if (run >= 0xFF || newValue != block) {
-                    out.write(run);
-                    out.write(block);
-                    run = 0;
-                    block = dict.get(blocks[y].getParams().getKey());
-                }
-                run++;
-            }
-            out.write(run);
-            out.write(block);
-        }
-
-        out.flush();
-        out.close();
-    }
-
-    private static Block[][] loadMap(GameItemsHolder gameItemsHolder, FileHandle file, String[] dict) throws Exception {
-        Block[][] map;
-        int version, width, height;
-        int run, block;
-
-        DataInputStream in = new DataInputStream(file.read());
-
-        version = in.readByte();
-
-        if (SAVE_VERSION == version) {
-            width = in.readInt();
-            height = in.readInt();
-            map = new Block[width][height];
-            for (int y = 0; y < height; y++) {
-                for (int x = 0; x < width; x += run) {
-                    run = in.readUnsignedByte();
-                    block = in.readUnsignedByte();
-                    for (int i = x; i < x + run; i++) {
-                        map[i][y] = gameItemsHolder.getBlock(dict[block]);
-                    }
-                }
-            }
-        } else {
-            throw new Exception("version mismatch");
-        }
-
-        in.close();
-        return map;
-    }
-
-    @CheckForNull
-    public static Data load(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
-        String folder = mainConfig.getGameFolder();
-        FileHandle file = Gdx.files.absolute(folder + "/saves/game.sav");
-
-        try {
-            ObjectInputStream in = new ObjectInputStream(file.read());
-            int version = in.readInt();
-            DropController dropController;
-            MobsController mobsController;
-
-            if (SAVE_VERSION == version) {
-                dropController = (DropController) in.readObject();
-                mobsController = (MobsController) in.readObject();
-            } else {
-                throw new Exception("version mismatch");
-            }
-
-            in.close();
-
-            final String[] dict = loadDict(Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/dict"));
-            Block[][] foreMap = loadMap(gameItemsHolder, Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/foremap.sav"), dict);
-            Block[][] backMap = loadMap(gameItemsHolder, Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/backmap.sav"), dict);
-
-            if (dropController == null || mobsController == null) {
-                throw new Exception("couldn't load");
-            }
-
-            return new Data(mobsController, dropController, foreMap, backMap);
-        } catch (Exception e) {
-            Gdx.app.error("GameSaver", e.getMessage());
-        }
-
-        return null;
-    }
-
-    public static void save(MainConfig mainConfig,
-                            DropController dropController,
-                            MobsController mobsController,
-                            GameWorld gameWorld) {
-        String folder = mainConfig.getGameFolder();
-        FileHandle file = Gdx.files.absolute(folder + "/saves/");
-        file.mkdirs();
-        file = Gdx.files.absolute(folder + "/saves/game.sav");
-
-        final Block[][] foreMap, backMap;
-        foreMap = gameWorld.getFullForeMap();
-        backMap = gameWorld.getFullBackMap();
-
-        final Map<String, Integer> dict = buildBlocksDictionary(foreMap, backMap);
-
-        try {
-            ObjectOutputStream out = new ObjectOutputStream(file.write(false));
-            out.writeInt(SAVE_VERSION);
-            out.writeObject(dropController);
-            out.writeObject(mobsController);
-            out.close();
-
-            saveDict(Gdx.files.absolute(folder + "/saves/dict"), dict);
-            saveMap(Gdx.files.absolute(folder + "/saves/foremap.sav"), gameWorld.getFullForeMap(), dict);
-            saveMap(Gdx.files.absolute(folder + "/saves/backmap.sav"), gameWorld.getFullBackMap(), dict);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    public static boolean exists(MainConfig mainConfig) {
-        String folder = mainConfig.getGameFolder();
-        return (Gdx.files.absolute(folder + "/saves/game.sav").exists() &&
-                Gdx.files.absolute(folder + "/saves/foremap.sav").exists() &&
-                Gdx.files.absolute(folder + "/saves/backmap.sav").exists());
-    }
-}
index 749d7d4055beeee9964e8f52cb7466188b093a1e..d7546e98ad60758261240dfbd661425e92f90bc6 100644 (file)
@@ -1,10 +1,9 @@
 package ru.deadsoftware.cavedroid.game;
 
-import com.badlogic.gdx.Gdx;
 import com.badlogic.gdx.Screen;
+import org.jetbrains.annotations.Nullable;
 import ru.deadsoftware.cavedroid.MainConfig;
 
-import javax.annotation.CheckForNull;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
@@ -13,9 +12,9 @@ public class GameScreen implements Screen {
 
     private final MainConfig mMainConfig;
 
-    @CheckForNull
+    @Nullable
     private GameProc mGameProc;
-    @CheckForNull
+    @Nullable
     private GameItemsHolder mGameItemsHolder;
 
     @Inject
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/PlaceBlockActionsModule.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/PlaceBlockActionsModule.kt
deleted file mode 100644 (file)
index f2c5a23..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-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.placeblock.IPlaceBlockAction
-import ru.deadsoftware.cavedroid.game.actions.placeblock.PlaceBlockItemToBackgroundAction
-import ru.deadsoftware.cavedroid.game.actions.placeblock.PlaceBlockItemToForegroundAction
-import ru.deadsoftware.cavedroid.game.actions.placeblock.PlaceSlabAction
-
-@Module
-class PlaceBlockActionsModule {
-
-    @Binds
-    @IntoMap
-    @StringKey(PlaceBlockItemToForegroundAction.ACTION_KEY)
-    @GameScope
-    fun bindPlaceBlockItemToForegroundAction(action: PlaceBlockItemToForegroundAction): IPlaceBlockAction {
-        return action
-    }
-
-    @Binds
-    @IntoMap
-    @StringKey(PlaceBlockItemToBackgroundAction.ACTION_KEY)
-    @GameScope
-    fun bindPlaceBlockItemToBackgroundAction(action: PlaceBlockItemToBackgroundAction): IPlaceBlockAction {
-        return action
-    }
-
-    @Binds
-    @IntoMap
-    @StringKey(PlaceSlabAction.ACTION_KEY)
-    @GameScope
-    fun bindPlaceSlabAction(action: PlaceSlabAction): IPlaceBlockAction {
-        return action
-    }
-
-}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/UpdateBlockActionsModule.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/UpdateBlockActionsModule.kt
deleted file mode 100644 (file)
index 5dcfe0e..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-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.updateblock.*
-
-@Module
-class UpdateBlockActionsModule {
-
-    @Binds
-    @IntoMap
-    @StringKey(UpdateSandAction.BLOCK_KEY)
-    @GameScope
-    fun bindUpdateSandAction(action: UpdateSandAction): IUpdateBlockAction {
-        return action;
-    }
-
-    @Binds
-    @IntoMap
-    @StringKey(UpdateGravelAction.BLOCK_KEY)
-    @GameScope
-    fun bindUpdateGravelAction(action: UpdateGravelAction): IUpdateBlockAction {
-        return action;
-    }
-
-    @Binds
-    @IntoMap
-    @StringKey(UpdateRequiresBlockAction.ACTION_KEY)
-    @GameScope
-    fun bindUpdateRequiresBlockAction(action: UpdateRequiresBlockAction): IUpdateBlockAction {
-        return action;
-    }
-
-    @Binds
-    @IntoMap
-    @StringKey(UpdateGrassAction.BLOCK_KEY)
-    @GameScope
-    fun bindUpdateGrassAction(action: UpdateGrassAction): IUpdateBlockAction {
-        return action;
-    }
-
-    @Binds
-    @IntoMap
-    @StringKey(UpdateSnowedGrassAction.BLOCK_KEY)
-    @GameScope
-    fun bindUpdateSnowedGrassAction(action: UpdateSnowedGrassAction): IUpdateBlockAction {
-        return action;
-    }
-}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/UseItemActionsModule.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/UseItemActionsModule.kt
deleted file mode 100644 (file)
index 0206d5f..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-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.useitem.*
-
-@Module
-class UseItemActionsModule {
-
-    @Binds
-    @IntoMap
-    @StringKey(UseWaterBucketAction.ACTION_KEY)
-    @GameScope
-    fun bindUseWaterBucketAction(action: UseWaterBucketAction): IUseItemAction {
-        return action
-    }
-
-    @Binds
-    @IntoMap
-    @StringKey(UseLavaBucketAction.ACTION_KEY)
-    @GameScope
-    fun bindUseLavaBucketAction(action: UseLavaBucketAction): IUseItemAction {
-        return action
-    }
-
-    @Binds
-    @IntoMap
-    @StringKey(UseEmptyBucketAction.ACTION_KEY)
-    @GameScope
-    fun bindUseEmptyBucketAction(action: UseEmptyBucketAction): IUseItemAction {
-        return action
-    }
-
-}
index fdfb61a53008914d6ed9650c5931e204d58d677b..72583017e73e32dc4d6408e1dd216525d744508f 100644 (file)
@@ -5,9 +5,11 @@ 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindPlaceBlockAction
 import javax.inject.Inject
 
 @GameScope
+@BindPlaceBlockAction(stringKey = PlaceBlockItemToBackgroundAction.ACTION_KEY)
 class PlaceBlockItemToBackgroundAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val gameItemsHolder: GameItemsHolder,
index 22aafdd98e5c75a75aeb1a5fbc39c2317e5d5111..ec249fbc027561047a08993854b8a4d397a2b7f7 100644 (file)
@@ -5,9 +5,11 @@ 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindPlaceBlockAction
 import javax.inject.Inject
 
 @GameScope
+@BindPlaceBlockAction(stringKey = PlaceBlockItemToForegroundAction.ACTION_KEY)
 class PlaceBlockItemToForegroundAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val placeSlabAction: PlaceSlabAction,
index 6ac94c98edfafd03a86b188a79ab004cc01180ed..00c62d7d94b392eb6e769a18366489c1a1417a4e 100644 (file)
@@ -6,9 +6,11 @@ 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindPlaceBlockAction
 import javax.inject.Inject
 
 @GameScope
+@BindPlaceBlockAction(stringKey = PlaceSlabAction.ACTION_KEY)
 class PlaceSlabAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val mobsController: MobsController,
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..c23b80f
--- /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.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUpdateBlockAction
+import javax.inject.Inject
+
+@GameScope
+@BindUpdateBlockAction(stringKey = UpdateBedLeftAction.BLOCK_KEY)
+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..cd8b2d3
--- /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.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUpdateBlockAction
+import javax.inject.Inject
+
+@GameScope
+@BindUpdateBlockAction(stringKey = UpdateBedRightAction.BLOCK_KEY)
+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..476235a3dae9671dd240f057045364770ace0a3c 100644 (file)
@@ -3,9 +3,11 @@ package ru.deadsoftware.cavedroid.game.actions.updateblock
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUpdateBlockAction
 import javax.inject.Inject
 
 @GameScope
+@BindUpdateBlockAction(stringKey = UpdateGrassAction.BLOCK_KEY)
 class UpdateGrassAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val mGameItemsHolder: GameItemsHolder,
@@ -13,8 +15,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"))
         }
     }
 
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGravelAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGravelAction.kt
deleted file mode 100644 (file)
index 6cc026f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package ru.deadsoftware.cavedroid.game.actions.updateblock
-
-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 UpdateGravelAction @Inject constructor(
-    private val gameWorld: GameWorld,
-    private val mobsController: MobsController,
-) : IUpdateBlockAction {
-
-    override fun update(x: Int, y: Int) {
-        val shouldFall = gameWorld.getForeMap(x, y + 1).params.hasCollision.not()
-
-        if (shouldFall) {
-            gameWorld.resetForeMap(x, y)
-            FallingGravel(x * 16f, y * 16f)
-                .apply { attachToController(mobsController) }
-        }
-    }
-
-    companion object {
-        const val BLOCK_KEY = "gravel"
-    }
-}
\ No newline at end of file
index 57172a5f24a7ef53408e4516b5aa7e3f3bc583eb..53f56e3ff682181133244d3eb0f3098e782b4687 100644 (file)
@@ -1,17 +1,31 @@
 package ru.deadsoftware.cavedroid.game.actions.updateblock
 
 import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.FallingBlock
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUpdateBlockAction
+import ru.deadsoftware.cavedroid.misc.utils.px
 import javax.inject.Inject
 
 @GameScope
+@BindUpdateBlockAction(stringKey = UpdateRequiresBlockAction.ACTION_KEY)
 class UpdateRequiresBlockAction @Inject constructor(
     private val gameWorld: GameWorld,
+    private val mobsController: MobsController,
 ) : IUpdateBlockAction {
 
     override fun update(x: Int, y: Int) {
         if (gameWorld.getForeMap(x, y + 1).params.hasCollision.not()) {
-            gameWorld.destroyForeMap(x, y)
+            val block = gameWorld.getForeMap(x, y)
+
+            if (block.params.isFallable) {
+                gameWorld.resetForeMap(x, y)
+                FallingBlock(block.params.key, x.px, y.px)
+                    .attachToController(mobsController)
+            } else {
+                gameWorld.destroyForeMap(x, y)
+            }
         }
     }
 
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSandAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSandAction.kt
deleted file mode 100644 (file)
index 4a2b58e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package ru.deadsoftware.cavedroid.game.actions.updateblock
-
-import ru.deadsoftware.cavedroid.game.GameScope
-import ru.deadsoftware.cavedroid.game.mobs.FallingSand
-import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.world.GameWorld
-import javax.inject.Inject
-
-@GameScope
-class UpdateSandAction @Inject constructor(
-    private val gameWorld: GameWorld,
-    private val mobsController: MobsController,
-) : IUpdateBlockAction {
-
-    override fun update(x: Int, y: Int) {
-        val shouldFall = gameWorld.getForeMap(x, y + 1).params.hasCollision.not()
-
-        if (shouldFall) {
-            gameWorld.resetForeMap(x, y)
-            FallingSand(x * 16f, y * 16f)
-                .apply { attachToController(mobsController) }
-        }
-    }
-
-    companion object {
-        const val BLOCK_KEY = "sand"
-    }
-}
\ No newline at end of file
index 8b7f0e34ecc9be0e650590edb9a152115d45e670..2cf5a57b5918e856e0f0042bde266d15623155fe 100644 (file)
@@ -3,9 +3,11 @@ package ru.deadsoftware.cavedroid.game.actions.updateblock
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUpdateBlockAction
 import javax.inject.Inject
 
 @GameScope
+@BindUpdateBlockAction(stringKey = UpdateSnowedGrassAction.BLOCK_KEY)
 class UpdateSnowedGrassAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val mGameItemsHolder: GameItemsHolder,
@@ -13,8 +15,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..93684e7
--- /dev/null
@@ -0,0 +1,28 @@
+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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUseBlockAction
+import javax.inject.Inject
+
+@GameScope
+@BindUseBlockAction(stringKey = UseChestAction.KEY)
+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..6d6c1e9
--- /dev/null
@@ -0,0 +1,22 @@
+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.misc.annotations.multibinding.BindUseBlockAction
+import javax.inject.Inject
+
+@GameScope
+@BindUseBlockAction(stringKey = UseCraftingTableAction.KEY)
+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..0b8ba53
--- /dev/null
@@ -0,0 +1,25 @@
+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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUseBlockAction
+import javax.inject.Inject
+
+@GameScope
+@BindUseBlockAction(stringKey = UseFurnaceAction.KEY)
+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..f48115e
--- /dev/null
@@ -0,0 +1,33 @@
+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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUseItemAction
+import javax.inject.Inject
+
+@GameScope
+@BindUseItemAction(UseBedAction.ACTION_KEY)
+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..6992af7d9f1af61cfdf9a52892f7fd983b2d83d2 100644 (file)
@@ -6,9 +6,11 @@ import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.model.block.Block
 import ru.deadsoftware.cavedroid.game.model.item.Item
 import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUseItemAction
 import javax.inject.Inject
 
 @GameScope
+@BindUseItemAction(UseEmptyBucketAction.ACTION_KEY)
 class UseEmptyBucketAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val mobsController: MobsController,
@@ -22,6 +24,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")
index 965961711ee2ec08b44bf07961d8124322be7ea5..ec1e585dfeadc87c8351695c2ab493024ef9d472 100644 (file)
@@ -5,9 +5,11 @@ 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUseItemAction
 import javax.inject.Inject
 
 @GameScope
+@BindUseItemAction(UseLavaBucketAction.ACTION_KEY)
 class UseLavaBucketAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val mobsController: MobsController,
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..5eddc5e
--- /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.mobs.Pig
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUseItemAction
+import ru.deadsoftware.cavedroid.misc.utils.px
+import javax.inject.Inject
+
+@GameScope
+@BindUseItemAction(UsePigSpawnEggAction.ACTION_KEY)
+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 15e08fc77bd78962ec8fd6ccfd389a917cd66699..739ec2899335130d1b9daae1e3c2f14db5b8e991 100644 (file)
@@ -5,9 +5,11 @@ 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindUseItemAction
 import javax.inject.Inject
 
 @GameScope
+@BindUseItemAction(UseWaterBucketAction.ACTION_KEY)
 class UseWaterBucketAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val mobsController: MobsController,
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}"
         )
index a8eaad1af71c2837a5118a82d33243d43edfb0c1..2c5f118b4f87b73d14f8088a84339caea99860f0 100644 (file)
@@ -1,6 +1,12 @@
 package ru.deadsoftware.cavedroid.game.input
 
 import ru.deadsoftware.cavedroid.game.input.action.IGameInputAction
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+
+interface IKeyboardInputHandler : IGameInputHandler<KeyboardInputAction>
+
+interface IMouseInputHandler : IGameInputHandler<MouseInputAction>
 
 interface IGameInputHandler<A : IGameInputAction> {
 
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
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/KeyboardInputHandlersModule.kt b/core/src/ru/deadsoftware/cavedroid/game/input/KeyboardInputHandlersModule.kt
deleted file mode 100644 (file)
index f590398..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-package ru.deadsoftware.cavedroid.game.input
-
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.IntoSet
-import ru.deadsoftware.cavedroid.game.GameScope
-import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
-import ru.deadsoftware.cavedroid.game.input.handler.keyboard.*
-
-@Module
-object KeyboardInputHandlersModule {
-    
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindGoLeftKeyboardInputHandler(handler: GoLeftKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindGoRightKeyboardInputHandler(handler: GoRightKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindJumpKeyboardActionHandler(handler: JumpKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindFlyUpKeyboardActionHandler(handler: FlyUpKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindTurnOnFlyModeKeyboardActionHandler(handler: TurnOnFlyModeKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindFlyDownKeyboardActionHandler(handler: FlyDownKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindOpenInventoryKeyboardInputHandler(handler: OpenInventoryKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindCloseGameWindowKeyboardInputHandler(handler: CloseGameWindowKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindToggleDebugInfoKeyboardInputHandler(handler: ToggleDebugInfoKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindToggleMinimapKeyboardInputHandler(handler: ToggleMinimapKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindToggleGameModeKeyboardInputHandler(handler: ToggleGameModeKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindPauseGameKeyboardInputHandler(handler: PauseGameKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindToggleControlsModeKeyboardInputHandler(handler: ToggleControlsModeKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindMoveCursorControlsModeKeyboardInputHandler(handler: MoveCursorControlsModeKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindOpenCraftingKeyboardInputHandler(handler: OpenCraftingKeyboardInputHandler): IGameInputHandler<KeyboardInputAction> {
-        return handler
-    }
-
-}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/MouseInputHandlersModule.kt b/core/src/ru/deadsoftware/cavedroid/game/input/MouseInputHandlersModule.kt
deleted file mode 100644 (file)
index 937b3e6..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-package ru.deadsoftware.cavedroid.game.input
-
-import dagger.Binds
-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.mouse.*
-
-@Module
-object MouseInputHandlersModule {
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindCursorMouseInputHandler(handler: CursorMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindHoldHotbarMouseInputHandler(handler: HotbarMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindCloseGameWindowMouseActionHandler(handler: CloseGameWindowMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindCreativeInventoryScrollMouseInputHandler(handler: CreativeInventoryScrollMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindSelectCreativeInventoryItemMouseActionHandler(handler: SelectCreativeInventoryItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindAttackMouseInputHandler(handler: AttackMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindUseItemMouseInputActionHandler(handler: UseItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindSelectSurvivalInventoryItemMouseInputHandler(handler: SelectSurvivalInventoryItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindSelectCraftingInventoryItemMouseInputHandler(handler: SelectCraftingInventoryItemMouseInputHandler): IGameInputHandler<MouseInputAction> {
-        return handler
-    }
-}
\ No newline at end of file
index 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..8bf9b1c25d666408b1c3c1a70ac1e9f14bd9dd23 100644 (file)
@@ -2,24 +2,26 @@ package ru.deadsoftware.cavedroid.game.input.handler.keyboard
 
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.GameUiWindow
-import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.IKeyboardInputHandler
 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class CloseGameWindowKeyboardInputHandler @Inject constructor(
     private val gameWindowsManager: GameWindowsManager,
     private val mobsController: MobsController,
     private val dropController: DropController,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     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..e9c8eb0
--- /dev/null
@@ -0,0 +1,51 @@
+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.IKeyboardInputHandler
+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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
+import javax.inject.Inject
+
+@GameScope
+@BindKeyboardInputHandler
+class DropItemKeyboardInputHandler @Inject constructor(
+    private val gameWindowsManager: GameWindowsManager,
+    private val mobsController: MobsController,
+    private val dropController: DropController,
+) : IKeyboardInputHandler {
+
+    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..ae5f4c3c86473c2b0240ee4d41171557c2a76f76 100644 (file)
@@ -2,18 +2,20 @@ 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.IKeyboardInputHandler
 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.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class FlyDownKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return action.actionKey is KeyboardInputActionKey.Down &&
index 2c6efc90beb2dee1f4efaab5127ca7814312e6c6..444fe09f36ab6dcfd2fdceb71928905cce53d74d 100644 (file)
@@ -2,21 +2,24 @@ 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.IKeyboardInputHandler
 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.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class FlyUpKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     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..2b40d4634ad55a81fb62c416190b133a94f557c2 100644 (file)
@@ -2,19 +2,21 @@ 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.IKeyboardInputHandler
 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class GoLeftKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return action.actionKey is KeyboardInputActionKey.Left &&
index 8f8f0385cf3af9a9e8c8d07a4038c34241b15b7c..ec38218fea5c7d5aa511615d559f3dab2cdfc1f6 100644 (file)
@@ -2,19 +2,21 @@ 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.IKeyboardInputHandler
 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class GoRightKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return action.actionKey is KeyboardInputActionKey.Right &&
index 1fbc9fb76b02c7926d9d6881336db5d1465adc12..bcacbf578ebf6928bbb4e0e62c0c5fc03eb1c3b7 100644 (file)
@@ -2,21 +2,23 @@ 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.IKeyboardInputHandler
 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.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class JumpKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     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..84c5b900cc5d12fdc2d217a5eee0326e716340e7 100644 (file)
@@ -1,59 +1,45 @@
 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.IKeyboardInputHandler
 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class MoveCursorControlsModeKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
     private val gameWorld: GameWorld,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return mainConfig.isTouch &&
                 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 {
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt
deleted file mode 100644 (file)
index d2e333c..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-package ru.deadsoftware.cavedroid.game.input.handler.keyboard
-
-import ru.deadsoftware.cavedroid.game.GameScope
-import ru.deadsoftware.cavedroid.game.GameUiWindow
-import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
-import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
-import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
-import ru.deadsoftware.cavedroid.game.mobs.MobsController
-import ru.deadsoftware.cavedroid.game.objects.DropController
-import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
-import javax.inject.Inject
-
-@GameScope
-class OpenCraftingKeyboardInputHandler @Inject constructor(
-    private val gameWindowsManager: GameWindowsManager,
-) : IGameInputHandler<KeyboardInputAction> {
-
-    override fun checkConditions(action: KeyboardInputAction): Boolean {
-        return action.actionKey is KeyboardInputActionKey.OpenCraft &&
-                action.isKeyDown && gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
-    }
-
-    override fun handle(action: KeyboardInputAction) {
-        gameWindowsManager.openCrafting()
-    }
-}
\ No newline at end of file
index 0e2c11bd17a0adce97b6853cbe37965891b11fb0..70ae1585c75a9f5ed3f974ddbb147c8871481ecb 100644 (file)
@@ -2,22 +2,22 @@ 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.IKeyboardInputHandler
 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class OpenInventoryKeyboardInputHandler @Inject constructor(
     private val gameWindowsManager: GameWindowsManager,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     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..0311c8831fb22dd753d08db23564e9a8f5b19235 100644 (file)
@@ -1,30 +1,42 @@
 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.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.IKeyboardInputHandler
 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.save.GameSaveLoader
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
 import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class PauseGameKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val dropController: DropController,
     private val mobsController: MobsController,
     private val gameWorld: GameWorld,
-) : IGameInputHandler<KeyboardInputAction> {
+    private val containerController: ContainerController,
+    private val gameWindowsManager: GameWindowsManager,
+) : IKeyboardInputHandler {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return action.actionKey is KeyboardInputActionKey.Pause && action.isKeyDown
     }
 
     override fun handle(action: KeyboardInputAction) {
-        GameSaver.save(mainConfig, dropController, mobsController, gameWorld)
+        if (gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE) {
+            gameWindowsManager.closeWindow()
+            return
+        }
+
+        GameSaveLoader.save(mainConfig, dropController, mobsController, containerController, gameWorld)
         mainConfig.caveGame.quitGame()
     }
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/SelectHotbarSlotKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/SelectHotbarSlotKeyboardInputHandler.kt
new file mode 100644 (file)
index 0000000..d22cd94
--- /dev/null
@@ -0,0 +1,26 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IKeyboardInputHandler
+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.misc.annotations.multibinding.BindKeyboardInputHandler
+import javax.inject.Inject
+
+@GameScope
+@BindKeyboardInputHandler
+class SelectHotbarSlotKeyboardInputHandler @Inject constructor(
+    private val mobsController: MobsController,
+) : IKeyboardInputHandler {
+
+    override fun checkConditions(action: KeyboardInputAction): Boolean {
+        return action.actionKey is KeyboardInputActionKey.SelectHotbarSlot &&
+                action.isKeyDown
+    }
+
+    override fun handle(action: KeyboardInputAction) {
+        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..d8c5acc
--- /dev/null
@@ -0,0 +1,32 @@
+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.IKeyboardInputHandler
+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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
+import javax.inject.Inject
+
+@GameScope
+@BindKeyboardInputHandler
+class StopSwimKeyboardInputHandler @Inject constructor(
+    private val mainConfig: MainConfig,
+    private val mobsController: MobsController,
+    private val gameWorld: GameWorld,
+) : IKeyboardInputHandler {
+
+    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..20cc82e
--- /dev/null
@@ -0,0 +1,38 @@
+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.IKeyboardInputHandler
+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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
+import javax.inject.Inject
+
+@GameScope
+@BindKeyboardInputHandler
+class SwimUpKeyboardInputHandler @Inject constructor(
+    private val mainConfig: MainConfig,
+    private val mobsController: MobsController,
+    private val gameWorld: GameWorld,
+) : IKeyboardInputHandler {
+
+    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..f8e0177faddbc6239b91f1f564a5d9061ebba94b 100644 (file)
@@ -2,21 +2,23 @@ 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.IKeyboardInputHandler
 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.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class ToggleControlsModeKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     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 96bc21d9719433bf027bb7ec96b75cb859964d9e..7cf08ed10656cf8c531bb63831146d3d0614c193 100644 (file)
@@ -2,15 +2,17 @@ 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.IKeyboardInputHandler
 import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class ToggleDebugInfoKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return action.actionKey is KeyboardInputActionKey.ShowDebug && action.isKeyDown
index ef4e696c2833613708f25c8ee99a2e458ffae5e4..64a11165e5b882548d1b4e3077cb92d2cc73379f 100644 (file)
@@ -1,16 +1,18 @@
 package ru.deadsoftware.cavedroid.game.input.handler.keyboard
 
 import ru.deadsoftware.cavedroid.game.GameScope
-import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.IKeyboardInputHandler
 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.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class ToggleGameModeKeyboardInputHandler @Inject constructor(
     private val mobsController: MobsController
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
index d39dfdbc818c618608dc2a69637c40a3e1bd88a6..2cd6638c01bb6c9e29604e2c9a1f28449ba9a4a7 100644 (file)
@@ -2,15 +2,17 @@ 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.IKeyboardInputHandler
 import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class ToggleMinimapKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     override fun checkConditions(action: KeyboardInputAction): Boolean {
         return action.actionKey is KeyboardInputActionKey.ShowMap && action.isKeyDown
index 6a73a9bc54f316db61c68134ce2f8355035aaf46..43a7122970acf6c94dec1c149539c82e2209d273 100644 (file)
@@ -2,21 +2,24 @@ 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.IKeyboardInputHandler
 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.misc.annotations.multibinding.BindKeyboardInputHandler
 import javax.inject.Inject
 
 @GameScope
+@BindKeyboardInputHandler
 class TurnOnFlyModeKeyboardInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
-) : IGameInputHandler<KeyboardInputAction> {
+) : IKeyboardInputHandler {
 
     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..3b925b0
--- /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.IMouseInputHandler
+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,
+) : IMouseInputHandler {
+
+    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..52333ebcef42c503e0a1c387234c9532a95a8caf 100644 (file)
@@ -1,22 +1,24 @@
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 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.IMouseInputHandler
 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
 
 @GameScope
+@BindMouseInputHandler
 class AttackMouseInputHandler @Inject constructor(
     private val mobsController: MobsController,
     private val gameWorld: GameWorld,
     private val gameWindowsManager: GameWindowsManager
-) : IGameInputHandler<MouseInputAction> {
+) : IMouseInputHandler {
 
     override fun checkConditions(action: MouseInputAction): Boolean {
         return gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE &&
index e80ff476b00fdfbb02c1c7623ddb2b8969b3ee6a..4fb0879ec4567481d1d09e7df42cc43db5595461 100644 (file)
@@ -1,33 +1,37 @@
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 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.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.input.IMouseInputHandler
 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
 
 @GameScope
+@BindMouseInputHandler
 class CloseGameWindowMouseInputHandler @Inject constructor(
     private val gameWindowsManager: GameWindowsManager,
     private val mobsController: MobsController,
     private val dropController: DropController,
-) : IGameInputHandler<MouseInputAction> {
+) : IMouseInputHandler {
 
     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 +40,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 +49,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..8acdb7e1d5cdd7437d61c9c64c678a5ce0a2aff1 100644 (file)
@@ -1,12 +1,13 @@
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 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.windows.GameWindowsManager
-import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.input.IMouseInputHandler
 import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
 import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
 import ru.deadsoftware.cavedroid.game.input.isInsideWindow
@@ -15,11 +16,12 @@ import javax.inject.Inject
 import kotlin.math.abs
 
 @GameScope
+@BindMouseInputHandler
 class CreativeInventoryScrollMouseInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val gameWindowsManager: GameWindowsManager,
     private val gameItemsHolder: GameItemsHolder,
-) : IGameInputHandler<MouseInputAction> {
+) : IMouseInputHandler {
 
     private val creativeInventoryTexture get() = requireNotNull(Assets.textureRegions["creative"])
 
@@ -34,12 +36,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 +77,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..c1f2af584a8ea931c8ec5f0b5d9429507c18038b 100644 (file)
@@ -1,49 +1,48 @@
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 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.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.IMouseInputHandler
 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
 
 @GameScope
+@BindMouseInputHandler
 class CursorMouseInputHandler @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
     private val gameWorld: GameWorld,
-) : IGameInputHandler<MouseInputAction> {
+    private val gameWindowsManager: GameWindowsManager,
+    private val gameItemsHolder: GameItemsHolder,
+    private val tooltipManager: TooltipManager,
+) : IMouseInputHandler {
 
     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 +58,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()) {
@@ -73,7 +73,7 @@ class CursorMouseInputHandler @Inject constructor(
     }
 
     private fun getPlayerHeadRotation(mouseWorldX: Float, mouseWorldY: Float): Float {
-        val h = mouseWorldX - player.x
+        val h = mouseWorldX - (player.x + player.width / 2)
         val v = mouseWorldY - player.y
 
         return MathUtils.atan(v / h) * MathUtils.radDeg
@@ -90,6 +90,33 @@ class CursorMouseInputHandler @Inject constructor(
         player.cursorY = worldY.bl
 
         player.headRotation = getPlayerHeadRotation(worldX, worldY)
+
+        if (worldX < player.x + player.width / 2) {
+            player.setDir(Mob.Direction.LEFT)
+        } else {
+            player.setDir(Mob.Direction.RIGHT)
+        }
+    }
+
+    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 {
@@ -105,12 +132,19 @@ class CursorMouseInputHandler @Inject constructor(
             !mainConfig.isTouch -> handleMouse(action)
         }
 
-        checkCursorBounds()
-        setPlayerDirectionToCursor()
+        player.checkCursorBounds(gameWorld)
+
+        if (player.controlMode == Player.ControlMode.WALK && mainConfig.isTouch) {
+            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..6a66338753956f6013de027289107f228110e183 100644 (file)
@@ -1,22 +1,30 @@
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 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.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.input.IMouseInputHandler
 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
 
 @GameScope
+@BindMouseInputHandler
 class HotbarMouseInputHandler @Inject constructor(
     private val gameWindowsManager: GameWindowsManager,
     private val mobsController: MobsController,
-) : IGameInputHandler<MouseInputAction> {
+    private val dropController: DropController,
+) : IMouseInputHandler {
 
     private val hotbarTexture get() = requireNotNull(Assets.textureRegions["hotbar"])
 
@@ -24,7 +32,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 +43,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 +81,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 +101,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 +123,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..62a4836
--- /dev/null
@@ -0,0 +1,74 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
+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
+@BindMouseInputHandler
+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..aefea54d6d5797161677ac137e05c961f887bae7 100644 (file)
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
-import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 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
 
 @GameScope
+@BindMouseInputHandler
 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 +84,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..976f443c93ceff7a8fb20c7beffd33296607cd04 100644 (file)
@@ -1,31 +1,33 @@
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 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.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.input.IMouseInputHandler
 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
 
 @GameScope
+@BindMouseInputHandler
 class SelectCreativeInventoryItemMouseInputHandler @Inject constructor(
     private val gameItemsHolder: GameItemsHolder,
     private val gameWindowsManager: GameWindowsManager,
     private val mobsController: MobsController,
-) : IGameInputHandler<MouseInputAction> {
+) : IMouseInputHandler {
 
     private val creativeInventoryTexture get() = requireNotNull(Assets.textureRegions["creative"])
 
     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 +48,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..eac8cfa
--- /dev/null
@@ -0,0 +1,102 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
+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
+@BindMouseInputHandler
+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..58687fcbc1ae0bf5063ed516d55cf52bc19d8265 100644 (file)
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
-import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 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
 
 @GameScope
+@BindMouseInputHandler
 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 +82,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..7405faf0c5eae97f812199b8ec46599506f822ef 100644 (file)
@@ -1,29 +1,37 @@
 package ru.deadsoftware.cavedroid.game.input.handler.mouse
 
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
 import com.badlogic.gdx.Gdx
 import com.badlogic.gdx.utils.Timer
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 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.IMouseInputHandler
 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.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
+@BindMouseInputHandler
 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,
-) : IGameInputHandler<MouseInputAction> {
+    private val gameWorld: GameWorld,
+    private val gameItemsHolder: GameItemsHolder,
+) : IMouseInputHandler {
 
     private var buttonHoldTask: Timer.Task? = null
 
@@ -43,13 +51,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 +75,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 +106,11 @@ 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 if (item is Item.Food && player.health < player.maxHealth) {
+            player.heal(item.heal)
+            player.decreaseCurrentItemCount(gameItemsHolder)
+        } 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..d717c18
--- /dev/null
@@ -0,0 +1,148 @@
+package ru.deadsoftware.cavedroid.game.input.handler.touch
+
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindMouseInputHandler
+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.*
+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.Player
+import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+@BindMouseInputHandler
+class JoystickInputHandler @Inject constructor(
+    private val mainConfig: MainConfig,
+    private val mobsController: MobsController,
+    private val gameWindowsManager: GameWindowsManager,
+    private val gameWorld: GameWorld,
+) : IMouseInputHandler {
+
+    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
         }
     }
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingBlock.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingBlock.kt
new file mode 100644 (file)
index 0000000..d4494a3
--- /dev/null
@@ -0,0 +1,96 @@
+package ru.deadsoftware.cavedroid.game.mobs
+
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.utils.bl
+import ru.deadsoftware.cavedroid.misc.utils.px
+
+class FallingBlock(
+    private val blockKey: String,
+    x: Float,
+    y: Float,
+) : Mob(x, y, 1.px, 1.px, Direction.RIGHT, Type.FALLING_BLOCK, Int.MAX_VALUE) {
+
+    private var _block: Block? = null
+
+    init {
+        velocity.y = 1f
+    }
+
+    override fun changeDir() = Unit
+
+    override fun getSpeed() = 0f
+
+    override fun jump() = Unit
+
+    override fun ai(
+        gameWorld: GameWorld,
+        gameItemsHolder: GameItemsHolder,
+        mobsController: MobsController,
+        delta: Float
+    ) {
+        if (_block == null) {
+            _block = gameItemsHolder.getBlock(blockKey)
+        }
+
+        if (velocity.isZero) {
+            gameWorld.setForeMap(x.bl, y.bl, _block)
+            kill()
+        }
+    }
+
+    override fun draw(
+        spriteBatch: SpriteBatch,
+        x: Float,
+        y: Float,
+        delta: Float
+    ) {
+        _block?.draw(spriteBatch, x, y)
+    }
+
+    override fun getSaveData(): SaveDataDto.FallingBlockSaveData {
+        return SaveDataDto.FallingBlockSaveData(
+            version = SAVE_DATA_VERSION,
+            x = x,
+            y = y,
+            width = width,
+            height = height,
+            velocityX = velocity.x,
+            velocityY = velocity.y,
+            type = mType,
+            animDelta = mAnimDelta,
+            anim = mAnim,
+            direction = mDirection,
+            dead = mDead,
+            canJump = mCanJump,
+            flyMode = mFlyMode,
+            maxHealth = mMaxHealth,
+            health = mHealth,
+            blockKey = blockKey,
+        )
+    }
+
+    companion object {
+        private const val SAVE_DATA_VERSION = 1
+
+        fun fromSaveData(saveData: SaveDataDto.FallingBlockSaveData): FallingBlock {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            return FallingBlock(saveData.blockKey, saveData.x, saveData.y).apply {
+                velocity.x = saveData.velocityX
+                velocity.y = saveData.velocityY
+                mAnimDelta = saveData.animDelta
+                mAnim = saveData.anim
+                mDirection = saveData.direction
+                mDead = saveData.dead
+                mCanJump = saveData.canJump
+                mFlyMode = saveData.flyMode
+                mMaxHealth = saveData.maxHealth
+                mHealth = saveData.health
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingGravel.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingGravel.java
deleted file mode 100644 (file)
index 641ffe5..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-package ru.deadsoftware.cavedroid.game.mobs;
-
-import com.badlogic.gdx.Gdx;
-import com.badlogic.gdx.graphics.Texture;
-import com.badlogic.gdx.graphics.g2d.SpriteBatch;
-import com.badlogic.gdx.math.Vector2;
-import ru.deadsoftware.cavedroid.game.GameItemsHolder;
-import ru.deadsoftware.cavedroid.game.world.GameWorld;
-import ru.deadsoftware.cavedroid.misc.Assets;
-
-import javax.annotation.CheckForNull;
-
-/**
- * Falling gravel is actually a mob, that spawns in place of gravel when there is no block under it,
- * falls down to the next block and becomes a block of gravel again.
- */
-public class FallingGravel extends Mob {
-
-    private static final String TAG = "FallingGravel";
-
-    /**
-     * Creates a FallingGravel mob at coordinates
-     *
-     * @param x X in pixels
-     * @param y Y in pixels
-     */
-    public FallingGravel(float x, float y) {
-        super(x, y, 16, 16, Direction.LEFT, Type.GRAVEL, Integer.MAX_VALUE);
-        mVelocity = new Vector2(0, 1);
-    }
-
-    @Override
-    public float getSpeed() {
-        return 0;
-    }
-
-    @Override
-    public void jump() {
-        // no-op
-    }
-
-    @Override
-    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
-        if (mVelocity.isZero()) {
-            gameWorld.setForeMap(getMapX(), getUpperMapY(), gameItemsHolder.getBlock("gravel"));
-            kill();
-        }
-    }
-
-    @Override
-    public void changeDir() {
-    }
-
-    @Override
-    public void draw(SpriteBatch spriteBatch, float x, float y, float delta) {
-        @CheckForNull final Texture texture = Assets.blockTextures.get("gravel");
-
-        if (texture == null) {
-            Gdx.app.error(TAG, "Couldn't draw: texture not found");
-            kill();
-            return;
-        }
-
-        spriteBatch.draw(texture, x, y);
-    }
-
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingSand.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingSand.java
deleted file mode 100644 (file)
index f41da96..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-package ru.deadsoftware.cavedroid.game.mobs;
-
-import com.badlogic.gdx.Gdx;
-import com.badlogic.gdx.graphics.Texture;
-import com.badlogic.gdx.graphics.g2d.SpriteBatch;
-import com.badlogic.gdx.math.Vector2;
-import ru.deadsoftware.cavedroid.game.GameItemsHolder;
-import ru.deadsoftware.cavedroid.game.world.GameWorld;
-import ru.deadsoftware.cavedroid.misc.Assets;
-
-import javax.annotation.CheckForNull;
-
-
-/**
- * Falling sand is actually a mob, that spawns in place of gravel when there is no block under it,
- * falls down to the next block and becomes a block of sand again.
- */
-public class FallingSand extends Mob {
-
-    private static final String TAG = "FallingSand";
-
-    /**
-     * Creates a FallingSand mob at coordinates
-     *
-     * @param x X in pixels
-     * @param y Y in pixels
-     */
-    public FallingSand(float x, float y) {
-        super(x, y, 16, 16, Direction.LEFT, Type.SAND, Integer.MAX_VALUE);
-        mVelocity = new Vector2(0, 1);
-    }
-
-    @Override
-    public float getSpeed() {
-        return 0;
-    }
-
-    @Override
-    public void jump() {
-        // no-op
-    }
-
-    @Override
-    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
-        if (mVelocity.isZero()) {
-            gameWorld.setForeMap(getMapX(), getUpperMapY(), gameItemsHolder.getBlock("sand"));
-            kill();
-        }
-    }
-
-    @Override
-    public void changeDir() {
-    }
-
-    @Override
-    public void draw(SpriteBatch spriteBatch, float x, float y, float delta) {
-        @CheckForNull final Texture texture = Assets.blockTextures.get("sand");
-
-        if (texture == null) {
-            Gdx.app.error(TAG, "Couldn't draw: texture not found");
-            kill();
-            return;
-        }
-
-        spriteBatch.draw(texture, x, y);
-    }
-
-}
index 415ef30376503a491a3eefd9eb8c127d53089b77..3ee37d37e626914cef7b41e55c151c42ec743c3b 100644 (file)
@@ -1,26 +1,37 @@
 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 org.jetbrains.annotations.Nullable;
 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto;
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
+import ru.deadsoftware.cavedroid.misc.Saveable;
 
-import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Mob class.
  */
-public abstract class Mob extends Rectangle implements Serializable {
+public abstract class Mob extends Rectangle implements  Saveable {
+
+    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 {
         MOB,
-        SAND,
-        GRAVEL
+        FALLING_BLOCK
     }
 
     public enum Direction {
@@ -51,18 +62,31 @@ 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;
     protected float mAnim;
 
-    private Direction mDirection;
+    protected Direction mDirection;
     protected boolean mDead;
-    private boolean mCanJump;
-    private boolean mFlyMode;
+    protected boolean mCanJump;
+    protected boolean mFlyMode;
+
+    protected int mMaxHealth;
+    protected int mHealth;
 
-    private final int mMaxHealth;
-    private int mHealth;
+    private boolean mTakingDamage = false;
+
+    @Nullable
+    private ResetTakeDamageTask mResetTakeDamageTask = null;
 
     /**
      * @param x          in pixels
@@ -99,17 +123,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;
         }
 
@@ -187,7 +215,7 @@ public abstract class Mob extends Rectangle implements Serializable {
     /**
      * Set's mob's dead variable to true and nothing else. It doesn't delete the
      */
-    public final void kill() {
+    public void kill() {
         mDead = true;
     }
 
@@ -237,6 +265,10 @@ public abstract class Mob extends Rectangle implements Serializable {
         return mHealth;
     }
 
+    public final int getMaxHealth() {
+        return mMaxHealth;
+    }
+
     public final void attachToController(MobsController controller) {
         controller.addMob(this);
     }
@@ -257,6 +289,8 @@ public abstract class Mob extends Rectangle implements Serializable {
 
         mHealth -= damage;
         checkHealth();
+
+        setTakingDamage(true);
     }
 
     public void heal(int heal) {
@@ -273,13 +307,50 @@ 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 List<InventoryItem> getDrop(GameItemsHolder gameItemsHolder) {
+        return Collections.emptyList();
+    }
+
     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();
 
     public abstract float getSpeed();
 
     public abstract void jump();
+
+    @Override
+    public abstract SaveDataDto.MobSaveDataDto getSaveData();
+
+    public static Mob fromSaveData(SaveDataDto.MobSaveDataDto saveData) {
+        return MobSaveDataMapperKt.fromSaveData(saveData);
+    }
 }
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/MobSaveDataMapper.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/MobSaveDataMapper.kt
new file mode 100644 (file)
index 0000000..49adee3
--- /dev/null
@@ -0,0 +1,12 @@
+package ru.deadsoftware.cavedroid.game.mobs
+
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+
+fun fromSaveData(saveData: SaveDataDto.MobSaveDataDto): Mob {
+    return when (saveData) {
+        is SaveDataDto.PigSaveData -> Pig.fromSaveData(saveData)
+        is SaveDataDto.FallingBlockSaveData -> FallingBlock.fromSaveData(saveData)
+
+        is SaveDataDto.PlayerSaveData -> throw IllegalArgumentException("Cannot load player as regular Mob")
+    }
+}
\ No newline at end of file
index 0ec111bbd66bd5b96ff38a9cdd7e323d53b7bf0c..4b2c209c22d6fe1dc44fc4e65b04e1bca4bbae7e 100644 (file)
@@ -2,18 +2,23 @@ package ru.deadsoftware.cavedroid.game.mobs
 
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
-import java.io.Serializable
+import ru.deadsoftware.cavedroid.game.mobs.player.Player
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager
+import ru.deadsoftware.cavedroid.misc.Saveable
 import java.util.*
 import javax.inject.Inject
 
 @GameScope
 class MobsController @Inject constructor(
-    gameItemsHolder: GameItemsHolder
-) : Serializable {
+    gameItemsHolder: GameItemsHolder,
+    tooltipManager: TooltipManager,
+) : Saveable {
 
     private val _mobs = LinkedList<Mob>()
 
-    val player: Player = Player(gameItemsHolder)
+    var player: Player = Player(gameItemsHolder, tooltipManager)
+        private set
 
     val mobs: List<Mob>
         get() = _mobs
@@ -22,7 +27,31 @@ class MobsController @Inject constructor(
         _mobs.add(mob)
     }
 
+    override fun getSaveData(): SaveDataDto.MobsControllerSaveData {
+        return SaveDataDto.MobsControllerSaveData(
+            version = SAVE_DATA_VERSION,
+            mobs = _mobs.map(Mob::getSaveData),
+            player = player.getSaveData(),
+        )
+    }
+
     companion object {
+        private const val SAVE_DATA_VERSION = 1
+
         private const val TAG = "MobsController"
+
+        fun fromSaveData(
+            saveData: SaveDataDto.MobsControllerSaveData,
+            gameItemsHolder: GameItemsHolder,
+            tooltipManager: TooltipManager
+        ): MobsController {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            return MobsController(gameItemsHolder, tooltipManager)
+                .apply {
+                    _mobs.addAll(saveData.mobs.map { mob -> Mob.fromSaveData(mob) })
+                    player = Player.fromSaveData(saveData.player, gameItemsHolder, tooltipManager)
+                }
+        }
     }
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/PeacefulMob.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/PeacefulMob.kt
new file mode 100644 (file)
index 0000000..b0e5bc6
--- /dev/null
@@ -0,0 +1,20 @@
+package ru.deadsoftware.cavedroid.game.mobs
+
+import com.badlogic.gdx.math.MathUtils
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+
+abstract class PeacefulMob(x: Float, y: Float, width: Float, height: Float, direction: Direction, maxHealth: Int, )
+    : Mob(x, y, width, height, direction, Type.MOB, maxHealth) {
+
+    override fun ai(world: GameWorld, gameItemsHolder: GameItemsHolder, mobsController: MobsController, delta: Float) {
+        if (MathUtils.randomBoolean(delta)) {
+            if (velocity.x != 0f) {
+                velocity.x = 0f
+            } else {
+                changeDir()
+            }
+        }
+    }
+
+}
\ No newline at end of file
index b55f42934be9f2d8d100d72d7068aa6e055c2951..b080e4078730713b897e8a45f1e1ba3655b81239 100644 (file)
@@ -1,10 +1,10 @@
 package ru.deadsoftware.cavedroid.game.mobs
 
 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.world.GameWorld
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
 import ru.deadsoftware.cavedroid.misc.utils.drawSprite
 import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getBackgroundLeg
 import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getBody
@@ -13,7 +13,7 @@ import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getLeftLegRelati
 import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getLegsRelativeY
 import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getRightLegRelativeX
 
-class Pig(x: Float, y: Float) : Mob(x, y, WIDTH, HEIGHT, randomDir(), Type.MOB, MAX_HEALTH) {
+class Pig(x: Float, y: Float) : PeacefulMob(x, y, WIDTH, HEIGHT, randomDir(), MAX_HEALTH) {
 
     override fun getSpeed(): Float {
         return SPEED
@@ -27,17 +27,21 @@ 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) {
-        if (MathUtils.randomBoolean(delta)) {
-            if (velocity.x != 0f) {
-                velocity.x = 0f
-            } else {
-                changeDir()
+
+    override fun damage(damage: Int) {
+        super.damage(damage)
+
+        if (damage > 0) {
+            if (canJump()) {
+                jump()
             }
         }
     }
 
+    override fun getDrop(gameItemsHolder: GameItemsHolder): List<InventoryItem> {
+        return listOf(gameItemsHolder.getItem("porkchop_raw").toInventoryItem())
+    }
+
     override fun draw(spriteBatch: SpriteBatch, x: Float, y: Float, delta: Float) {
         updateAnimation(delta)
 
@@ -45,19 +49,58 @@ 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)
+    }
+
+    override fun getSaveData(): SaveDataDto.PigSaveData {
+        return SaveDataDto.PigSaveData(
+            version = SAVE_DATA_VERSION,
+            x = x,
+            y = y,
+            width = width,
+            height = height,
+            velocityX = velocity.x,
+            velocityY = velocity.y,
+            type = mType,
+            animDelta = mAnimDelta,
+            anim = mAnim,
+            direction = mDirection,
+            dead = mDead,
+            canJump = mCanJump,
+            flyMode = mFlyMode,
+            maxHealth = mMaxHealth,
+            health = mHealth
+        )
     }
     
-    
-    private companion object {
+    companion object {
+        private const val SAVE_DATA_VERSION = 1
+
         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
+
+        fun fromSaveData(saveData: SaveDataDto.PigSaveData): Pig {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            return Pig(saveData.x, saveData.y).apply {
+                velocity.x = saveData.velocityX
+                velocity.y = saveData.velocityY
+                mAnimDelta = saveData.animDelta
+                mAnim = saveData.anim
+                mDirection = saveData.direction
+                mDead = saveData.dead
+                mCanJump = saveData.canJump
+                mFlyMode = saveData.flyMode
+                mMaxHealth = saveData.maxHealth
+                mHealth = saveData.health
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/package-info.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/package-info.java
deleted file mode 100644 (file)
index e76bf1b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@NonnullByDefault
-package ru.deadsoftware.cavedroid.game.mobs;
-
-import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault;
\ 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..91e8be1
--- /dev/null
@@ -0,0 +1,179 @@
+package ru.deadsoftware.cavedroid.game.mobs.player
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+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 ru.deadsoftware.cavedroid.misc.Saveable
+
+class Inventory @JvmOverloads constructor(
+    val size: Int,
+    val hotbarSize: Int,
+    gameItemsHolder: GameItemsHolder,
+    tooltipManager: TooltipManager,
+    initialItems: List<InventoryItem>? = null
+) : Saveable {
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    private lateinit var tooltipManager: TooltipManager
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    private lateinit var fallbackItem: InventoryItem
+
+    private val _items: Array<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")
+        }
+
+        _items = Array(size) { index -> initialItems?.getOrNull(index) ?: InventoryItem(gameItemsHolder.fallbackItem) }
+    }
+
+    val items get() = _items.asList() as MutableList<InventoryItem>
+
+    val hotbarItems get() = items.subList(0, hotbarSize)
+
+    private var _activeSlot = 0
+
+    var activeSlot
+        get() = _activeSlot
+        set(value) {
+            if (value in 0 ..< hotbarSize) {
+                _activeSlot = 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)
+    }
+
+    fun clear() {
+        for (i in _items.indices) {
+            _items[i] = fallbackItem
+        }
+    }
+
+    override fun getSaveData(): SaveDataDto.InventorySaveData {
+        return SaveDataDto.InventorySaveData(
+            version = SAVE_DATA_VERSION,
+            size = size,
+            hotbarSize = hotbarSize,
+            activeSlot = _activeSlot,
+            items = items.map(InventoryItem::getSaveData)
+        )
+    }
+
+    companion object {
+        private const val SAVE_DATA_VERSION = 1
+
+        fun fromSaveData(
+            saveData: SaveDataDto.InventorySaveData,
+            gameItemsHolder: GameItemsHolder,
+            tooltipManager: TooltipManager,
+        ): Inventory {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            return Inventory(
+                size = saveData.size,
+                hotbarSize = saveData.hotbarSize,
+                gameItemsHolder = gameItemsHolder,
+                tooltipManager = tooltipManager,
+                initialItems = saveData.items.map { item -> InventoryItem.fromSaveData(item, gameItemsHolder) }
+            ).apply {
+                _activeSlot = saveData.activeSlot
+            }
+        }
+    }
+}
\ No newline at end of file
similarity index 57%
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..9aa503f917a1daec0fa0d92482d7bf377b06cb32 100644 (file)
@@ -1,34 +1,44 @@
-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 org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 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.dto.SaveDataDto;
 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 int SAVE_DATA_VERSION = 1;
+
     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 Inventory inventory;
+
     public int gameMode;
     public boolean swim;
     public float headRotation = 0f;
@@ -37,7 +47,7 @@ public class Player extends Mob {
     public int cursorX = 0;
     public int cursorY = 0;
 
-    @CheckForNull
+    @Nullable
     private Vector2 spawnPoint = null;
 
     public ControlMode controlMode = ControlMode.WALK;
@@ -47,26 +57,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 +80,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 +127,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 +137,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 +186,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 +215,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;
@@ -199,11 +230,11 @@ public class Player extends Mob {
 
         final Block foregroundBlock = gameWorld.getForeMap(cursorX, cursorY);
         final Block backgroundBlock = gameWorld.getBackMap(cursorX, cursorY);
-        @CheckForNull final Block target;
+        @Nullable 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 +243,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 +284,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 +417,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()) {
@@ -390,4 +442,79 @@ public class Player extends Mob {
         SpriteUtilsKt.drawSprite(spriteBatch, frontHand, x + 2, y + 8, frontHandAnim);
     }
 
+    @NotNull
+    @Override
+    public SaveDataDto.PlayerSaveData getSaveData() {
+        return new SaveDataDto.PlayerSaveData(
+                SAVE_DATA_VERSION,
+                mType,
+                mAnimDelta,
+                mAnim,
+                mDirection,
+                mDead,
+                mCanJump,
+                mFlyMode,
+                mMaxHealth,
+                mHealth,
+                x,
+                y,
+                width,
+                height,
+                getVelocity().x,
+                getVelocity().y,
+                hitting,
+                hittingWithDamage,
+                hitAnim,
+                hitAnimDelta,
+                inventory.getSaveData(),
+                gameMode,
+                swim,
+                headRotation,
+                blockDamage,
+                cursorX,
+                cursorY,
+                spawnPoint != null ? spawnPoint.x : 0f,
+                spawnPoint != null ? spawnPoint.y : 0f,
+                controlMode
+        );
+    }
+
+    public static Player fromSaveData(
+            SaveDataDto.PlayerSaveData saveData,
+            GameItemsHolder gameItemsHolder,
+            TooltipManager tooltipManager
+    ) {
+        saveData.verifyVersion(SAVE_DATA_VERSION);
+
+        Player player = new Player(gameItemsHolder, tooltipManager);
+
+        player.mType = saveData.getType();
+        player.mAnimDelta = saveData.getAnimDelta();
+        player.mAnim = saveData.getAnim();
+        player.mDirection = saveData.getDirection();
+        player.mDead = saveData.getDead();
+        player.mCanJump = saveData.getCanJump();
+        player.mFlyMode = saveData.getFlyMode();
+        player.mMaxHealth = saveData.getMaxHealth();
+        player.mHealth = saveData.getHealth();
+        player.x = saveData.getX();
+        player.y = saveData.getY();
+        player.width = saveData.getWidth();
+        player.height = saveData.getHeight();
+        player.hitting = saveData.getHitting();
+        player.hittingWithDamage = saveData.getHittingWithDamage();
+        player.hitAnim = saveData.getHitAnim();
+        player.hitAnimDelta = saveData.getHitAnimDelta();
+        player.inventory = Inventory.Companion.fromSaveData(saveData.getInventory(), gameItemsHolder, tooltipManager);
+        player.gameMode = saveData.getGameMode();
+        player.swim = saveData.getSwim();
+        player.headRotation = saveData.getHeadRotation();
+        player.blockDamage = saveData.getBlockDamage();
+        player.cursorX = saveData.getCursorX();
+        player.cursorY = saveData.getCursorY();
+        player.spawnPoint = new Vector2(saveData.getSpawnPointX(), saveData.getSpawnPointY());
+        player.controlMode = saveData.getControlMode();
+
+        return player;
+    }
 }
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..19454629f6d475c127162b623a434168ab8c9015 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,
@@ -20,4 +19,5 @@ data class CommonBlockParams(
     val toolType: Class<out Item.Tool>?,
     val damage: Int,
     val tint: String?,
+    val isFallable: Boolean,
 )
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..ecbe5bf0e7c3cc4b1564b92d4e03ba8fe20c5b3d 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,
@@ -32,4 +31,5 @@ data class BlockDto(
     @SerialName("tool_type") val toolType: String? = null,
     @SerialName("damage") val damage: Int = 0,
     @SerialName("tint") val tint: String? = null,
+    @SerialName("fallable") val fallable: Boolean = false,
 )
index fe28e6440adc30a52eadaab656d6bbb9306592f3..a276b7100decd2174e87cd88bb0f9231f7030b9e 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,8 @@ 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,
+    @SerialName("heal") val heal: Int? = null,
 )
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/dto/SaveDataDto.kt b/core/src/ru/deadsoftware/cavedroid/game/model/dto/SaveDataDto.kt
new file mode 100644 (file)
index 0000000..e6ad828
--- /dev/null
@@ -0,0 +1,191 @@
+package ru.deadsoftware.cavedroid.game.model.dto
+
+import kotlinx.serialization.Contextual
+import kotlinx.serialization.Serializable
+import ru.deadsoftware.cavedroid.game.mobs.Mob
+import ru.deadsoftware.cavedroid.game.mobs.player.Player.ControlMode
+
+@Serializable
+sealed class SaveDataDto {
+
+    abstract val version: Int
+
+    fun verifyVersion(expectedVersion: Int) {
+        require(version == expectedVersion) {
+            "${this::class.simpleName} version mismatch ($version != $expectedVersion)"
+        }
+    }
+
+    @Serializable
+    sealed class ContainerSaveDataDto : SaveDataDto() {
+        abstract val size: Int
+        abstract val items: List<InventoryItemSaveData>
+    }
+
+    @Serializable
+    sealed class RectangleObjectSaveDataDto : SaveDataDto() {
+        abstract val x: Float
+        abstract val y: Float
+        abstract val width: Float
+        abstract val height: Float
+        abstract val velocityX: Float
+        abstract val velocityY: Float
+    }
+
+    @Serializable
+    sealed class MobSaveDataDto : RectangleObjectSaveDataDto() {
+        abstract val type: Mob.Type
+        abstract val animDelta: Int
+        abstract val anim: Float
+        abstract val direction: Mob.Direction
+        abstract val dead: Boolean
+        abstract val canJump: Boolean
+        abstract val flyMode: Boolean
+        abstract val maxHealth: Int
+        abstract val health: Int
+    }
+
+    @Serializable
+    data class InventoryItemSaveData(
+        override val version: Int,
+        val itemKey: String,
+        val amount: Int,
+    ) : SaveDataDto()
+
+    @Serializable
+    data class InventorySaveData(
+        override val version: Int,
+        override val size: Int,
+        val hotbarSize: Int,
+        val activeSlot: Int,
+        override val items: List<InventoryItemSaveData>,
+    ) : ContainerSaveDataDto()
+
+    @Serializable
+    data class FurnaceSaveData(
+        override val version: Int,
+        override val size: Int,
+        val currentFuelItemKey: String?,
+        override val items: List<InventoryItemSaveData>,
+        val startBurnTimeMs: Long,
+        val startSmeltTimeMs: Long,
+        val burnProgress: Float,
+        val smeltProgress: Float,
+    ) : ContainerSaveDataDto()
+
+    @Serializable
+    data class ChestSaveData(
+        override val version: Int,
+        override val size: Int,
+        override val items: List<InventoryItemSaveData>
+    ) : ContainerSaveDataDto()
+
+    @Serializable
+    data class ContainerControllerSaveData(
+        override val version: Int,
+        val containerMap: Map<String, @Contextual ContainerSaveDataDto>,
+    ): SaveDataDto()
+
+    @Serializable
+    data class DropSaveData(
+        override val version: Int,
+        override val x: Float,
+        override val y: Float,
+        override val width: Float,
+        override val height: Float,
+        override val velocityX: Float,
+        override val velocityY: Float,
+        val itemKey: String,
+        val amount: Int,
+        val pickedUp: Boolean
+    ) : RectangleObjectSaveDataDto()
+    
+    @Serializable
+    data class DropControllerSaveData(
+        override val version: Int,
+        val drops: List<DropSaveData>
+    ) : SaveDataDto()
+    
+    @Serializable
+    data class PigSaveData(
+        override val version: Int,
+        override val x: Float,
+        override val y: Float,
+        override val width: Float,
+        override val height: Float,
+        override val velocityX: Float,
+        override val velocityY: Float,
+        override val type: Mob.Type,
+        override val animDelta: Int,
+        override val anim: Float,
+        override val direction: Mob.Direction,
+        override val dead: Boolean,
+        override val canJump: Boolean,
+        override val flyMode: Boolean,
+        override val maxHealth: Int,
+        override val health: Int,
+    ) : MobSaveDataDto()
+
+    @Serializable
+    data class FallingBlockSaveData(
+        override val version: Int,
+        override val x: Float,
+        override val y: Float,
+        override val width: Float,
+        override val height: Float,
+        override val velocityX: Float,
+        override val velocityY: Float,
+        override val type: Mob.Type,
+        override val animDelta: Int,
+        override val anim: Float,
+        override val direction: Mob.Direction,
+        override val dead: Boolean,
+        override val canJump: Boolean,
+        override val flyMode: Boolean,
+        override val maxHealth: Int,
+        override val health: Int,
+        val blockKey: String,
+    ) : MobSaveDataDto()
+
+    @Serializable
+    data class PlayerSaveData(
+        override val version: Int,
+        override val type: Mob.Type,
+        override val animDelta: Int,
+        override val anim: Float,
+        override val direction: Mob.Direction,
+        override val dead: Boolean,
+        override val canJump: Boolean,
+        override val flyMode: Boolean,
+        override val maxHealth: Int,
+        override val health: Int,
+        override val x: Float,
+        override val y: Float,
+        override val width: Float,
+        override val height: Float,
+        override val velocityX: Float,
+        override val velocityY: Float,
+        val hitting: Boolean,
+        val hittingWithDamage: Boolean,
+        val hitAnim: Float,
+        val hitAnimDelta: Float,
+        val inventory: InventorySaveData,
+        val gameMode: Int,
+        val swim: Boolean,
+        val headRotation: Float,
+        val blockDamage: Float,
+        val cursorX: Int,
+        val cursorY: Int,
+        val spawnPointX: Float,
+        val spawnPointY: Float,
+        val controlMode: ControlMode,
+    ) : MobSaveDataDto()
+
+    @Serializable
+    data class MobsControllerSaveData(
+        override val version: Int,
+        val mobs: List<@Contextual MobSaveDataDto>,
+        val player: PlayerSaveData,
+    ) : SaveDataDto()
+    
+}
\ No newline at end of file
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..f420120b2844e7bd2d77ae4d01513837bac80c74 100644 (file)
@@ -4,31 +4,73 @@ import com.badlogic.gdx.graphics.Color
 import com.badlogic.gdx.graphics.g2d.SpriteBatch
 import com.badlogic.gdx.graphics.glutils.ShapeRenderer
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
 import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.Saveable
 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,
-) : Serializable {
+    _amount: Int = 1,
+) :  Saveable {
 
-    @Transient
-    lateinit var item: Item
-        private set
+    var amount = _amount
+        set(value) {
+            field = if (value < 0) {
+                0
+            } else {
+                value
+            }
+        }
+
+    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,35 @@ class InventoryItem @JvmOverloads constructor(
         }
     }
 
+    override fun getSaveData(): SaveDataDto.InventoryItemSaveData {
+        return SaveDataDto.InventoryItemSaveData(
+            version = SAVE_DATA_VERSION,
+            itemKey = itemKey,
+            amount = amount,
+        )
+    }
+
+    companion object {
+        private const val SAVE_DATA_VERSION = 1
+
+        @OptIn(ExperimentalContracts::class)
+        fun InventoryItem?.isNoneOrNull(): Boolean {
+            contract { returns(false) implies(this@isNoneOrNull != null) }
+            return this?.item == null || this.item.isNone()
+        }
+
+
+        fun fromSaveData(
+            saveData: SaveDataDto.InventoryItemSaveData,
+            gameItemsHolder: GameItemsHolder? = null
+        ): InventoryItem {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            val inventoryItem = InventoryItem(saveData.itemKey, saveData.amount)
+            gameItemsHolder?.let(inventoryItem::init)
+
+            return inventoryItem
+        }
+    }
+
 }
index 1e6755ab7c367b8886301e3173c0664df038e3ec..9b2c62de4794111b4edab8bb332af7a0a99f5809 100644 (file)
@@ -46,8 +46,13 @@ 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
+    }
+
+    fun isFood(): Boolean {
+        contract { returns(true) implies (this@Item is Food) }
+        return this is Food
     }
 
     @JvmOverloads
@@ -66,10 +71,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 +83,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 +141,11 @@ sealed class Item {
         override val blockDamageMultiplier: Float,
         override val level: Int,
     ) : Tool()
-    
-    data class Bucket(
+
+    data class Food(
         override val params: CommonItemParams,
         override val sprite: Sprite,
-        override val useActionKey: String
-    ) : Usable()
+        val heal: Int,
+    ) : Item()
 
 }
\ No newline at end of file
index 85d8cb722d457511ba96d4f46c10e81e994a0e38..19d8c4757d125235a8dfd62b482bbfb467c87862 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,
@@ -56,6 +57,7 @@ class BlockMapper @Inject constructor(
             toolType = mapToolType(dto),
             damage = dto.damage,
             tint = dto.tint,
+            isFallable = dto.fallable,
         )
     }
 
index b951f261e795b7937d40c0c6a6f3b08272e50d72..a1a6f0c68d0cb2357eebc5262fa2acb9c1b6f777 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))
@@ -31,6 +32,7 @@ class ItemMapper @Inject constructor(
             "shears" -> Shears(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier, requireNotNull(dto.toolLevel))
             "block" -> Block(params, requireNotNull(block))
             "slab" -> Slab(params, requireNotNull(slabTopBlock), requireNotNull(slabBottomBlock))
+            "food" -> Food(params, requireNotNull(loadSprite(dto)), requireNotNull(dto.heal))
             "none" -> None(params)
             else -> throw IllegalArgumentException("Unknown item type ${dto.type}")
         }
@@ -38,7 +40,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 +47,8 @@ class ItemMapper @Inject constructor(
                 y = dto.origin_y,
             ),
             maxStack = dto.maxStack,
+            burningTimeMs = dto.burningTime,
+            smeltProductKey = dto.smeltProduct,
         )
     }
 
@@ -56,7 +59,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/Drop.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.kt
deleted file mode 100644 (file)
index 151e486..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-package ru.deadsoftware.cavedroid.game.objects
-
-import com.badlogic.gdx.math.Intersector
-import com.badlogic.gdx.math.Rectangle
-import com.badlogic.gdx.math.Vector2
-import ru.deadsoftware.cavedroid.game.GameItemsHolder
-import ru.deadsoftware.cavedroid.game.model.item.Item
-
-class Drop(
-    x: Float,
-    y: Float,
-    _item: Item,
-) : Rectangle(x, y, DROP_SIZE, DROP_SIZE) {
-
-    val itemKey = _item.params.key
-    val velocity = getInitialVelocity()
-    var pickedUp = false
-
-    @Transient
-    lateinit var item: Item
-        private set
-
-    init {
-        item = _item
-    }
-
-    fun initItem(gameItemsHolder: GameItemsHolder) {
-        if (this::item.isInitialized) {
-            return
-        }
-
-        item = gameItemsHolder.getItem(itemKey)
-    }
-
-    fun canMagnetTo(rectangle: Rectangle): Boolean {
-        val magnetArea = getMagnetArea()
-        return Intersector.overlaps(magnetArea, rectangle)
-    }
-
-    private fun getMagnetArea(): Rectangle {
-        return Rectangle(
-            /* x = */ x - MAGNET_DISTANCE,
-            /* y = */ y - MAGNET_DISTANCE,
-            /* width = */ width + MAGNET_DISTANCE * 2,
-            /* height = */ height + MAGNET_DISTANCE * 2,
-        )
-    }
-
-    companion object {
-        private const val MAGNET_DISTANCE = 16f
-
-        const val MAGNET_VELOCITY = 128f
-        const val DROP_SIZE = 8f
-
-        private fun getInitialVelocity(): Vector2 = Vector2(0f, -1f)
-    }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/DropController.java b/core/src/ru/deadsoftware/cavedroid/game/objects/DropController.java
deleted file mode 100644 (file)
index 798a84c..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-package ru.deadsoftware.cavedroid.game.objects;
-
-import ru.deadsoftware.cavedroid.game.GameItemsHolder;
-import ru.deadsoftware.cavedroid.game.GameScope;
-import ru.deadsoftware.cavedroid.game.model.item.Item;
-
-import javax.inject.Inject;
-import java.io.Serializable;
-import java.util.Iterator;
-import java.util.LinkedList;
-
-@GameScope
-public class DropController implements Serializable {
-
-    public interface Callback {
-        void run(Drop drop);
-    }
-
-    private final LinkedList<Drop> mDrops = new LinkedList<>();
-
-    @Inject
-    public DropController() {
-    }
-
-    public void initDrops(GameItemsHolder gameItemsHolder) {
-        mDrops.forEach((drop) -> drop.initItem(gameItemsHolder));
-    }
-
-    public void addDrop(float x, float y, Item item) {
-        if (item.isNone()) {
-            return;
-        }
-        mDrops.add(new Drop(x, y, item));
-    }
-
-    public int getSize() {
-        return mDrops.size();
-    }
-
-    public void forEach(Callback callback) {
-        mDrops.forEach(callback::run);
-    }
-
-    public Iterator<Drop> getIterator() {
-        return mDrops.iterator();
-    }
-
-}
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..a9e893b
--- /dev/null
@@ -0,0 +1,38 @@
+package ru.deadsoftware.cavedroid.game.objects.container
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.misc.Saveable
+
+class Chest @JvmOverloads constructor(
+    gameItemsHolder: GameItemsHolder,
+    initialItems: List<InventoryItem>? = null
+) : Container(SIZE, gameItemsHolder, initialItems), Saveable {
+
+    override fun update(gameItemsHolder: GameItemsHolder) {
+        // no-op
+    }
+
+    override fun getSaveData(): SaveDataDto.ChestSaveData {
+        return SaveDataDto.ChestSaveData(
+            version = SAVE_DATA_VERSION,
+            size = size,
+            items = items.map(InventoryItem::getSaveData)
+        )
+    }
+
+    companion object {
+        private const val SAVE_DATA_VERSION = 1
+        private const val SIZE = 27
+
+        fun fromSaveData(saveData: SaveDataDto.ChestSaveData, gameItemsHolder: GameItemsHolder): Chest {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            return Chest(
+                gameItemsHolder = gameItemsHolder,
+                initialItems = saveData.items.map { item -> InventoryItem.fromSaveData(item, gameItemsHolder) }
+            )
+        }
+    }
+}
\ 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..3938ef7
--- /dev/null
@@ -0,0 +1,40 @@
+package ru.deadsoftware.cavedroid.game.objects.container
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.misc.Saveable
+
+abstract class Container @JvmOverloads constructor(
+    val size: Int,
+    gameItemsHolder: GameItemsHolder,
+    initialItems: List<InventoryItem>? = null,
+) : Saveable {
+
+    private val _items = Array(size) { index ->
+        initialItems?.getOrNull(index) ?: gameItemsHolder.fallbackItem.toInventoryItem()
+    }
+
+    val items get() = _items.asList() as MutableList<InventoryItem>
+
+    open fun initItems(gameItemsHolder: GameItemsHolder) {
+        _items.forEach { it.init(gameItemsHolder) }
+    }
+
+    abstract fun update(gameItemsHolder: GameItemsHolder)
+
+    abstract override fun getSaveData(): SaveDataDto.ContainerSaveDataDto
+
+    companion object {
+        fun fromSaveData(saveData: SaveDataDto.ContainerSaveDataDto, gameItemsHolder: GameItemsHolder): Container {
+            return when (saveData) {
+                is SaveDataDto.FurnaceSaveData -> Furnace.fromSaveData(saveData, gameItemsHolder)
+                is SaveDataDto.ChestSaveData -> Chest.fromSaveData(saveData, gameItemsHolder)
+
+                is SaveDataDto.InventorySaveData -> {
+                    throw IllegalArgumentException("Cannot load Container from InventorySaveData")
+                }
+            }
+        }
+    }
+}
\ 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..d2159f1
--- /dev/null
@@ -0,0 +1,112 @@
+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.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem.Companion.isNoneOrNull
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+import ru.deadsoftware.cavedroid.misc.Saveable
+import ru.deadsoftware.cavedroid.misc.utils.px
+import javax.inject.Inject
+
+@GameScope
+class ContainerController @Inject constructor(
+    _dropController: DropController,
+    _gameItemsHolder: GameItemsHolder
+) : Saveable {
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    private lateinit var dropController: DropController
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    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)
+        }
+    }
+
+    override fun getSaveData(): SaveDataDto.ContainerControllerSaveData {
+        return SaveDataDto.ContainerControllerSaveData(
+            version = SAVE_DATA_VERSION,
+            containerMap = containerMap.mapValues { (_, container) -> container.getSaveData() },
+        )
+    }
+
+    companion object {
+        private const val SAVE_DATA_VERSION = 1
+        private const val TAG = "ContainerController"
+
+        fun fromSaveData(
+            saveData: SaveDataDto.ContainerControllerSaveData,
+            dropController: DropController,
+            gameItemsHolder: GameItemsHolder
+        ): ContainerController {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            return ContainerController(
+                dropController,
+                gameItemsHolder
+            ).apply {
+                containerMap.putAll(
+                    saveData.containerMap.mapValues { (_, containerSaveData) ->
+                        Container.fromSaveData(containerSaveData, gameItemsHolder)
+                    }
+                )
+            }
+        }
+    }
+
+}
\ 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..237006d
--- /dev/null
@@ -0,0 +1,181 @@
+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.dto.SaveDataDto
+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
+import ru.deadsoftware.cavedroid.misc.Saveable
+
+class Furnace @JvmOverloads constructor(
+    gameItemsHolder: GameItemsHolder,
+    initialItems: List<InventoryItem>? = null
+) : Container(SIZE, gameItemsHolder, initialItems), Saveable {
+
+    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
+
+    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
+        }
+    }
+
+    override fun getSaveData(): SaveDataDto.FurnaceSaveData {
+        return SaveDataDto.FurnaceSaveData(
+            version = SAVE_DATA_VERSION,
+            size = size,
+            currentFuelItemKey = currentFuelKey,
+            items = items.map(InventoryItem::getSaveData),
+            startBurnTimeMs = startBurnTimeMs,
+            startSmeltTimeMs = smeltStarTimeMs,
+            burnProgress = burnProgress,
+            smeltProgress = smeltProgress,
+        )
+    }
+
+    companion object {
+        private const val SAVE_DATA_VERSION = 1
+        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
+
+        fun fromSaveData(saveData: SaveDataDto.FurnaceSaveData, gameItemsHolder: GameItemsHolder): Furnace {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            return Furnace(
+                gameItemsHolder = gameItemsHolder,
+                initialItems = saveData.items.map { item ->
+                    InventoryItem.fromSaveData(item, gameItemsHolder)
+                }
+            ).apply {
+                currentFuelKey = saveData.currentFuelItemKey
+                startBurnTimeMs = saveData.startSmeltTimeMs
+                smeltStarTimeMs = saveData.startSmeltTimeMs
+                burnProgress = saveData.burnProgress
+                smeltProgress = saveData.smeltProgress
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/drop/Drop.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/drop/Drop.kt
new file mode 100644 (file)
index 0000000..2b4f740
--- /dev/null
@@ -0,0 +1,104 @@
+package ru.deadsoftware.cavedroid.game.objects.drop
+
+import com.badlogic.gdx.math.Intersector
+import com.badlogic.gdx.math.Rectangle
+import com.badlogic.gdx.math.Vector2
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.misc.Saveable
+
+class Drop @JvmOverloads constructor(
+    x: Float,
+    y: Float,
+    _item: Item,
+    _amount: Int = 1,
+) : Rectangle(x, y, DROP_SIZE, DROP_SIZE), Saveable {
+
+    var amount: Int = _amount
+        private set
+
+    val itemKey = _item.params.key
+    val velocity = getInitialVelocity()
+    var pickedUp = false
+
+    @Suppress("UNNECESSARY_LATEINIT")
+    lateinit var item: Item
+        private set
+
+    init {
+        item = _item
+    }
+
+    fun initItem(gameItemsHolder: GameItemsHolder) {
+        if (this::item.isInitialized) {
+            return
+        }
+
+        item = gameItemsHolder.getItem(itemKey)
+    }
+
+    fun canMagnetTo(rectangle: Rectangle): Boolean {
+        val magnetArea = getMagnetArea()
+        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,
+            /* y = */ y - MAGNET_DISTANCE,
+            /* width = */ width + MAGNET_DISTANCE * 2,
+            /* height = */ height + MAGNET_DISTANCE * 2,
+        )
+    }
+
+    override fun getSaveData(): SaveDataDto.DropSaveData {
+        return SaveDataDto.DropSaveData(
+            version = SAVE_DATA_VERSION,
+            x = x,
+            y = y,
+            width = width,
+            height = height,
+            velocityX = velocity.x,
+            velocityY = velocity.y,
+            itemKey = itemKey,
+            amount = amount,
+            pickedUp = pickedUp
+        )
+    }
+
+    companion object {
+        private const val SAVE_DATA_VERSION = 1
+
+        private const val MAGNET_DISTANCE = 8f
+
+        const val MAGNET_VELOCITY = 256f
+        const val DROP_SIZE = 8f
+
+        private fun getInitialVelocity(): Vector2 = Vector2(0f, -1f)
+
+        fun fromSaveData(saveData: SaveDataDto.DropSaveData, gameItemsHolder: GameItemsHolder): Drop {
+            saveData.verifyVersion(SAVE_DATA_VERSION)
+
+            return Drop(
+                x = saveData.x,
+                y = saveData.y,
+                _item = gameItemsHolder.getItem(saveData.itemKey),
+                _amount = saveData.amount,
+            ).apply {
+                velocity.x = saveData.velocityX
+                velocity.y = saveData.velocityY
+                pickedUp = saveData.pickedUp
+            }
+        }
+    }
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/drop/DropController.java b/core/src/ru/deadsoftware/cavedroid/game/objects/drop/DropController.java
new file mode 100644 (file)
index 0000000..7b99676
--- /dev/null
@@ -0,0 +1,88 @@
+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.dto.SaveDataDto;
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
+import ru.deadsoftware.cavedroid.game.model.item.Item;
+import ru.deadsoftware.cavedroid.misc.Saveable;
+
+import javax.inject.Inject;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+@GameScope
+public class DropController implements Saveable {
+
+    private static final int SAVE_DATA_VERSION = 1;
+
+    public interface Callback {
+        void run(Drop drop);
+    }
+
+    private final LinkedList<Drop> mDrops = new LinkedList<>();
+
+    @Inject
+    public DropController() {
+    }
+
+    public void initDrops(GameItemsHolder gameItemsHolder) {
+        for (Drop drop : mDrops) {
+            drop.initItem(gameItemsHolder);
+        }
+    }
+
+    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, count));
+    }
+
+    public void addDrop(float x, float y, @NotNull InventoryItem invItem) {
+        addDrop(x, y, invItem.getItem(), invItem.getAmount());
+    }
+
+    public int getSize() {
+        return mDrops.size();
+    }
+
+    public void forEach(Callback callback) {
+        for (Drop drop : mDrops) {
+            callback.run(drop);
+        }
+    }
+
+    public Iterator<Drop> getIterator() {
+        return mDrops.iterator();
+    }
+
+    @Override
+    @NotNull
+    public SaveDataDto.DropControllerSaveData getSaveData() {
+        final LinkedList<SaveDataDto.DropSaveData> dropSaveData = new LinkedList<>();
+        for (Drop drop : mDrops) {
+            dropSaveData.add(drop.getSaveData());
+        }
+        return new SaveDataDto.DropControllerSaveData(SAVE_DATA_VERSION, dropSaveData);
+    }
+
+    public static DropController fromSaveData(
+            SaveDataDto.DropControllerSaveData saveData, GameItemsHolder gameItemsHolder) {
+        saveData.verifyVersion(SAVE_DATA_VERSION);
+
+        DropController controller = new DropController();
+
+        for (SaveDataDto.DropSaveData dropSaveData : saveData.getDrops()) {
+            controller.mDrops.add(Drop.Companion.fromSaveData(dropSaveData, gameItemsHolder));
+        }
+
+        return controller;
+    }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/package-info.java b/core/src/ru/deadsoftware/cavedroid/game/objects/package-info.java
deleted file mode 100644 (file)
index c5856a0..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@NonnullByDefault
-package ru.deadsoftware.cavedroid.game.objects;
-
-import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault;
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/package-info.java b/core/src/ru/deadsoftware/cavedroid/game/package-info.java
deleted file mode 100644 (file)
index 6290746..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@NonnullByDefault
-package ru.deadsoftware.cavedroid.game;
-
-import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault;
\ No newline at end of file
index aae4f9891e26212224ec020b8be8ce67f910faf8..0d345403c17e443dc418136bf16331ee6a3c6f97 100644 (file)
@@ -8,10 +8,12 @@ import com.badlogic.gdx.math.Rectangle
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindRenderer
 import ru.deadsoftware.cavedroid.misc.utils.forEachBlockInArea
 import javax.inject.Inject
 
 @GameScope
+@BindRenderer
 class BackgroundBlocksRenderer @Inject constructor(
     gameWorld: GameWorld,
     mobsController: MobsController
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 03f62534522264f008936a451faaa9d2139ea989..0fa27560c6d372c576aaaa6f4926d1d08519f2e2 100644 (file)
@@ -10,7 +10,7 @@ import ru.deadsoftware.cavedroid.game.debug.DebugInfoStringsProvider
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.model.block.Block
 import ru.deadsoftware.cavedroid.game.world.GameWorld
-import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindRenderer
 import ru.deadsoftware.cavedroid.misc.utils.bl
 import ru.deadsoftware.cavedroid.misc.utils.drawString
 import ru.deadsoftware.cavedroid.misc.utils.forEachBlockInArea
@@ -18,6 +18,7 @@ import ru.deadsoftware.cavedroid.misc.utils.px
 import javax.inject.Inject
 
 @GameScope
+@BindRenderer
 class DebugRenderer @Inject constructor(
     private val mainConfig: MainConfig,
     private val gameWorld: GameWorld,
index a17bd613002ddb1922151bb6d3df31b92f92c969..d1fc65ab8ee654ec62936b687b4341cc92554f6d 100644 (file)
@@ -4,14 +4,16 @@ 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.annotations.multibinding.BindRenderer
 import ru.deadsoftware.cavedroid.misc.utils.cycledInsideWorld
 import ru.deadsoftware.cavedroid.misc.utils.drawSprite
 import ru.deadsoftware.cavedroid.misc.utils.px
 import javax.inject.Inject
 
 @GameScope
+@BindRenderer
 class DropsRenderer @Inject constructor(
     private val dropController: DropController,
     private val gameWorld: GameWorld,
index ef8be714220b88ce94b849893be3883cc9d14a31..8353b0b39bc6262565d0062611df0d8eae84633d 100644 (file)
@@ -6,10 +6,12 @@ import com.badlogic.gdx.math.Rectangle
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindRenderer
 import ru.deadsoftware.cavedroid.misc.utils.forEachBlockInArea
 import javax.inject.Inject
 
 @GameScope
+@BindRenderer
 class ForegroundBlocksRenderer @Inject constructor(
     gameWorld: GameWorld,
     mobsController: MobsController
index b3f89909e4620db19e5b177cbe132319bad0bc96..b6b22aeaa13ca621c96f2f9bc5d7c184c0695324 100644 (file)
@@ -5,16 +5,22 @@ 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.annotations.multibinding.BindRenderer
+import ru.deadsoftware.cavedroid.misc.utils.drawString
 import ru.deadsoftware.cavedroid.misc.utils.px
 import javax.inject.Inject
 
 @GameScope
+@BindRenderer
 class HudRenderer @Inject constructor(
     private val gameWorld: GameWorld,
     private val mobsController: MobsController,
+    private val tooltipManager: TooltipManager,
 ) : IGameRenderer {
 
     override val renderLayer = RENDER_LAYER
@@ -23,6 +29,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 +52,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 +94,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 +107,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 +131,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 6d388bb94aa7c8a98d3222895a00273c7a094b20..52ac971954e64483000608a839db82efe8b1db5a 100644 (file)
@@ -7,11 +7,13 @@ 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.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindRenderer
 import ru.deadsoftware.cavedroid.misc.utils.cycledInsideWorld
 import ru.deadsoftware.cavedroid.misc.utils.px
 import javax.inject.Inject
 
 @GameScope
+@BindRenderer
 class MobsRenderer @Inject constructor(
     private val mobsController: MobsController,
     private val gameWorld: GameWorld,
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/RenderModule.kt b/core/src/ru/deadsoftware/cavedroid/game/render/RenderModule.kt
deleted file mode 100644 (file)
index 9ae3b46..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-package ru.deadsoftware.cavedroid.game.render
-
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.IntoSet
-import ru.deadsoftware.cavedroid.game.GameScope
-
-@Module
-object RenderModule {
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindBackGroundBlocksRenderer(renderer: BackgroundBlocksRenderer): IGameRenderer = renderer
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindForegroundBlocksRenderer(renderer: ForegroundBlocksRenderer): IGameRenderer = renderer
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindMobsRenderer(renderer: MobsRenderer): IGameRenderer = renderer
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindDropsRenderer(renderer: DropsRenderer): IGameRenderer = renderer
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindHudRenderer(renderer: HudRenderer): IGameRenderer = renderer
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindWindowsRenderer(renderer: WindowsRenderer): IGameRenderer = renderer
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindTouchControlsRenderer(renderer: TouchControlsRenderer): IGameRenderer = renderer
-
-    @Binds
-    @IntoSet
-    @GameScope
-    fun bindDebugRenderer(renderer: DebugRenderer): IGameRenderer = renderer
-
-//    @Provides
-//    @GameScope
-//    fun provideGameRenderers(renderers: Set<@JvmSuppressWildcards IGameRenderer>): List<IGameRenderer> {
-//        return renderers.asSequence()
-//            .sortedWith(Comparator.comparingInt(IGameRenderer::renderLayer))
-//            .toList()
-//    }
-
-}
index 5c95d696c675cadba52dcaf7fd1d8186e9a79491..f2198aac4bfb0409960fe1b7f5cd87884a579424 100644 (file)
@@ -6,15 +6,19 @@ 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.annotations.multibinding.BindRenderer
 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
+@BindRenderer
 class TouchControlsRenderer @Inject constructor(
     private val mainConfig: MainConfig,
     private val mobsController: MobsController,
@@ -25,6 +29,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 +72,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..945bb1ec458991b9e4487636895f53914c77d376 100644 (file)
@@ -6,18 +6,20 @@ 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 ru.deadsoftware.cavedroid.misc.annotations.multibinding.BindRenderer
 import javax.inject.Inject
 
 @GameScope
+@BindRenderer
 class WindowsRenderer @Inject constructor(
     private val creativeWindowRenderer: CreativeWindowRenderer,
     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 +29,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/save/GameSaveData.kt b/core/src/ru/deadsoftware/cavedroid/game/save/GameSaveData.kt
new file mode 100644 (file)
index 0000000..e0d64e7
--- /dev/null
@@ -0,0 +1,55 @@
+package ru.deadsoftware.cavedroid.game.save
+
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+
+class GameSaveData(
+    private var mobsController: MobsController?,
+    private var dropController: DropController?,
+    private var containerController: ContainerController?,
+    private var foreMap: Array<Array<Block>>?,
+    private var backMap: Array<Array<Block>>?
+) {
+
+    fun retrieveMobsController(): MobsController {
+        val value = requireNotNull(mobsController)
+        mobsController = null
+        return value
+    }
+
+    fun retrieveDropController(): DropController {
+        val value = requireNotNull(dropController)
+        dropController = null
+        return value
+    }
+
+    fun retrieveContainerController(): ContainerController {
+        val value = requireNotNull(containerController)
+        containerController = null
+        return value
+    }
+
+    fun retrieveForeMap(): Array<Array<Block>> {
+        val value = requireNotNull(foreMap)
+        foreMap = null
+        return value
+    }
+
+    fun retrieveBackMap(): Array<Array<Block>> {
+        val value = requireNotNull(backMap)
+        backMap = null
+        return value
+    }
+
+    fun isEmpty(): Boolean {
+        return mobsController == null &&
+                dropController == null &&
+                containerController == null &&
+                foreMap == null &&
+                backMap == null
+    }
+
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/save/GameSaveLoader.kt b/core/src/ru/deadsoftware/cavedroid/game/save/GameSaveLoader.kt
new file mode 100644 (file)
index 0000000..bd8c3d6
--- /dev/null
@@ -0,0 +1,255 @@
+package ru.deadsoftware.cavedroid.game.save
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.files.FileHandle
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.decodeFromByteArray
+import kotlinx.serialization.encodeToByteArray
+import kotlinx.serialization.protobuf.ProtoBuf
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+import ru.deadsoftware.cavedroid.game.objects.container.ContainerController
+import ru.deadsoftware.cavedroid.game.objects.drop.DropController
+import ru.deadsoftware.cavedroid.game.ui.TooltipManager
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import java.nio.ByteBuffer
+import java.util.zip.GZIPInputStream
+import java.util.zip.GZIPOutputStream
+
+@OptIn(ExperimentalSerializationApi::class)
+object GameSaveLoader {
+
+    private const val MAP_SAVE_VERSION: UByte = 2u
+
+    private const val SAVES_DIR = "/saves"
+    private const val DROP_FILE = "/drop.dat"
+    private const val MOBS_FILE = "/mobs.dat"
+    private const val CONTAINERS_FILE = "/containers.dat"
+    private const val DICT_FILE = "/dict"
+    private const val FOREMAP_FILE = "/foremap.dat.gz"
+    private const val BACKMAP_FILE = "/backmap.dat.gz"
+
+    private fun Int.toByteArray(): ByteArray {
+        return ByteBuffer.allocate(Int.SIZE_BYTES)
+            .putInt(this)
+            .array()
+    }
+
+    private fun Short.toByteArray(): ByteArray {
+        return ByteBuffer.allocate(Short.SIZE_BYTES)
+            .putShort(this)
+            .array()
+    }
+
+    private fun buildBlocksDictionary(
+        foreMap: Array<Array<Block>>,
+        backMap: Array<Array<Block>>
+    ): Map<String, Int> {
+        val maps = sequenceOf(foreMap.asSequence(), backMap.asSequence())
+
+        return maps.flatten()
+            .flatMap(Array<Block>::asSequence)
+            .toSet()
+            .mapIndexed { index, block -> block.params.key to index }
+            .toMap()
+    }
+
+    private fun saveDict(file: FileHandle, dict: Map<String, Int>) {
+        val result = dict.asSequence()
+            .sortedBy { it.value }
+            .joinToString(separator = "\n") { it.key }
+            .encodeToByteArray()
+
+        file.writeBytes(result, false)
+    }
+
+    private fun compressMap(map: Array<Array<Block>>, dict: Map<String, Int>): ByteArray {
+        if (dict.size > 0xff) {
+            throw IllegalArgumentException("Cannot save this map as bytes")
+        }
+
+        val width = map.size
+        val height = map[0].size
+
+        val blocks = sequence {
+            for (y in 0 ..< height) {
+                for (x in 0 ..< width) {
+                    yield(map[x][y])
+                }
+            }
+        }
+
+        val result = sequence {
+            var run = 0
+            var runValue: UByte? = null
+
+            yield(MAP_SAVE_VERSION.toByte())
+            width.toByteArray().forEach { yield(it) }
+            height.toByteArray().forEach { yield(it) }
+
+            blocks.forEach { block ->
+                val key = block.params.key
+
+                val blockId = dict[key]?.toUByte()
+                    ?: throw IllegalArgumentException("Dictionary does not contain key $key")
+
+                if (blockId != runValue || run == Int.MAX_VALUE) {
+                    if (run > 0 && runValue != null) {
+                        run.toByteArray().forEach { yield(it) }
+                        yield(runValue!!.toByte())
+                    }
+                    run = 1
+                    runValue = blockId
+                } else {
+                    run++
+                }
+            }
+
+            run.toByteArray().forEach { yield(it) }
+            yield(runValue!!.toByte())
+        }
+
+        return result.toList().toByteArray()
+    }
+
+    private fun decompressMap(
+        bytes: ByteArray,
+        dict: List<String>,
+        gameItemsHolder: GameItemsHolder
+    ): Array<Array<Block>> {
+        val version = bytes.first().toUByte()
+        require(version == MAP_SAVE_VERSION)
+
+        val width = ByteBuffer.wrap(bytes, 1, Int.SIZE_BYTES).getInt()
+        val height = ByteBuffer.wrap(bytes, 1 + Int.SIZE_BYTES, Int.SIZE_BYTES).getInt()
+
+        val blocks = buildList {
+            for (i in 1 + (Int.SIZE_BYTES shl 1) .. bytes.lastIndex step Int.SIZE_BYTES + 1) {
+                val run = ByteBuffer.wrap(bytes, i, Int.SIZE_BYTES).getInt()
+                val blockId = bytes[i + Int.SIZE_BYTES].toUByte().toInt()
+
+                for (j in 0 ..< run) {
+                    add(gameItemsHolder.getBlock(dict[blockId]))
+                }
+            }
+        }
+
+        return Array(width) { x ->
+            Array(height) { y ->
+                blocks[x + y * width]
+            }
+        }
+    }
+
+    private fun loadMap(
+        gameItemsHolder: GameItemsHolder,
+        savesPath: String
+    ): Pair<Array<Array<Block>>, Array<Array<Block>>> {
+        val dict = Gdx.files.absolute("$savesPath$DICT_FILE").readString().split("\n")
+
+        val foreMap: Array<Array<Block>>
+        with(GZIPInputStream(Gdx.files.absolute("$savesPath$FOREMAP_FILE").read())) {
+            foreMap = decompressMap(readBytes(), dict, gameItemsHolder)
+            close()
+        }
+
+        val backMap: Array<Array<Block>>
+        with(GZIPInputStream(Gdx.files.absolute("$savesPath$BACKMAP_FILE").read())) {
+            backMap = decompressMap(readBytes(), dict, gameItemsHolder)
+            close()
+        }
+
+        return foreMap to backMap
+    }
+
+    private fun saveMap(gameWorld: GameWorld, savesPath: String) {
+        val fullForeMap = gameWorld.fullForeMap
+        val fullBackMap = gameWorld.fullBackMap
+
+        val dict = buildBlocksDictionary(fullForeMap, fullBackMap)
+
+        saveDict(Gdx.files.absolute("$savesPath$DICT_FILE"), dict)
+
+        with(GZIPOutputStream(Gdx.files.absolute("$savesPath$FOREMAP_FILE").write(false))) {
+            write(compressMap(fullForeMap, dict))
+            close()
+        }
+
+        with(GZIPOutputStream(Gdx.files.absolute("$savesPath$BACKMAP_FILE").write(false))) {
+            write(compressMap(fullBackMap, dict))
+            close()
+        }
+    }
+
+    fun load(
+        mainConfig: MainConfig,
+        gameItemsHolder: GameItemsHolder,
+        tooltipManager: TooltipManager
+    ): GameSaveData {
+        val gameFolder = mainConfig.gameFolder
+        val savesPath = "$gameFolder$SAVES_DIR"
+
+        val dropFile = Gdx.files.absolute("$savesPath$DROP_FILE")
+        val mobsFile = Gdx.files.absolute("$savesPath$MOBS_FILE")
+        val containersFile = Gdx.files.absolute("$savesPath$CONTAINERS_FILE")
+
+        val dropBytes = dropFile.readBytes()
+        val mobsBytes = mobsFile.readBytes()
+        val containersBytes = containersFile.readBytes()
+
+        val dropController = ProtoBuf.decodeFromByteArray<SaveDataDto.DropControllerSaveData>(dropBytes)
+            .let { saveData -> DropController.fromSaveData(saveData, gameItemsHolder) }
+        val mobsController = ProtoBuf.decodeFromByteArray<SaveDataDto.MobsControllerSaveData>(mobsBytes)
+            .let { saveData -> MobsController.fromSaveData(saveData, gameItemsHolder, tooltipManager) }
+        val containerController = ProtoBuf.decodeFromByteArray<SaveDataDto.ContainerControllerSaveData>(containersBytes)
+            .let { saveData -> ContainerController.fromSaveData(saveData, dropController, gameItemsHolder) }
+
+        val (foreMap, backMap) = loadMap(gameItemsHolder, savesPath)
+
+        return GameSaveData(mobsController, dropController, containerController, foreMap, backMap)
+    }
+
+    fun save(
+        mainConfig: MainConfig,
+        dropController: DropController,
+        mobsController: MobsController,
+        containerController: ContainerController,
+        gameWorld: GameWorld
+    ) {
+        val gameFolder = mainConfig.gameFolder
+        val savesPath = "$gameFolder$SAVES_DIR"
+
+        Gdx.files.absolute(savesPath).mkdirs()
+
+        val dropFile = Gdx.files.absolute("$savesPath$DROP_FILE")
+        val mobsFile = Gdx.files.absolute("$savesPath$MOBS_FILE")
+        val containersFile = Gdx.files.absolute("$savesPath$CONTAINERS_FILE")
+
+        val dropBytes = ProtoBuf.encodeToByteArray(dropController.getSaveData())
+        val mobsBytes = ProtoBuf.encodeToByteArray(mobsController.getSaveData())
+        val containersBytes = ProtoBuf.encodeToByteArray(containerController.getSaveData())
+
+        dropFile.writeBytes(dropBytes, false)
+        mobsFile.writeBytes(mobsBytes, false)
+        containersFile.writeBytes(containersBytes, false)
+
+        saveMap(gameWorld, savesPath)
+    }
+
+    fun exists(mainConfig: MainConfig): Boolean {
+        val gameFolder = mainConfig.gameFolder
+        val savesPath = "$gameFolder$SAVES_DIR"
+
+        return Gdx.files.absolute("$savesPath$DROP_FILE").exists() &&
+                Gdx.files.absolute("$savesPath$MOBS_FILE").exists() &&
+                Gdx.files.absolute("$savesPath$CONTAINERS_FILE").exists() &&
+                Gdx.files.absolute("$savesPath$DICT_FILE").exists() &&
+                Gdx.files.absolute("$savesPath$FOREMAP_FILE").exists() &&
+                Gdx.files.absolute("$savesPath$BACKMAP_FILE").exists()
+    }
+
+
+}
\ No newline at end of file
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..b53e0a628e23f4e466bc5ea08492aecbe1c243eb 100644 (file)
@@ -1,42 +1,53 @@
 package ru.deadsoftware.cavedroid.game.world;
 
+import com.badlogic.gdx.math.MathUtils;
 import kotlin.Pair;
+import org.jetbrains.annotations.Nullable;
 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;
 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,
-                     @CheckForNull Block[][] foreMap,
-                     @CheckForNull Block[][] backMap) {
+                     ContainerController containerController,
+                     @Nullable Block[][] foreMap,
+                     @Nullable 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());
     }
+
+    @Nullable
+    private Container getContainerAt(int x, int y, int z) {
+        return mContainerController.getContainer(transformX(x), y, z);
+    }
+
+    @Nullable
+    public Container getForegroundContainer(int x, int y) {
+        return getContainerAt(x, y, FOREGROUND_Z);
+    }
+
+    @Nullable
+    public Container getBackgroundContainer(int x, int y) {
+        return getContainerAt(x, y, BACKGROUND_Z);
+    }
+
+    @Nullable
+    public Furnace getForegroundFurnace(int x, int y) {
+        @Nullable
+        final Container container = getForegroundContainer(x, y);
+
+        if (container instanceof Furnace) {
+            return (Furnace) container;
+        }
+
+        return null;
+    }
+
+    @Nullable
+    public Furnace getBackgroundFurnace(int x, int y) {
+        @Nullable
+        final Container container = getBackgroundContainer(x, y);
+
+        if (container instanceof Furnace) {
+            return (Furnace) container;
+        }
+
+        return null;
+    }
 }
\ No newline at end of file
index 387407a841e482fc85dd50a7ff8ab7af1c0f280c..e92b9e2ffb6f6278a01a2fe9d4d82c63454368a4 100644 (file)
@@ -33,7 +33,7 @@ class GameWorldBlocksLogicControllerTask @Inject constructor(
 
         val blockKey = block.params.key
         val action = updateBlockActions[blockKey]
-            ?: updateBlockActions.getRequiresBlockAction().takeIf { block.params.requiresBlock }
+            ?: updateBlockActions.getRequiresBlockAction().takeIf { block.params.run { requiresBlock || isFallable } }
 
         action?.update(x, y)
     }
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java
deleted file mode 100644 (file)
index 416721f..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-package ru.deadsoftware.cavedroid.game.world;
-
-import com.badlogic.gdx.utils.Timer;
-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.block.Block;
-
-import javax.annotation.CheckForNull;
-import javax.inject.Inject;
-import java.util.*;
-
-@GameScope
-public class GameWorldFluidsLogicControllerTask extends Timer.Task {
-
-    public static final float FLUID_UPDATE_INTERVAL_SEC = 0.25f;
-
-    private short mUpdateTick = 0;
-
-    private final GameWorld mGameWorld;
-    private final MobsController mMobsController;
-    private final GameItemsHolder mGameItemsHolder;
-
-    private final Map<Class<? extends Block.Fluid>, List<? extends Block.Fluid>> mFluidStatesMap;
-
-    private final class UpdateCommand {
-        final Runnable command;
-        final int priority;
-
-        private UpdateCommand(int priority, Runnable command) {
-            this.priority = priority;
-            this.command = command;
-        }
-
-        private UpdateCommand(Block block, int x, int y, int priority) {
-            this(priority, () -> mGameWorld.setForeMap(x, y, block));
-        }
-
-        private UpdateCommand(Block.Fluid fluid, int x, int y) {
-            this(fluid, x, y, ((5 -fluid.getState() )+ 1) * (fluid.isLava() ? 2 : 1));
-        }
-
-        private int getPriority() {
-            return priority;
-        }
-
-        private void exec() {
-           command.run();
-        }
-    }
-
-    private final PriorityQueue<UpdateCommand> mUpdateQueue
-            = new PriorityQueue<>(Comparator.comparingInt(UpdateCommand::getPriority));
-
-    @Inject
-    GameWorldFluidsLogicControllerTask(GameWorld gameWorld,
-                                       MobsController mobsController,
-                                       GameItemsHolder gameItemsHolder) {
-        mGameWorld = gameWorld;
-        mMobsController = mobsController;
-        mGameItemsHolder = gameItemsHolder;
-
-        final List<Block.Water> waters = mGameItemsHolder.getBlocksByType(Block.Water.class);
-        waters.sort(Comparator.comparingInt(Block.Water::getState));
-
-        final List<Block.Lava> lavas = mGameItemsHolder.getBlocksByType(Block.Lava.class);
-        lavas.sort(Comparator.comparingInt(Block.Lava::getState));
-
-        mFluidStatesMap = new HashMap<>();
-        mFluidStatesMap.put(Block.Water.class, waters);
-        mFluidStatesMap.put(Block.Lava.class, lavas);
-    }
-
-    @CheckForNull
-    private List<? extends Block.Fluid> getFluidStateList(Block.Fluid fluid) {
-        return mFluidStatesMap.get(fluid.getClass());
-    }
-
-    private int getCurrentStateIndex(Block.Fluid fluid) {
-        @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
-
-        if (stateList == null) {
-            return -1;
-        }
-
-        return stateList.indexOf(fluid);
-    }
-
-    @CheckForNull
-    private Block.Fluid getNextStateBlock(Block.Fluid fluid) {
-        @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
-
-        if (stateList == null) {
-            return null;
-        }
-
-        int currentState = stateList.indexOf(fluid);
-
-        if (currentState < 0) {
-            return null;
-        }
-
-        int nextState = currentState + 1;
-
-        if (nextState == 1) {
-            nextState++;
-        }
-
-        if (nextState < stateList.size()) {
-            return stateList.get(nextState);
-        }
-
-        return null;
-    }
-
-    private boolean noFluidNearby(int x, int y) {
-        return !mGameWorld.getForeMap(x, y - 1).isFluid() &&
-                (!mGameWorld.getForeMap(x - 1, y).isFluid() || ((Block.Fluid)mGameWorld.getForeMap(x - 1, y)).getState() >= ((Block.Fluid)mGameWorld.getForeMap(x, y)).getState()) &&
-                (!mGameWorld.getForeMap(x + 1, y).isFluid() || ((Block.Fluid)mGameWorld.getForeMap(x + 1, y)).getState() >= ((Block.Fluid)mGameWorld.getForeMap(x, y)).getState());
-    }
-
-    private boolean drainFluid(int x, int y) {
-        final Block block = mGameWorld.getForeMap(x, y);
-
-        if (!(block instanceof Block.Fluid fluid)) {
-            return true;
-        }
-
-        if (fluid.getState() > 0) {
-            if (noFluidNearby(x, y)) {
-                @CheckForNull final Block.Fluid nextState = getNextStateBlock(fluid);
-                if (nextState == null) {
-                    mUpdateQueue.offer(new UpdateCommand(-1, () -> mGameWorld.resetForeMap(x, y)));
-                    return true;
-                }
-
-                mUpdateQueue.offer(new UpdateCommand(nextState, x, y));
-            }
-        }
-        return false;
-    }
-
-    private boolean fluidCanFlowThere(Block.Fluid fluid, Block targetBlock) {
-        return targetBlock == mGameItemsHolder.getFallbackBlock() ||
-                (!targetBlock.getParams().getHasCollision() && !targetBlock.isFluid()) ||
-                (fluid.getClass() == targetBlock.getClass() && fluid.getState() < ((Block.Fluid)targetBlock).getState());
-    }
-
-    private void flowFluidTo(Block.Fluid currentFluid, int x, int y, Block.Fluid nextStateFluid) {
-        final Block targetBlock = mGameWorld.getForeMap(x, y);
-
-        if (fluidCanFlowThere(currentFluid, targetBlock)) {
-            mUpdateQueue.offer(new UpdateCommand(nextStateFluid, x, y));
-        } else if (currentFluid.isWater() && targetBlock.isLava()) {
-            if (((Block.Lava)targetBlock).getState() > 0) {
-                mUpdateQueue.offer(new UpdateCommand(100, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("cobblestone"))));
-            } else {
-                mUpdateQueue.offer(new UpdateCommand(300, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("obsidian"))));
-            }
-        } else if (currentFluid.isLava() && targetBlock.isWater()) {
-            mUpdateQueue.offer(new UpdateCommand(200, () -> mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("stone"))));
-        }
-    }
-
-    private void flowFluid(int x, int y) {
-        Block.Fluid fluid = (Block.Fluid) mGameWorld.getForeMap(x, y);
-        @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
-
-        if (stateList == null) {
-            return;
-        }
-
-        if (fluid.getState() < stateList.size() - 1 && mGameWorld.getForeMap(x, y + 1).hasCollision()) {
-            @CheckForNull Block.Fluid nextState = getNextStateBlock(fluid);
-
-            if (nextState == null) {
-                return;
-            }
-
-            flowFluidTo(fluid, x - 1, y, nextState);
-            flowFluidTo(fluid, x + 1, y, nextState);
-        } else {
-            flowFluidTo(fluid, x, y + 1, stateList.get(1));
-        }
-
-    }
-
-    private void updateFluids(int x, int y) {
-        final Block block = mGameWorld.getForeMap(x, y);
-        if (!block.isFluid() || (block.isLava() && mUpdateTick % 2 == 0)) {
-            return;
-        }
-        if (drainFluid(x, y)) {
-            return;
-        }
-        flowFluid(x, y);
-    }
-
-    private void fluidUpdater() {
-        int midScreen = (int) mMobsController.getPlayer().x / 16;
-        for (int y = mGameWorld.getHeight() - 1; y >= 0; y--) {
-            for (int x = 0; x <= Math.min(mGameWorld.getWidth() / 2, 32); x++) {
-                updateFluids(midScreen + x, y);
-                updateFluids(midScreen - x, y);
-            }
-        }
-
-        while (!mUpdateQueue.isEmpty()) {
-            final UpdateCommand command = mUpdateQueue.poll();
-            command.exec();
-        }
-    }
-
-    @Override
-    public void run() {
-        if (mUpdateTick < 0xFF) {
-            mUpdateTick ++;
-        } else {
-            mUpdateTick = 0;
-        }
-        fluidUpdater();
-    }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.kt b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.kt
new file mode 100644 (file)
index 0000000..d0f4eaa
--- /dev/null
@@ -0,0 +1,189 @@
+package ru.deadsoftware.cavedroid.game.world
+
+import com.badlogic.gdx.utils.Timer
+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.block.Block
+import ru.deadsoftware.cavedroid.misc.utils.bl
+import java.util.PriorityQueue
+import javax.inject.Inject
+import kotlin.math.min
+import kotlin.reflect.KClass
+
+@GameScope
+class GameWorldFluidsLogicControllerTask @Inject constructor(
+    private val gameWorld: GameWorld,
+    private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
+) : Timer.Task() {
+
+    private var updateTick: Short = 0;
+
+    private val fluidStatesMap = mutableMapOf<KClass<out Block.Fluid>, List<Block.Fluid>>()
+
+    private val updateQueue = PriorityQueue<UpdateCommand>(16) { c1, c2 ->
+        c1.priority.compareTo(c2.priority)
+    }
+
+    init {
+        val waters = gameItemsHolder.getBlocksByType(Block.Water::class.java)
+            .sortedBy(Block.Water::state)
+        val lavas = gameItemsHolder.getBlocksByType(Block.Lava::class.java)
+            .sortedBy(Block.Lava::state)
+
+        fluidStatesMap[Block.Water::class] = waters
+        fluidStatesMap[Block.Lava::class] = lavas
+    }
+
+    private fun getNextStateBlock(fluid: Block.Fluid): Block.Fluid? {
+        val stateList = fluidStatesMap[fluid::class] ?: return null
+        val currentState = stateList.indexOf(fluid)
+            .takeIf { it >= 0 } ?: return null
+
+        var nextState = currentState + 1
+
+        if (nextState == 1) {
+            nextState++
+        }
+
+        if (nextState < stateList.size) {
+            return stateList[nextState]
+        }
+
+        return null
+    }
+
+    private fun noFluidNearby(x: Int, y: Int): Boolean {
+        val current = gameWorld.getForeMap(x, y)
+
+        if (current !is Block.Fluid) {
+            throw IllegalArgumentException("block at $x;$y is not a fluid")
+        }
+
+        val onTop = gameWorld.getForeMap(x, y - 1)
+        val onLeft = gameWorld.getForeMap(x - 1, y)
+        val onRight = gameWorld.getForeMap(x + 1, y)
+
+        return !onTop.isFluid() &&
+                (onLeft !is Block.Fluid || onLeft.state >= current.state) &&
+                (onRight !is Block.Fluid || onRight.state >= current.state)
+    }
+
+    private fun drainFluid(x: Int, y: Int): Boolean {
+        val fluid = (gameWorld.getForeMap(x, y) as? Block.Fluid)
+            ?: return true
+
+        if (fluid.state > 0) {
+            if (noFluidNearby(x, y)) {
+                val nexState = getNextStateBlock(fluid)
+                if (nexState == null) {
+                    updateQueue.offer(UpdateCommand(-1) { gameWorld.resetForeMap(x, y) })
+                    return true
+                }
+                updateQueue.offer(UpdateCommand(nexState, x, y))
+            }
+        }
+
+        return false
+    }
+
+    private fun fluidCanFlowThere(fluid: Block.Fluid, targetBlock: Block): Boolean {
+        return targetBlock.isNone() ||
+                (!targetBlock.params.hasCollision && !targetBlock.isFluid()) ||
+                (fluid::class == targetBlock::class && fluid.state < (targetBlock as Block.Fluid).state)
+    }
+
+    private fun flowFluidTo(currentFluid: Block.Fluid, x: Int, y: Int, nextStateFluid: Block.Fluid) {
+        val targetBlock = gameWorld.getForeMap(x, y)
+
+        val command = when {
+            fluidCanFlowThere(currentFluid, targetBlock) -> UpdateCommand(nextStateFluid, x, y)
+
+            currentFluid.isWater() && targetBlock is Block.Lava && targetBlock.state > 0 ->
+                UpdateCommand(100) { gameWorld.setForeMap(x, y, gameItemsHolder.getBlock("cobblestone")) }
+
+            currentFluid.isWater() && targetBlock.isLava() ->
+                UpdateCommand(100) { gameWorld.setForeMap(x, y, gameItemsHolder.getBlock("obsidian")) }
+
+            currentFluid.isLava() && targetBlock.isWater() ->
+                UpdateCommand(200) { gameWorld.setForeMap(x, y, gameItemsHolder.getBlock("stone")) }
+
+            else -> null
+        }
+
+        command?.let(updateQueue::offer)
+    }
+
+    private fun flowFluid(x: Int, y: Int) {
+        val fluid = gameWorld.getForeMap(x, y) as Block.Fluid
+        val stateList = fluidStatesMap[fluid::class] ?: return
+
+          if (fluid.state < stateList.lastIndex && gameWorld.getForeMap(x, y + 1).params.hasCollision) {
+              val nextState = getNextStateBlock(fluid) ?: return
+
+              flowFluidTo(fluid, x - 1, y, nextState)
+              flowFluidTo(fluid, x + 1, y, nextState)
+          } else {
+              flowFluidTo(fluid, x, y + 1, stateList[1])
+          }
+    }
+
+    fun updateFluids(x: Int, y: Int) {
+        val block = gameWorld.getForeMap(x, y)
+        if (!block.isFluid() || (block.isLava() && updateTick % 2 == 0)) {
+            return
+        }
+
+        if (drainFluid(x, y)) {
+            return
+        }
+
+        flowFluid(x, y)
+    }
+
+    private fun fluidUpdater() {
+        val midScreen = mobsController.player.x.bl
+
+        for (y in gameWorld.height - 1 downTo 0) {
+            for (x in 0 ..< min(gameWorld.width / 2, 32)) {
+                updateFluids(midScreen + x, y)
+                updateFluids(midScreen - x, y)
+            }
+        }
+
+        while (!updateQueue.isEmpty()) {
+            updateQueue.poll().exec()
+        }
+    }
+
+    override fun run() {
+        if (updateTick < 0xFF) {
+            updateTick++
+        } else {
+            updateTick = 1
+        }
+
+        fluidUpdater()
+    }
+
+    private inner class UpdateCommand(
+        val priority: Int,
+        val command: Runnable
+    ) {
+
+        constructor(block: Block, x: Int, y: Int, priority: Int) :
+                this(priority, Runnable { gameWorld.setForeMap(x, y, block) })
+
+        constructor(fluid: Block.Fluid, x: Int, y: Int) :
+                this(fluid, x, y, ((5 - fluid.state) + 1) * (if (fluid.isLava()) 2 else 1))
+
+        fun exec() = command.run()
+
+    }
+
+    companion object {
+        const val FLUID_UPDATE_INTERVAL_SEC = 0.25f
+    }
+
+}
\ No newline at end of file
index e00a0ea4dd2cd3397b632650b69b635cdc1ec24f..9562f08b192462e95b1704b5e73a36fd20f395a4 100644 (file)
@@ -1,12 +1,12 @@
 package ru.deadsoftware.cavedroid.game.world
 
-import com.google.common.primitives.Ints.min
 import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.model.block.Block
 import ru.deadsoftware.cavedroid.game.model.world.Biome
 import ru.deadsoftware.cavedroid.game.model.world.generator.WorldGeneratorConfig
 import kotlin.math.abs
 import kotlin.math.max
+import kotlin.math.min
 import kotlin.random.Random
 
 class GameWorldGenerator(
@@ -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..aa3888a5b0c26181b38f1102694b8344e6ba7b9b 100644 (file)
@@ -5,13 +5,15 @@ 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.Method;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import static ru.deadsoftware.cavedroid.misc.Assets.*;
 
 @MenuScope
@@ -30,6 +32,10 @@ public class MenuProc extends Renderer {
             mMainConfig.getCaveGame().loadGame();
         }
 
+        public void optionsClicked() {
+            mCurrentMenu = mMenuOptions;
+        }
+
         public void quitClicked() {
             Gdx.app.exit();
         }
@@ -45,20 +51,24 @@ public class MenuProc extends Renderer {
         public void backClicked() {
             mCurrentMenu = mMenuMain;
         }
+
+        public void setPreference(String key, Object value) {
+            mMainConfig.setPreference(key, value.toString());
+        }
     }
 
     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 +76,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
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/objects/BooleanOptionButton.kt b/core/src/ru/deadsoftware/cavedroid/menu/objects/BooleanOptionButton.kt
new file mode 100644 (file)
index 0000000..56329d5
--- /dev/null
@@ -0,0 +1,29 @@
+package ru.deadsoftware.cavedroid.menu.objects
+
+import ru.deadsoftware.cavedroid.MainConfig
+
+class BooleanOptionButton(
+    private val mainConfig: MainConfig,
+    private val optionKey: String,
+    private val defaultValue: Boolean,
+    label: String,
+    x: Int,
+    y: Int,
+    type: Int,
+) : Button(
+    label,
+    x,
+    y,
+    type,
+    {
+        val current = (mainConfig.getPreference(optionKey)?.toBooleanStrictOrNull()) ?: defaultValue
+        mainConfig.setPreference(optionKey, (!current).toString())
+    }
+) {
+
+    override fun getLabel(): String {
+        val value = (mainConfig.getPreference(optionKey)?.toBooleanStrictOrNull()) ?: defaultValue
+        return super.getLabel().replace("%%value%%", value.toString())
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/objects/package-info.java b/core/src/ru/deadsoftware/cavedroid/menu/objects/package-info.java
deleted file mode 100644 (file)
index b132187..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@NonnullByDefault
-package ru.deadsoftware.cavedroid.menu.objects;
-
-import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault;
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/package-info.java b/core/src/ru/deadsoftware/cavedroid/menu/package-info.java
deleted file mode 100644 (file)
index 304c211..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@NonnullByDefault
-package ru.deadsoftware.cavedroid.menu;
-
-import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault;
\ No newline at end of file
index 587d0f622ebbc5df347d482734e75996bc006c3a..7e793b7b1db1d77a21a85dbb2faf30e4437974bd 100644 (file)
@@ -1,13 +1,16 @@
 package ru.deadsoftware.cavedroid.menu.submenus;
 
+import com.badlogic.gdx.Application;
 import com.badlogic.gdx.Gdx;
 import com.badlogic.gdx.files.FileHandle;
 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
 import com.badlogic.gdx.graphics.g2d.TextureRegion;
 import com.badlogic.gdx.utils.ArrayMap;
 import com.badlogic.gdx.utils.JsonValue;
+import kotlin.text.StringsKt;
 import ru.deadsoftware.cavedroid.MainConfig;
 import ru.deadsoftware.cavedroid.menu.MenuProc;
+import ru.deadsoftware.cavedroid.menu.objects.BooleanOptionButton;
 import ru.deadsoftware.cavedroid.menu.objects.Button;
 import ru.deadsoftware.cavedroid.menu.objects.ButtonEventListener;
 import ru.deadsoftware.cavedroid.menu.objects.ButtonRenderer;
@@ -76,13 +79,27 @@ public abstract class Menu {
         JsonValue json = Assets.jsonReader.parse(jsonFile);
         int y = (int) mHeight / 4;
         for (JsonValue key = json.child(); key != null; key = key.next(), y += Button.HEIGHT + 10) {
-            buttons.put(key.name(),
-                    new Button(Assets.getStringFromJson(key, "label", ""),
-                            (int) mWidth / 2 - Button.WIDTH / 2,
-                            Assets.getIntFromJson(key, "y", y),
-                            Assets.getIntFromJson(key, "type", Button.NORMAL),
-                            eventListeners.containsKey(key.name()) ? eventListeners.get(key.name()) : () -> {
-                            }));
+
+            if (Gdx.app.getType() == Application.ApplicationType.Android &&
+                    !Assets.getBooleanFromJson(key, "visible_on_android", true)) {
+                continue;
+            }
+
+            String optionType = Assets.getStringFromJson(key, "option_type", "");
+            String label = Assets.getStringFromJson(key, "label", "");
+            int x = (int) mWidth / 2 - Button.WIDTH / 2;
+            int type = Assets.getIntFromJson(key, "type", Button.NORMAL);
+            String defaultValue = Assets.getStringFromJson(key, "default_value", "");
+
+
+            Button button = switch (optionType) {
+                case "boolean" ->
+                    new BooleanOptionButton(mMainConfig, key.name(), Boolean.parseBoolean(defaultValue), label, x, y, type);
+                default ->
+                    new Button(label, x, y, type, eventListeners.containsKey(key.name()) ? eventListeners.get(key.name()) : () -> {});
+            };
+
+            buttons.put(key.name(), button);
         }
     }
 
index f71183a0583ce95b4c78fa760cb081e5323188e0..29aeb8fb272b53f58101634c15a23e38573b48d6 100644 (file)
@@ -1,8 +1,7 @@
 package ru.deadsoftware.cavedroid.menu.submenus;
 
-import com.badlogic.gdx.Gdx;
 import ru.deadsoftware.cavedroid.MainConfig;
-import ru.deadsoftware.cavedroid.game.GameSaver;
+import ru.deadsoftware.cavedroid.game.save.GameSaveLoader;
 import ru.deadsoftware.cavedroid.menu.MenuProc;
 import ru.deadsoftware.cavedroid.menu.objects.Button;
 import ru.deadsoftware.cavedroid.menu.objects.ButtonEventListener;
@@ -28,6 +27,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;
     }
@@ -35,7 +35,7 @@ public class MenuMain extends Menu {
     @Override
     protected void initButtons() {
         loadButtonsFromJson(mAssetLoader.getAssetHandle("json/menu_main_buttons.json"));
-        if (GameSaver.exists(mMainConfig)) {
+        if (GameSaveLoader.INSTANCE.exists(mMainConfig)) {
             getButtons().get("load_game").setType(Button.NORMAL);
         }
     }
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..7cc8a90
--- /dev/null
@@ -0,0 +1,27 @@
+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["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
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/package-info.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/package-info.java
deleted file mode 100644 (file)
index 6c28493..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@NonnullByDefault
-package ru.deadsoftware.cavedroid.menu.submenus;
-
-import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault;
\ No newline at end of file
index b7fd03e132063167fe37cc0ee2d97ac5f2b6090e..893414ba61c3aa3c5ee1700cbcec121aeef457fc 100644 (file)
@@ -41,9 +41,17 @@ 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);
+        for (Texture texture : loadedTextures) {
+            texture.dispose();
+        }
         loadedTextures.clear();
     }
     
@@ -73,14 +81,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 +108,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 +174,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 +188,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 +215,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);
     }
 
     /**
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/Saveable.kt b/core/src/ru/deadsoftware/cavedroid/misc/Saveable.kt
new file mode 100644 (file)
index 0000000..d350f1f
--- /dev/null
@@ -0,0 +1,7 @@
+package ru.deadsoftware.cavedroid.misc
+
+import ru.deadsoftware.cavedroid.game.model.dto.SaveDataDto
+
+interface Saveable {
+    fun getSaveData(): SaveDataDto
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/NonnullByDefault.java b/core/src/ru/deadsoftware/cavedroid/misc/annotations/NonnullByDefault.java
deleted file mode 100644 (file)
index bee4070..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-package ru.deadsoftware.cavedroid.misc.annotations;
-
-import javax.annotation.Nonnull;
-import javax.annotation.meta.TypeQualifierDefault;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import static java.lang.annotation.ElementType.*;
-
-@Nonnull
-@TypeQualifierDefault({METHOD, FIELD, PARAMETER})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface NonnullByDefault {
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindKeyboardInputHandler.kt
new file mode 100644 (file)
index 0000000..558dc96
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.misc.annotations.multibinding
+
+import ru.deadsoftware.cavedroid.game.input.IKeyboardInputHandler
+import ru.fredboy.automultibind.annotations.BindsIntoSet
+
+@BindsIntoSet(
+    interfaceClass = IKeyboardInputHandler::class,
+    modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE,
+    moduleName = "KeyboardInputHandlersModule"
+)
+annotation class BindKeyboardInputHandler
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindMouseInputHandler.kt
new file mode 100644 (file)
index 0000000..af9b608
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.misc.annotations.multibinding
+
+import ru.deadsoftware.cavedroid.game.input.IMouseInputHandler
+import ru.fredboy.automultibind.annotations.BindsIntoSet
+
+@BindsIntoSet(
+    interfaceClass = IMouseInputHandler::class,
+    modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE,
+    moduleName = "MouseInputHandlersModule"
+)
+annotation class BindMouseInputHandler
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindPlaceBlockAction.kt b/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindPlaceBlockAction.kt
new file mode 100644 (file)
index 0000000..89f2dd0
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.misc.annotations.multibinding
+
+import ru.deadsoftware.cavedroid.game.actions.placeblock.IPlaceBlockAction
+import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey
+
+@BindsIntoMapStringKey(
+    interfaceClass = IPlaceBlockAction::class,
+    modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE,
+    moduleName = "PlaceBlockActionsModule"
+)
+annotation class BindPlaceBlockAction(val stringKey: String)
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindRenderer.kt b/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindRenderer.kt
new file mode 100644 (file)
index 0000000..c1d96a9
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.misc.annotations.multibinding
+
+import ru.deadsoftware.cavedroid.game.render.IGameRenderer
+import ru.fredboy.automultibind.annotations.BindsIntoSet
+
+@BindsIntoSet(
+    interfaceClass = IGameRenderer::class,
+    modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE,
+    moduleName = "RenderModule"
+)
+annotation class BindRenderer
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUpdateBlockAction.kt b/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUpdateBlockAction.kt
new file mode 100644 (file)
index 0000000..310d69e
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.misc.annotations.multibinding
+
+import ru.deadsoftware.cavedroid.game.actions.updateblock.IUpdateBlockAction
+import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey
+
+@BindsIntoMapStringKey(
+    interfaceClass = IUpdateBlockAction::class,
+    modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE,
+    moduleName = "UpdateBlockActionsModule"
+)
+annotation class BindUpdateBlockAction(val stringKey: String)
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUseBlockAction.kt b/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUseBlockAction.kt
new file mode 100644 (file)
index 0000000..46353ca
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.misc.annotations.multibinding
+
+import ru.deadsoftware.cavedroid.game.actions.useblock.IUseBlockAction
+import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey
+
+@BindsIntoMapStringKey(
+    interfaceClass = IUseBlockAction::class,
+    modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE,
+    moduleName = "UseBlockActionsModule"
+)
+annotation class BindUseBlockAction(val stringKey: String)
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUseItemAction.kt b/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/BindUseItemAction.kt
new file mode 100644 (file)
index 0000000..7f17bfc
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.misc.annotations.multibinding
+
+import ru.deadsoftware.cavedroid.game.actions.useitem.IUseItemAction
+import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey
+
+@BindsIntoMapStringKey(
+    interfaceClass = IUseItemAction::class,
+    modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE,
+    moduleName = "UseItemActionsModule"
+)
+annotation class BindUseItemAction(val stringKey: String)
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/MultibindingConfig.kt b/core/src/ru/deadsoftware/cavedroid/misc/annotations/multibinding/MultibindingConfig.kt
new file mode 100644 (file)
index 0000000..12427bd
--- /dev/null
@@ -0,0 +1,5 @@
+package ru.deadsoftware.cavedroid.misc.annotations.multibinding
+
+data object MultibindingConfig {
+    const val GENERATED_MODULES_PACKAGE = "ru.deadsoftware.cavedroid.generated.module"
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/package-info.java b/core/src/ru/deadsoftware/cavedroid/misc/package-info.java
deleted file mode 100644 (file)
index acdf3e1..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@NonnullByDefault
-package ru.deadsoftware.cavedroid.misc;
-
-import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault;
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/GdxExtensions.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/GdxExtensions.kt
new file mode 100644 (file)
index 0000000..1c80048
--- /dev/null
@@ -0,0 +1,5 @@
+package ru.deadsoftware.cavedroid.misc.utils
+
+import com.badlogic.gdx.Graphics
+
+val Graphics.ratio get() = width.toFloat() / height.toFloat()
\ No newline at end of file
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) {
diff --git a/core/src/ru/deadsoftware/cavedroid/package-info.java b/core/src/ru/deadsoftware/cavedroid/package-info.java
deleted file mode 100644 (file)
index a566cb5..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@NonnullByDefault
-package ru.deadsoftware.cavedroid;
-
-import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault;
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/prefs/PreferencesStore.kt b/core/src/ru/deadsoftware/cavedroid/prefs/PreferencesStore.kt
new file mode 100644 (file)
index 0000000..edc9517
--- /dev/null
@@ -0,0 +1,9 @@
+package ru.deadsoftware.cavedroid.prefs
+
+interface PreferencesStore {
+
+    fun getPreference(key: String): String?
+
+    fun setPreference(key: String, value: String?)
+
+}
index 70a63ba56bc3d5f3207941b4c50ac64eab09ee3e..912734500bc8b2c2b414039e17cc9ef830176306 100644 (file)
@@ -1,16 +1,19 @@
 plugins {
+    id 'java-library'
     id 'kotlin'
     id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlinVersion"
 }
 
-sourceCompatibility = 17
-sourceSets.main.java.srcDirs = [ "src/" ]
+java.targetCompatibility = JavaVersion.VERSION_17
+java.sourceCompatibility = JavaVersion.VERSION_1_8
+
+sourceSets.main.java.srcDirs = ["src/"]
 sourceSets.main.resources.srcDirs = ["../android/assets"]
 
 project.ext.mainClassName = "ru.deadsoftware.cavedroid.desktop.DesktopLauncher"
 project.ext.assetsDir = new File("../android/assets")
 
-task run(dependsOn: classes, type: JavaExec) {
+task run(dependsOn: build, type: JavaExec) {
     main = project.mainClassName
     classpath = sourceSets.main.runtimeClasspath
     standardInput = System.in
@@ -19,7 +22,7 @@ task run(dependsOn: classes, type: JavaExec) {
     args "--debug"
 }
 
-task runTouch(dependsOn: classes, type: JavaExec) {
+task runTouch(dependsOn: build, type: JavaExec) {
     main = project.mainClassName
     classpath = sourceSets.main.runtimeClasspath
     standardInput = System.in
@@ -28,25 +31,20 @@ task runTouch(dependsOn: classes, type: JavaExec) {
     args "--touch", "--debug"
 }
 
-task debug(dependsOn: classes, type: JavaExec) {
-    main = project.mainClassName
-    classpath = sourceSets.main.runtimeClasspath
-    standardInput = System.in
-    workingDir = project.assetsDir
-    ignoreExitValue = true as JavaExecSpec
-    debug = true
-}
-
-task dist(type: Jar) {
+task dist(dependsOn: build, type: Jar) {
     duplicatesStrategy = DuplicatesStrategy.EXCLUDE
     manifest {
         attributes 'Main-Class': project.mainClassName
     }
     from {
-        configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
+        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
     }
     with jar
 }
 
-
-dist.dependsOn classes
\ No newline at end of file
+dependencies {
+    implementation project(":core")
+    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializationVersion"
+    api "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
+    api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
+}
index fe78235dd8fe5bc0ec937561b82ae1a9b4f6e87e..713749703d6ebe52ea59120850f4598164da414e 100644 (file)
@@ -8,7 +8,8 @@ import ru.deadsoftware.cavedroid.CaveGame;
 class DesktopLauncher {
     public static void main(String[] arg) {
         Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
-        config.setWindowIcon(Files.FileType.Internal, "icons/icon256.png", "icons/icon128.png");
+        config.setWindowIcon(Files.FileType.Internal,
+                "icons/icon512.png", "icons/icon256.png", "icons/icon128.png");
         config.setTitle("CaveDroid");
         config.setWindowedMode(960, 540);
         config.useVsync(true);
@@ -34,8 +35,12 @@ class DesktopLauncher {
             }
         }
 
-        CaveGame caveGame = new CaveGame(System.getProperty("user.home") + "/.cavedroid", touch, assetsPath);
-        caveGame.setDebug(debug);
+        CaveGame caveGame = new CaveGame(
+                System.getProperty("user.home") + "/.cavedroid",
+                touch,
+                debug,
+                new DesktopPreferencesStore()
+        );
 
         new Lwjgl3Application(caveGame, config);
     }
diff --git a/desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopPreferencesStore.kt b/desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopPreferencesStore.kt
new file mode 100644 (file)
index 0000000..03591bb
--- /dev/null
@@ -0,0 +1,18 @@
+package ru.deadsoftware.cavedroid.desktop
+
+import ru.deadsoftware.cavedroid.prefs.PreferencesStore
+import java.util.prefs.Preferences
+
+class DesktopPreferencesStore : PreferencesStore {
+
+    private val prefs = Preferences.userNodeForPackage(DesktopPreferencesStore::class.java)
+
+    override fun getPreference(key: String): String? {
+        return prefs.get(key, null)
+    }
+
+    override fun setPreference(key: String, value: String?) {
+        prefs.put(key, value)
+    }
+
+}
\ No newline at end of file
index b7f66d430f6f4d25e3db7900a59a42c0962076ff..dc8c2b772fe0839531f9970e2e48985ab814e7f1 100644 (file)
@@ -2,3 +2,4 @@ org.gradle.daemon=true
 org.gradle.jvmargs=-Xms128m -Xmx1500m
 org.gradle.configureondemand=true
 android.useAndroidX=true
+ksp.incremental=false
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
index 77ae4636a6ddc7c1a7bce9cd98481cbb4b4e849d..99367dfdf5483f85e79b409e35994d0e70fa1892 100644 (file)
@@ -1 +1,2 @@
-include 'desktop', 'android', 'core'
\ No newline at end of file
+include 'desktop', 'android', 'core'
+