summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (from: 16b1098)
raw | patch | inline | side by side (from: 16b1098)
author | fredboy <fredboy@protonmail.com> | |
Wed, 24 Apr 2024 15:49:20 +0000 (22:49 +0700) | ||
committer | fredboy <fredboy@protonmail.com> | |
Wed, 24 Apr 2024 15:49:20 +0000 (22:49 +0700) |
190 files changed:
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
--- /dev/null
@@ -0,0 +1,28 @@
+name: Android CI
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ cache: gradle
+
+ - name: mock keystore.properties
+ run: echo "releaseKeystorePath=mock" > keystore.properties
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Build with Gradle
+ run: ./gradlew android:buildDebug
diff --git a/.gitignore b/.gitignore
index ac624d207dbf4971a19c34d50b5b78ea187c9358..e03f5587f9ef9b42b0be788ce28a0aa394291c60 100644 (file)
--- a/.gitignore
+++ b/.gitignore
!/ios-moe/xcode/*.xcodeproj/xcshareddata
!/ios-moe/xcode/*.xcodeproj/project.pbxproj
/ios-moe/xcode/native/
+
+release-*/
+keystore.properties
index 37400b601b719a895b831164d55737df9ddd37b2..6d0eb3acdafa494b776a3a16163fa4b22458a294 100644 (file)
--- a/COPYING
+++ b/COPYING
Textures used in this game:
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/
+
+Some scripts from stack overflow are distributed under applicable licenses
diff --git a/README.md b/README.md
index b0f75259d51a0543afa1e874894aca6733b2c176..684c5a354316afe22a10f1134a69e5e7848ab072 100644 (file)
--- a/README.md
+++ b/README.md
# CaveDroid
-[![Build Status](https://travis-ci.org/fredboy/cavedroid.svg?branch=master)](https://travis-ci.org/fredboy/cavedroid)
-[![Releases](https://img.shields.io/github/release/fredboy/cavedroid.svg)](https://github.com/fredboy/cavedroid/releases/latest) <br>
+[![Android CI](https://github.com/fredboy/cavedroid/actions/workflows/android.yml/badge.svg)](https://github.com/fredboy/cavedroid/actions/workflows/android.yml)
+[![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>
## Binary releases
index 47eaf7a1afc8266caabc5563a9b7a645e66a9216..9328a4edd5b08ac8f70675819a992dd7e619f620 100644 (file)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools" package="ru.deadsoftware.cavedroid">
+ xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:theme="@style/GdxTheme" tools:ignore="GoogleAppIndexingWarning"
android:fullBackupContent="@xml/backup_descriptor">
<activity
+ android:exported="true"
android:name="ru.deadsoftware.cavedroid.AndroidLauncher"
android:label="@string/app_name"
android:screenOrientation="sensorLandscape"
diff --git a/android/assets/crafting_table.png b/android/assets/crafting_table.png
new file mode 100644 (file)
index 0000000..e019e39
Binary files /dev/null and b/android/assets/crafting_table.png differ
index 0000000..e019e39
Binary files /dev/null and b/android/assets/crafting_table.png differ
diff --git a/android/assets/health.png b/android/assets/health.png
new file mode 100644 (file)
index 0000000..93314e6
Binary files /dev/null and b/android/assets/health.png differ
index 0000000..93314e6
Binary files /dev/null and b/android/assets/health.png differ
diff --git a/android/assets/inventory.png b/android/assets/inventory.png
new file mode 100644 (file)
index 0000000..8bc6cf7
Binary files /dev/null and b/android/assets/inventory.png differ
index 0000000..8bc6cf7
Binary files /dev/null and b/android/assets/inventory.png differ
diff --git a/android/assets/json/crafting.json b/android/assets/json/crafting.json
--- /dev/null
@@ -0,0 +1,42 @@
+{
+ "planks_oak": {
+ "input": ["log_oak", "none", "none", "none", "none", "none", "none", "none", "none"],
+ "count": 4
+ },
+ "stick": {
+ "input": ["planks_oak", "none", "none", "planks_oak", "none", "none", "none", "none", "none"],
+ "count": 4
+ },
+ "wood_pickaxe": {
+ "input": ["planks_oak", "planks_oak", "planks_oak", "none", "stick", "none", "none", "stick", "none"],
+ "count": 59
+ },
+ "wood_axe": {
+ "input": ["planks_oak", "planks_oak", "none", "planks_oak", "stick", "none", "none", "stick", "none"],
+ "count": 60
+ },
+ "wood_sword": {
+ "input": ["none", "planks_oak", "none", "none", "planks_oak", "none", "none", "stick", "none"],
+ "count": 60
+ },
+ "wood_shovel": {
+ "input": ["none", "planks_oak", "none", "none", "stick", "none", "none", "stick", "none"],
+ "count": 59
+ },
+ "stone_pickaxe": {
+ "input": ["cobblestone", "cobblestone", "cobblestone", "none", "stick", "none", "none", "stick", "none"],
+ "count": 131
+ },
+ "stone_axe": {
+ "input": ["cobblestone", "cobblestone", "none", "cobblestone", "stick", "none", "none", "stick", "none"],
+ "count": 131
+ },
+ "stone_sword": {
+ "input": ["none", "cobblestone", "none", "none", "cobblestone", "none", "none", "stick", "none"],
+ "count": 132
+ },
+ "stone_shovel": {
+ "input": ["none", "cobblestone", "none", "none", "stick", "none", "none", "stick", "none"],
+ "count": 131
+ }
+}
\ No newline at end of file
index 042f0c9bbea1a77cde66481caaf6ec8fde516cc2..d6c3b441a3fc0cff5672e08a10985e8705bfbcd0 100644 (file)
"blocks": {
"none": {
"collision": false,
- "transparent": true
+ "transparent": true,
+ "drop": "none",
+ "texture": "none",
+ "meta": "none"
},
"stone": {
"hp": 450,
- "drop": "cobblestone"
+ "drop": "cobblestone",
+ "texture": "stone",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
"grass": {
"hp": 54,
- "drop": "dirt"
+ "drop": "dirt",
+ "texture": "grass",
+ "tool_level": 0,
+ "tool_type": "shovel"
+ },
+ "grass_snowed": {
+ "hp": 54,
+ "drop": "dirt",
+ "texture": "grass_snowed",
+ "tool_level": 0,
+ "tool_type": "shovel"
},
"dirt": {
- "hp": 45
+ "hp": 45,
+ "drop": "dirt",
+ "texture": "dirt",
+ "tool_level": 0,
+ "tool_type": "shovel"
},
"cobblestone": {
- "hp": 600
+ "hp": 600,
+ "drop": "cobblestone",
+ "texture": "cobblestone",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
"planks_oak": {
- "hp": 180
+ "hp": 180,
+ "drop": "planks_oak",
+ "texture": "planks_oak",
+ "tool_level": 0,
+ "tool_type": "axe"
},
"sapling_oak": {
- "hp": 0,
"collision": false,
- "background": false,
"transparent": true,
- "block_required": true
+ "block_required": true,
+ "drop": "sapling_oak",
+ "texture": "sapling_oak",
+ "hp": 0
},
"bedrock": {
- "hp": -1
+ "drop": "bedrock",
+ "texture": "bedrock"
},
"water": {
- "hp": -1,
"collision": false,
- "background": false,
"transparent": true,
- "fluid": true,
- "texture": "water_still",
+ "drop": "water",
"meta": "water",
+ "texture": "water_still",
"animated": true,
- "frames": 16
+ "frames": 16,
+ "state": 0
},
"lava": {
- "hp": -1,
"collision": false,
- "background": false,
- "fluid": true,
- "texture": "lava_still",
+ "drop": "lava",
"meta": "lava",
+ "texture": "lava_still",
"animated": true,
- "frames": 16
+ "frames": 16,
+ "state": 0,
+ "damage": 4
},
"sand": {
- "hp": 45
+ "hp": 45,
+ "drop": "sand",
+ "texture": "sand",
+ "tool_level": 0,
+ "tool_type": "shovel"
},
"gravel": {
- "hp": 54
+ "hp": 54,
+ "drop": "gravel",
+ "texture": "gravel",
+ "tool_level": 0,
+ "tool_type": "shovel"
},
"gold_ore": {
- "hp": 900
+ "hp": 900,
+ "drop": "gold_ore",
+ "texture": "gold_ore",
+ "tool_level": 3,
+ "tool_type": "pickaxe"
},
"iron_ore": {
- "hp": 900
+ "hp": 900,
+ "drop": "iron_ore",
+ "texture": "iron_ore",
+ "tool_level": 2,
+ "tool_type": "pickaxe"
},
"coal_ore": {
"hp": 900,
- "drop": "coal"
+ "drop": "coal",
+ "texture": "coal_ore",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
+ },
+ "diamond_ore": {
+ "hp": 900,
+ "drop": "diamond",
+ "texture": "diamond_ore",
+ "tool_level": 3,
+ "tool_type": "pickaxe"
+ },
+ "diamond_block": {
+ "hp": 1500,
+ "drop": "diamond_block",
+ "texture": "diamond_block",
+ "tool_level": 3,
+ "tool_type": "pickaxe"
},
"log_oak": {
- "hp": 180
+ "hp": 180,
+ "drop": "log_oak",
+ "texture": "log_oak",
+ "tool_level": 0,
+ "tool_type": "axe"
},
"leaves_oak": {
"hp": 21,
- "transparent": true
+ "drop": "leaves_oak",
+ "texture": "leaves_oak",
+ "tool_level": 1,
+ "tool_type": "shears",
+ "tint": "#5AC557"
},
"sponge": {
- "hp": 54
+ "hp": 54,
+ "drop": "sponge",
+ "texture": "sponge",
+ "tool_level": 0,
+ "tool_type": "hoe"
},
"glass": {
"hp": 27,
+ "transparent": true,
"drop": "none",
- "background": false,
- "transparent": true
+ "texture": "glass"
},
"lapis_ore": {
"hp": 900,
- "drop": "lapis"
+ "drop": "lapis",
+ "texture": "lapis_ore",
+ "tool_level": 2,
+ "tool_type": "pickaxe"
},
"lapis_block": {
- "hp": 900
+ "hp": 900,
+ "drop": "lapis_block",
+ "texture": "lapis_block",
+ "tool_level": 2,
+ "tool_type": "pickaxe"
},
"sandstone": {
- "hp": 240
+ "hp": 240,
+ "drop": "sandstone",
+ "texture": "sandstone",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
"noteblock": {
- "hp": 75
+ "hp": 75,
+ "drop": "noteblock",
+ "texture": "noteblock",
+ "tool_level": 0,
+ "tool_type": "axe"
},
"bed_l": {
"hp": 21,
- "drop": "none",
"collision": false,
"background": true,
- "transparent": true
+ "transparent": true,
+ "drop": "none",
+ "texture": "bed_l",
+ "tool_level": 0,
+ "tool_type": "axe"
},
"bed_r": {
"hp": 21,
- "drop": "none",
"collision": false,
"background": true,
- "transparent": true
+ "transparent": true,
+ "drop": "none",
+ "texture": "bed_r",
+ "tool_level": 0,
+ "tool_type": "axe"
},
"web": {
"hp": 1200,
"collision": false,
- "background": false,
- "transparent": true
+ "transparent": true,
+ "drop": "web",
+ "texture": "web",
+ "tool_level": 1,
+ "tool_type": "shears"
},
"tallgrass": {
- "hp": 0,
"collision": false,
- "background": false,
"transparent": true,
- "block_required": true
+ "block_required": true,
+ "drop": "tallgrass",
+ "texture": "tallgrass",
+ "tool_level": 1,
+ "tool_type": "shears",
+ "tint": "#5AC557",
+ "hp": 0
},
"deadbush": {
- "hp": 0,
"collision": false,
- "background": false,
"transparent": true,
- "block_required": true
+ "block_required": true,
+ "drop": "deadbush",
+ "texture": "deadbush",
+ "tool_level": 1,
+ "tool_type": "shears",
+ "hp": 0
},
"bricks": {
- "hp": 600
+ "hp": 600,
+ "drop": "bricks",
+ "texture": "bricks",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
"dandelion": {
- "hp": 0,
"collision": false,
- "background": false,
"transparent": true,
- "block_required": true
+ "block_required": true,
+ "drop": "dandelion",
+ "texture": "dandelion",
+ "hp": 0
},
"rose": {
- "hp": 0,
"collision": false,
- "background": false,
"transparent": true,
- "block_required": true
+ "block_required": true,
+ "drop": "rose",
+ "texture": "rose",
+ "hp": 0
},
"mushroom_brown": {
- "hp": 0,
"collision": false,
- "background": false,
"transparent": true,
- "block_required": true
+ "block_required": true,
+ "drop": "mushroom_brown",
+ "texture": "mushroom_brown",
+ "hp": 0
},
"mushroom_red": {
- "hp": 0,
"collision": false,
- "background": false,
"transparent": true,
- "block_required": true
+ "block_required": true,
+ "drop": "mushroom_red",
+ "texture": "mushroom_red",
+ "hp": 0
},
"wool_colored_white": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_white",
+ "texture": "wool_colored_white",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_orange": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_orange",
+ "texture": "wool_colored_orange",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_magenta": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_magenta",
+ "texture": "wool_colored_magenta",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_light_blue": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_light_blue",
+ "texture": "wool_colored_light_blue",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_yellow": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_yellow",
+ "texture": "wool_colored_yellow",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_lime": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_lime",
+ "texture": "wool_colored_lime",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_pink": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_pink",
+ "texture": "wool_colored_pink",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_gray": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_gray",
+ "texture": "wool_colored_gray",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_silver": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_silver",
+ "texture": "wool_colored_silver",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_cyan": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_cyan",
+ "texture": "wool_colored_cyan",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_purple": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_purple",
+ "texture": "wool_colored_purple",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_blue": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_blue",
+ "texture": "wool_colored_blue",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_brown": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_brown",
+ "texture": "wool_colored_brown",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_green": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_green",
+ "texture": "wool_colored_green",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_red": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_red",
+ "texture": "wool_colored_red",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"wool_colored_black": {
- "hp": 75
+ "hp": 75,
+ "drop": "wool_colored_black",
+ "texture": "wool_colored_black",
+ "tool_level": 0,
+ "tool_type": "shears"
},
"gold_block": {
- "hp": 900
+ "hp": 900,
+ "drop": "gold_block",
+ "texture": "gold_block",
+ "tool_level": 3,
+ "tool_type": "pickaxe"
},
"iron_block": {
- "hp": 1500
+ "hp": 1500,
+ "drop": "iron_block",
+ "texture": "iron_block",
+ "tool_level": 2,
+ "tool_type": "pickaxe"
},
- "stone_slab": {
+ "stone_slab_bottom": {
"top": 8,
+ "sprite_top": 8,
"hp": 600,
"transparent": true,
- "sprite_top": 8
+ "drop": "stone_slab",
+ "meta": "slab",
+ "texture": "stone_slab",
+ "full_block": "double_stone_slab",
+ "other_part": "stone_slab_top",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
+ },
+ "stone_slab_top": {
+ "hp": 600,
+ "bottom": 8,
+ "sprite_bottom": 8,
+ "transparent": true,
+ "drop": "stone_slab",
+ "meta": "slab",
+ "texture": "stone_slab",
+ "full_block": "double_stone_slab",
+ "other_part": "stone_slab_bottom",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
"double_stone_slab": {
"hp": 600,
"drop": "stone_slab",
- "texture": "stone_slab"
+ "drop_count": 2,
+ "texture": "stone_slab",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
- "sandstone_slab": {
+ "sandstone_slab_bottom": {
"top": 8,
+ "sprite_top": 8,
"hp": 600,
"transparent": true,
- "texture": "sandstone"
- },
- "oak_slab": {
+ "drop": "sandstone_slab",
+ "meta": "slab",
+ "texture": "sandstone",
+ "full_block": "sandstone",
+ "other_part": "sandstone_slab_top",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
+ },
+ "sandstone_slab_top": {
+ "bottom": 8,
+ "sprite_bottom": 8,
+ "hp": 600,
+ "transparent": true,
+ "drop": "sandstone_slab",
+ "meta": "slab",
+ "texture": "sandstone",
+ "full_block": "sandstone",
+ "other_part": "sandstone_slab_bottom",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
+ },
+ "oak_slab_bottom": {
"top": 8,
+ "sprite_top": 8,
"hp": 180,
"transparent": true,
- "texture": "planks_oak"
- },
- "cobblestone_slab": {
+ "drop": "oak_slab",
+ "meta": "slab",
+ "texture": "planks_oak",
+ "full_block": "planks_oak",
+ "other_part": "oak_slab_top",
+ "tool_level": 0,
+ "tool_type": "axe"
+ },
+ "oak_slab_top": {
+ "bottom": 8,
+ "sprite_bottom": 8,
+ "hp": 180,
+ "transparent": true,
+ "drop": "oak_slab",
+ "meta": "slab",
+ "texture": "planks_oak",
+ "full_block": "planks_oak",
+ "other_part": "oak_slab_bottom",
+ "tool_level": 0,
+ "tool_type": "axe"
+ },
+ "cobblestone_slab_bottom": {
"top": 8,
+ "sprite_top": 8,
"hp": 600,
"transparent": true,
- "texture": "cobblestone"
- },
- "brick_slab": {
+ "meta": "slab",
+ "drop": "cobblestone_slab",
+ "texture": "cobblestone",
+ "full_block": "cobblestone",
+ "other_part": "cobblestone_slab_top",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
+ },
+ "cobblestone_slab_top": {
+ "bottom": 8,
+ "sprite_bottom": 8,
+ "hp": 600,
+ "transparent": true,
+ "meta": "slab",
+ "drop": "cobblestone_slab",
+ "texture": "cobblestone",
+ "full_block": "cobblestone",
+ "other_part": "cobblestone_slab_bottom",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
+ },
+ "brick_slab_bottom": {
"top": 8,
+ "sprite_top": 8,
"hp": 600,
"transparent": true,
- "texture": "bricks"
+ "drop": "brick_slab",
+ "meta": "slab",
+ "texture": "bricks",
+ "full_block": "bricks",
+ "other_part": "brick_slab_top",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
+ },
+ "brick_slab_top": {
+ "bottom": 8,
+ "sprite_bottom": 8,
+ "hp": 600,
+ "transparent": true,
+ "drop": "brick_slab",
+ "meta": "slab",
+ "texture": "bricks",
+ "full_block": "bricks",
+ "other_part": "brick_slab_bottom",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
"stonebrick": {
- "hp": 450
+ "hp": 450,
+ "drop": "stonebrick",
+ "texture": "stonebrick",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
- "stonebrick_slab": {
+ "stonebrick_slab_bottom": {
"top": 8,
+ "sprite_top": 8,
"hp": 450,
"transparent": true,
- "texture": "stonebrick"
+ "drop": "stonebrick_slab",
+ "meta": "slab",
+ "texture": "stonebrick",
+ "full_block": "stonebrick",
+ "other_part": "stonebrick_slab_top",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
+ },
+ "stonebrick_slab_top": {
+ "bottom": 8,
+ "sprite_bottom": 8,
+ "hp": 450,
+ "transparent": true,
+ "drop": "stonebrick_slab",
+ "meta": "slab",
+ "texture": "stonebrick",
+ "full_block": "stonebrick",
+ "other_part": "brick_slab_bottom",
+ "tool_level": 1,
+ "tool_type": "pickaxe"
},
"cactus": {
"left": 1,
"right": 1,
"hp": 39,
"transparent": true,
- "block_required": true
+ "block_required": true,
+ "drop": "cactus",
+ "texture": "cactus",
+ "damage": 1
},
"water_16": {
- "hp": -1,
"collision": false,
- "background": false,
"transparent": true,
- "fluid": true,
- "texture": "water_flow",
+ "drop": "water_16",
"meta": "water",
+ "texture": "water_flow",
"animated": true,
- "frames": 16
+ "frames": 16,
+ "state": 1
},
"water_12": {
"top": 4,
- "hp": -1,
+ "sprite_top": 4,
"collision": false,
- "background": false,
"transparent": true,
- "fluid": true,
- "texture": "water_flow",
+ "drop": "water_12",
"meta": "water",
+ "texture": "water_flow",
"animated": true,
"frames": 16,
- "sprite_top": 4
+ "state": 2
},
"water_8": {
"top": 8,
- "hp": -1,
+ "sprite_top": 8,
"collision": false,
- "background": false,
"transparent": true,
- "fluid": true,
- "texture": "water_flow",
+ "drop": "water_8",
"meta": "water",
+ "texture": "water_flow",
"animated": true,
"frames": 16,
- "sprite_top": 8
+ "state": 3
},
"water_4": {
"top": 12,
- "hp": -1,
+ "sprite_top": 12,
"collision": false,
- "background": false,
"transparent": true,
- "fluid": true,
- "texture": "water_flow",
+ "drop": "water_4",
"meta": "water",
+ "texture": "water_flow",
"animated": true,
"frames": 16,
- "sprite_top": 12
+ "state": 4
},
"lava_16": {
- "hp": -1,
"collision": false,
- "background": false,
- "fluid": true,
- "texture": "lava_flow",
+ "drop": "lava_16",
"meta": "lava",
+ "texture": "lava_flow",
"animated": true,
- "frames": 16
+ "frames": 16,
+ "state": 1,
+ "damage": 4
},
"lava_12": {
"top": 4,
- "hp": -1,
+ "sprite_top": 4,
"collision": false,
- "background": false,
- "fluid": true,
- "texture": "lava_flow",
+ "transparent": true,
+ "drop": "lava_12",
"meta": "lava",
+ "texture": "lava_flow",
"animated": true,
"frames": 16,
- "sprite_top": 4
+ "state": 2,
+ "damage": 4
},
"lava_8": {
"top": 8,
- "hp": -1,
+ "sprite_top": 8,
"collision": false,
- "background": false,
- "fluid": true,
- "texture": "lava_flow",
+ "transparent": true,
+ "drop": "lava_8",
"meta": "lava",
+ "texture": "lava_flow",
"animated": true,
"frames": 16,
- "sprite_top": 8
+ "state": 3,
+ "damage": 4
},
"lava_4": {
"top": 12,
- "hp": -1,
+ "sprite_top": 12,
"collision": false,
- "background": false,
- "fluid": true,
- "texture": "lava_flow",
+ "transparent": true,
+ "drop": "lava_4",
"meta": "lava",
+ "texture": "lava_flow",
"animated": true,
"frames": 16,
- "sprite_top": 12
+ "state": 4,
+ "damage": 4
},
"obsidian": {
- "hp": 1500
+ "hp": 15000,
+ "drop": "obsidian",
+ "texture": "obsidian",
+ "tool_level": 4,
+ "tool_type": "pickaxe"
}
},
"items": {
"none": {
"name": "",
- "type": "block"
+ "type": "none",
+ "texture": "none"
},
"stone": {
"name": "Stone",
- "type": "block"
+ "type": "block",
+ "texture": "stone"
},
"grass": {
"name": "Grass",
- "type": "block"
+ "type": "block",
+ "texture": "grass"
},
"dirt": {
"name": "Dirt",
- "type": "block"
+ "type": "block",
+ "texture": "dirt"
},
"cobblestone": {
"name": "Cobblestone",
- "type": "block"
+ "type": "block",
+ "texture": "cobblestone"
},
"planks_oak": {
"name": "Oak Planks",
- "type": "block"
+ "type": "block",
+ "texture": "planks_oak"
},
"sapling_oak": {
"name": "Oak Sapling",
- "type": "block"
+ "type": "block",
+ "texture": "sapling_oak"
},
"bedrock": {
"name": "Bedrock",
- "type": "block"
+ "type": "block",
+ "texture": "bedrock"
},
"water": {
"name": "Water",
- "type": "block"
+ "type": "block",
+ "texture": "water"
},
"lava": {
"name": "Lava",
- "type": "block"
+ "type": "block",
+ "texture": "lava"
},
"sand": {
"name": "Sand",
- "type": "block"
+ "type": "block",
+ "texture": "sand"
},
"gravel": {
"name": "Gravel",
- "type": "block"
+ "type": "block",
+ "texture": "gravel"
},
"gold_ore": {
"name": "Golden Ore",
- "type": "block"
+ "type": "block",
+ "texture": "gold_ore"
},
"iron_ore": {
"name": "Iron Ore",
- "type": "block"
+ "type": "block",
+ "texture": "iron_ore"
},
"coal_ore": {
"name": "Coal Ore",
- "type": "block"
+ "type": "block",
+ "texture": "coal_ore"
+ },
+ "diamond_ore": {
+ "name": "Diamond Ore",
+ "type": "block",
+ "texture": "diamond_ore"
},
"log_oak": {
"name": "Wood",
- "type": "block"
+ "type": "block",
+ "texture": "log_oak"
},
"leaves_oak": {
"name": "Leaves",
- "type": "block"
+ "type": "block",
+ "texture": "leaves_oak"
},
"glass": {
"name": "Glass",
- "type": "block"
+ "type": "block",
+ "texture": "glass"
},
"lapis_ore": {
"name": "Lapis Ore",
- "type": "block"
+ "type": "block",
+ "texture": "lapis_ore"
},
"lapis_block": {
"name": "Lapis Block",
- "type": "block"
+ "type": "block",
+ "texture": "lapis_block"
},
"sandstone": {
"name": "Sandstone",
- "type": "block"
+ "type": "block",
+ "texture": "sandstone"
},
"web": {
"name": "Cobweb",
- "type": "block"
+ "type": "block",
+ "texture": "web"
},
"tallgrass": {
"name": "Tall Grass",
- "type": "block"
+ "type": "block",
+ "texture": "tallgrass",
+ "origin_x": 0.5
},
"deadbush": {
"name": "Dead Bush",
- "type": "block"
+ "type": "block",
+ "texture": "deadbush",
+ "origin_x": 0.5
},
"bricks": {
"name": "Bricks",
- "type": "block"
+ "type": "block",
+ "texture": "bricks"
},
"dandelion": {
"name": "Dandelion",
- "type": "block"
+ "type": "block",
+ "texture": "dandelion",
+ "origin_x": 0.5
},
"rose": {
"name": "Rose",
- "type": "block"
+ "type": "block",
+ "texture": "rose",
+ "origin_x": 0.5
},
"mushroom_brown": {
"name": "Mushroom",
- "type": "block"
+ "type": "block",
+ "texture": "mushroom_brown",
+ "origin_x": 0.5
},
"mushroom_red": {
"name": "Mushroom",
- "type": "block"
+ "type": "block",
+ "texture": "mushroom_red",
+ "origin_x": 0.5
},
"wool_colored_white": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_white"
},
"wool_colored_orange": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_orange"
},
"wool_colored_magenta": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_magenta"
},
"wool_colored_light_blue": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_light_blue"
},
"wool_colored_yellow": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_yellow"
},
"wool_colored_lime": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_lime"
},
"wool_colored_pink": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_pink"
},
"wool_colored_gray": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_gray"
},
"wool_colored_silver": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_silver"
},
"wool_colored_cyan": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_cyan"
},
"wool_colored_purple": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_purple"
},
"wool_colored_blue": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_blue"
},
"wool_colored_brown": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_brown"
},
"wool_colored_green": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_green"
},
"wool_colored_red": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_red"
},
"wool_colored_black": {
"name": "Wool",
- "type": "block"
+ "type": "block",
+ "texture": "wool_colored_black"
},
"gold_block": {
"name": "Gold Block",
- "type": "block"
+ "type": "block",
+ "texture": "gold_block"
},
"iron_block": {
"name": "Iron Block",
- "type": "block"
+ "type": "block",
+ "texture": "iron_block"
+ },
+ "diamond_block": {
+ "name": "Diamond Block",
+ "type": "block",
+ "texture": "diamond_block"
},
"stone_slab": {
"name": "Stone Slab",
- "type": "block",
- "meta": "slab"
+ "type": "slab",
+ "texture": "stone_slab",
+ "top_slab_block": "stone_slab_top",
+ "bottom_slab_block": "stone_slab_bottom"
},
"sandstone_slab": {
"name": "Sandstone Slab",
- "type": "block",
- "meta": "slab"
+ "type": "slab",
+ "texture": "sandstone_slab",
+ "top_slab_block": "sandstone_slab_top",
+ "bottom_slab_block": "sandstone_slab_bottom"
},
"oak_slab": {
"name": "Oak Slab",
- "type": "block",
- "meta": "slab"
+ "type": "slab",
+ "texture": "oak_slab",
+ "top_slab_block": "oak_slab_top",
+ "bottom_slab_block": "oak_slab_bottom"
},
"cobblestone_slab": {
"name": "Cobblestone Slab",
- "type": "block",
- "meta": "slab"
+ "type": "slab",
+ "texture": "cobblestone_slab",
+ "top_slab_block": "cobblestone_slab_top",
+ "bottom_slab_block": "cobblestone_slab_bottom"
},
"brick_slab": {
"name": "Brick Slab",
- "type": "block",
- "meta": "slab"
+ "type": "slab",
+ "texture": "brick_slab",
+ "top_slab_block": "brick_slab_top",
+ "bottom_slab_block": "brick_slab_bottom"
},
"stonebrick": {
"name": "Stone Brick",
- "type": "block"
+ "type": "block",
+ "texture": "stonebrick"
},
"stonebrick_slab": {
"name": "Stone Brick Slab",
- "type": "block",
- "meta": "slab"
+ "type": "slab",
+ "texture": "stonebrick_slab",
+ "top_slab_block": "stonebrick_slab_top",
+ "bottom_slab_block": "stonebrick_slab_bottom"
},
"cactus": {
"name": "Cactus",
- "type": "block"
+ "type": "block",
+ "texture": "cactus"
},
"obsidian": {
"name": "Obsidian",
- "type": "block"
+ "type": "block",
+ "texture": "obsidian"
+ },
+ "stick": {
+ "name": "Stick",
+ "texture": "stick"
},
"wood_sword": {
"name": "Wooden Sword",
- "type": "tool"
+ "type": "sword",
+ "texture": "wood_sword",
+ "origin_x": 0.125,
+ "tool_level": 1,
+ "max_stack": 60
},
"stone_sword": {
"name": "Stone Sword",
- "type": "tool"
+ "type": "sword",
+ "texture": "stone_sword",
+ "origin_x": 0.125,
+ "tool_level": 2,
+ "max_stack": 132
},
"iron_sword": {
"name": "Iron Sword",
- "type": "tool"
+ "type": "sword",
+ "texture": "iron_sword",
+ "origin_x": 0.125,
+ "tool_level": 3,
+ "max_stack": 251
},
"diamond_sword": {
"name": "Diamond Sword",
- "type": "tool"
+ "type": "sword",
+ "texture": "diamond_sword",
+ "origin_x": 0.125,
+ "tool_level": 4,
+ "max_stack": 1562
},
"gold_sword": {
"name": "Golden Sword",
- "type": "tool"
+ "type": "sword",
+ "texture": "gold_sword",
+ "origin_x": 0.125,
+ "tool_level": 1,
+ "max_stack": 33
},
"wood_shovel": {
"name": "Wooden Shovel",
- "type": "tool"
+ "type": "shovel",
+ "texture": "wood_shovel",
+ "origin_x": 0.125,
+ "tool_level" : 1,
+ "max_stack": 59
},
"stone_shovel": {
"name": "Stone Shovel",
- "type": "tool"
+ "type": "shovel",
+ "texture": "stone_shovel",
+ "origin_x": 0.125,
+ "tool_level" : 2,
+ "max_stack": 131
},
"iron_shovel": {
"name": "Iron Shovel",
- "type": "tool"
+ "type": "shovel",
+ "texture": "iron_shovel",
+ "origin_x": 0.125,
+ "tool_level" : 3,
+ "max_stack": 250
},
"diamond_shovel": {
"name": "Diamond Shovel",
- "type": "tool"
+ "type": "shovel",
+ "texture": "diamond_shovel",
+ "origin_x": 0.125,
+ "tool_level" : 4,
+ "max_stack": 1561
},
"gold_shovel": {
"name": "Golden Shovel",
- "type": "tool"
+ "type": "shovel",
+ "texture": "gold_shovel",
+ "origin_x": 0.125,
+ "tool_level" : 1,
+ "block_damage_multiplier": 6,
+ "max_stack": 32
+ },
+ "wood_pickaxe": {
+ "name": "Wooden Pickaxe",
+ "type": "pickaxe",
+ "texture": "wood_pickaxe",
+ "origin_x": 0.125,
+ "tool_level" : 1,
+ "max_stack": 59
+ },
+ "stone_pickaxe": {
+ "name": "Stone Pickaxe",
+ "type": "pickaxe",
+ "texture": "stone_pickaxe",
+ "origin_x": 0.125,
+ "tool_level" : 2,
+ "max_stack": 131
+ },
+ "iron_pickaxe": {
+ "name": "Iron Pickaxe",
+ "type": "pickaxe",
+ "texture": "iron_pickaxe",
+ "origin_x": 0.125,
+ "tool_level" : 3,
+ "max_stack": 251
+ },
+ "diamond_pickaxe": {
+ "name": "Diamond Pickaxe",
+ "type": "pickaxe",
+ "texture": "diamond_pickaxe",
+ "origin_x": 0.125,
+ "tool_level" : 4,
+ "max_stack": 1562
+ },
+ "gold_pickaxe": {
+ "name": "Golden Pickaxe",
+ "type": "pickaxe",
+ "texture": "gold_pickaxe",
+ "origin_x": 0.125,
+ "tool_level" : 1,
+ "block_damage_multiplier": 6,
+ "max_stack": 33
+ },
+ "wood_axe": {
+ "name": "Wooden Axe",
+ "type": "axe",
+ "texture": "wood_axe",
+ "origin_x": 0.125,
+ "tool_level" : 1,
+ "max_stack": 60
+ },
+ "stone_axe": {
+ "name": "Stone Axe",
+ "type": "axe",
+ "texture": "stone_axe",
+ "origin_x": 0.125,
+ "tool_level" : 2,
+ "max_stack": 131
+ },
+ "iron_axe": {
+ "name": "Iron Axe",
+ "type": "axe",
+ "texture": "iron_axe",
+ "origin_x": 0.125,
+ "tool_level" : 3,
+ "max_stack": 250
+ },
+ "diamond_axe": {
+ "name": "Diamond Axe",
+ "type": "axe",
+ "texture": "diamond_axe",
+ "origin_x": 0.125,
+ "tool_level" : 4,
+ "max_stack": 1561
+ },
+ "gold_axe": {
+ "name": "Golden Axe",
+ "type": "axe",
+ "texture": "gold_axe",
+ "origin_x": 0.125,
+ "tool_level" : 1,
+ "block_damage_multiplier": 6,
+ "max_stack": 32
+ },
+ "shears": {
+ "name": "Shears",
+ "type": "shears",
+ "texture": "shears",
+ "tool_level" : 1,
+ "origin_x": 0.125,
+ "max_stack": 238
},
"bucket_empty": {
"name": "Empty Bucket",
- "type": "tool"
+ "type": "bucket",
+ "texture": "bucket_empty",
+ "origin_x": 0.25,
+ "action_key": "use_empty_bucket",
+ "max_stack": 1
},
"bucket_water": {
"name": "Water Bucket",
- "type": "tool"
+ "type": "bucket",
+ "texture": "bucket_water",
+ "origin_x": 0.25,
+ "action_key": "use_water_bucket",
+ "max_stack": 1
},
"bucket_lava": {
"name": "Lava Bucket",
- "type": "tool"
+ "type": "bucket",
+ "texture": "bucket_lava",
+ "origin_x": 0.25,
+ "action_key": "use_lava_bucket",
+ "max_stack": 1
}
}
-}
\ No newline at end of file
+}
diff --git a/android/assets/json/menu_new_game_buttons.json b/android/assets/json/menu_new_game_buttons.json
index c61adc3d36f8310355683f53d179402b6d56ddd0..ca4c4042bfa178fe69579c238ebdc8bf7f7c41f2 100644 (file)
"label": "Creative"
},
"survival": {
- "label": "Survival",
- "type": 0
+ "label": "Survival"
},
"back": {
"label": "Back"
index e1f3e48adc14ebf25d3578a590cfc545826017c8..800098081fedec8efe5483a3ba2af7803c742a71 100644 (file)
"h": 26
}
},
- "break": {
- "break_0": {
- "w": 16,
- "h": 16
- },
- "break_1": {
- "x": 16,
- "w": 16,
- "h": 16
- },
- "break_2": {
- "x": 32,
- "w": 16,
- "h": 16
- },
- "break_3": {
- "x": 48,
- "w": 16,
- "h": 16
- },
- "break_4": {
- "x": 64,
- "w": 16,
- "h": 16
- },
- "break_5": {
- "x": 80,
- "w": 16,
- "h": 16
- },
- "break_6": {
- "x": 96,
- "w": 16,
- "h": 16
- },
- "break_7": {
- "x": 112,
- "w": 16,
- "h": 16
- },
- "break_8": {
- "x": 128,
- "w": 16,
- "h": 16
- },
- "break_9": {
- "x": 144,
- "w": 16,
- "h": 16
- }
- },
"allitems": {
"creative": {
"w": 176,
"h": 15
}
},
+ "inventory": {
+ "survival": {
+ "w": 176,
+ "h": 166
+ }
+ },
+ "crafting_table": {
+ "crafting_table": {
+ "w": 176,
+ "h": 166
+ }
+ },
"buttons": {
"button_0": {
"w": 200,
},
"shade": {},
"gamelogo": {},
- "background": {}
+ "background": {},
+ "health":{
+ "heart_whole": {
+ "w": 9
+ },
+ "heart_half": {
+ "x": 9,
+ "w": 9
+ }
+ }
}
\ No newline at end of file
diff --git a/android/assets/textures/blocks/grass_snowed.png b/android/assets/textures/blocks/grass_snowed.png
new file mode 100644 (file)
index 0000000..730996d
Binary files /dev/null and b/android/assets/textures/blocks/grass_snowed.png differ
index 0000000..730996d
Binary files /dev/null and b/android/assets/textures/blocks/grass_snowed.png differ
diff --git a/android/assets/textures/blocks/leaves_birch.png b/android/assets/textures/blocks/leaves_birch.png
deleted file mode 100644 (file)
index 3fa5139..0000000
Binary files a/android/assets/textures/blocks/leaves_birch.png and /dev/null differ
index 3fa5139..0000000
Binary files a/android/assets/textures/blocks/leaves_birch.png and /dev/null differ
diff --git a/android/assets/textures/blocks/leaves_oak.png b/android/assets/textures/blocks/leaves_oak.png
index c2a54ee5e827094223e85d93d5bb957793f79a8e..d9027521b78eaa34aac1ff2d3e3c46f98617c7c2 100644 (file)
Binary files a/android/assets/textures/blocks/leaves_oak.png and b/android/assets/textures/blocks/leaves_oak.png differ
Binary files a/android/assets/textures/blocks/leaves_oak.png and b/android/assets/textures/blocks/leaves_oak.png differ
diff --git a/android/assets/textures/blocks/leaves_spruce.png b/android/assets/textures/blocks/leaves_spruce.png
deleted file mode 100644 (file)
index cdcd782..0000000
Binary files a/android/assets/textures/blocks/leaves_spruce.png and /dev/null differ
index cdcd782..0000000
Binary files a/android/assets/textures/blocks/leaves_spruce.png and /dev/null differ
diff --git a/android/assets/textures/items/diamond_axe.png b/android/assets/textures/items/diamond_axe.png
new file mode 100644 (file)
index 0000000..ac81a23
Binary files /dev/null and b/android/assets/textures/items/diamond_axe.png differ
index 0000000..ac81a23
Binary files /dev/null and b/android/assets/textures/items/diamond_axe.png differ
diff --git a/android/assets/textures/items/diamond_hoe.png b/android/assets/textures/items/diamond_hoe.png
new file mode 100644 (file)
index 0000000..f0ad2c6
Binary files /dev/null and b/android/assets/textures/items/diamond_hoe.png differ
index 0000000..f0ad2c6
Binary files /dev/null and b/android/assets/textures/items/diamond_hoe.png differ
diff --git a/android/assets/textures/items/diamond_pickaxe.png b/android/assets/textures/items/diamond_pickaxe.png
new file mode 100644 (file)
index 0000000..75823f6
Binary files /dev/null and b/android/assets/textures/items/diamond_pickaxe.png differ
index 0000000..75823f6
Binary files /dev/null and b/android/assets/textures/items/diamond_pickaxe.png differ
diff --git a/android/assets/textures/items/gold_axe.png b/android/assets/textures/items/gold_axe.png
new file mode 100644 (file)
index 0000000..d939170
Binary files /dev/null and b/android/assets/textures/items/gold_axe.png differ
index 0000000..d939170
Binary files /dev/null and b/android/assets/textures/items/gold_axe.png differ
diff --git a/android/assets/textures/items/gold_hoe.png b/android/assets/textures/items/gold_hoe.png
new file mode 100644 (file)
index 0000000..da32a5d
Binary files /dev/null and b/android/assets/textures/items/gold_hoe.png differ
index 0000000..da32a5d
Binary files /dev/null and b/android/assets/textures/items/gold_hoe.png differ
diff --git a/android/assets/textures/items/gold_pickaxe.png b/android/assets/textures/items/gold_pickaxe.png
new file mode 100644 (file)
index 0000000..5b4e6d6
Binary files /dev/null and b/android/assets/textures/items/gold_pickaxe.png differ
index 0000000..5b4e6d6
Binary files /dev/null and b/android/assets/textures/items/gold_pickaxe.png differ
diff --git a/android/assets/textures/items/iron_axe.png b/android/assets/textures/items/iron_axe.png
new file mode 100644 (file)
index 0000000..52113da
Binary files /dev/null and b/android/assets/textures/items/iron_axe.png differ
index 0000000..52113da
Binary files /dev/null and b/android/assets/textures/items/iron_axe.png differ
diff --git a/android/assets/textures/items/iron_hoe.png b/android/assets/textures/items/iron_hoe.png
new file mode 100644 (file)
index 0000000..d56485d
Binary files /dev/null and b/android/assets/textures/items/iron_hoe.png differ
index 0000000..d56485d
Binary files /dev/null and b/android/assets/textures/items/iron_hoe.png differ
diff --git a/android/assets/textures/items/iron_pickaxe.png b/android/assets/textures/items/iron_pickaxe.png
new file mode 100644 (file)
index 0000000..0746cc0
Binary files /dev/null and b/android/assets/textures/items/iron_pickaxe.png differ
index 0000000..0746cc0
Binary files /dev/null and b/android/assets/textures/items/iron_pickaxe.png differ
diff --git a/android/assets/textures/items/shears.png b/android/assets/textures/items/shears.png
new file mode 100644 (file)
index 0000000..bfa815c
Binary files /dev/null and b/android/assets/textures/items/shears.png differ
index 0000000..bfa815c
Binary files /dev/null and b/android/assets/textures/items/shears.png differ
diff --git a/android/assets/textures/items/stick.png b/android/assets/textures/items/stick.png
new file mode 100644 (file)
index 0000000..81c915e
Binary files /dev/null and b/android/assets/textures/items/stick.png differ
index 0000000..81c915e
Binary files /dev/null and b/android/assets/textures/items/stick.png differ
diff --git a/android/assets/textures/items/stone_axe.png b/android/assets/textures/items/stone_axe.png
new file mode 100644 (file)
index 0000000..5f42de6
Binary files /dev/null and b/android/assets/textures/items/stone_axe.png differ
index 0000000..5f42de6
Binary files /dev/null and b/android/assets/textures/items/stone_axe.png differ
diff --git a/android/assets/textures/items/stone_hoe.png b/android/assets/textures/items/stone_hoe.png
new file mode 100644 (file)
index 0000000..2118229
Binary files /dev/null and b/android/assets/textures/items/stone_hoe.png differ
index 0000000..2118229
Binary files /dev/null and b/android/assets/textures/items/stone_hoe.png differ
diff --git a/android/assets/textures/items/stone_pickaxe.png b/android/assets/textures/items/stone_pickaxe.png
new file mode 100644 (file)
index 0000000..13bdd75
Binary files /dev/null and b/android/assets/textures/items/stone_pickaxe.png differ
index 0000000..13bdd75
Binary files /dev/null and b/android/assets/textures/items/stone_pickaxe.png differ
diff --git a/android/assets/textures/items/wood_axe.png b/android/assets/textures/items/wood_axe.png
new file mode 100644 (file)
index 0000000..b0cbde6
Binary files /dev/null and b/android/assets/textures/items/wood_axe.png differ
index 0000000..b0cbde6
Binary files /dev/null and b/android/assets/textures/items/wood_axe.png differ
diff --git a/android/assets/textures/items/wood_hoe.png b/android/assets/textures/items/wood_hoe.png
new file mode 100644 (file)
index 0000000..3744c1b
Binary files /dev/null and b/android/assets/textures/items/wood_hoe.png differ
index 0000000..3744c1b
Binary files /dev/null and b/android/assets/textures/items/wood_hoe.png differ
diff --git a/android/assets/textures/items/wood_pickaxe.png b/android/assets/textures/items/wood_pickaxe.png
new file mode 100644 (file)
index 0000000..5932623
Binary files /dev/null and b/android/assets/textures/items/wood_pickaxe.png differ
index 0000000..5932623
Binary files /dev/null and b/android/assets/textures/items/wood_pickaxe.png differ
diff --git a/android/build.gradle b/android/build.gradle
index a5ed087df1b3458384d90a0ddf8b1bccd50ba73f..061b54d0649c631d3cba7795b9e5bedb648da2f8 100644 (file)
--- a/android/build.gradle
+++ b/android/build.gradle
+plugins {
+ id "kotlin-android"
+}
+
+def keystorePropertiesFile = rootProject.file("keystore.properties")
+def keystoreProperties = new Properties()
+keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
android {
- buildToolsVersion "28.0.3"
- compileSdkVersion 29
+ namespace "ru.deadsoftware.cavedroid"
+ compileSdkVersion 34
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
}
}
compileOptions {
- sourceCompatibility 1.8
- targetCompatibility 1.8
+ sourceCompatibility 17
+ targetCompatibility 17
}
packagingOptions {
exclude 'META-INF/robovm/ios/robovm.xml'
defaultConfig {
applicationId "ru.deadsoftware.cavedroid"
minSdkVersion 24
- targetSdkVersion 29
- versionCode 10
- versionName "alpha0.4"
+ targetSdkVersion 34
+ versionCode 20
+ versionName "alpha0.7.0"
}
applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "android-${versionName}.apk"
}
}
+
+ signingConfigs {
+ release_config {
+ storeFile file(keystoreProperties['releaseKeystorePath'])
+ storePassword keystoreProperties['releaseKeystorePassword']
+ keyAlias keystoreProperties['releaseKeyAlias']
+ keyPassword keystoreProperties['releaseKeyPassword']
+ }
+ }
+
buildTypes {
release {
minifyEnabled false
+ signingConfig signingConfigs.release_config
}
debug {
applicationIdSuffix ".debug"
}
}
+ buildFeatures {
+ buildConfig true
+ }
}
diff --git a/android/src/ru/deadsoftware/cavedroid/AndroidLauncher.java b/android/src/ru/deadsoftware/cavedroid/AndroidLauncher.java
index fcf083199e0e667bb839c4e45a2c7fdfc22330a0..e7aad50df8c3df8deab19206bf22d5fee456f3ef 100644 (file)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
- config.hideStatusBar = true;
config.useImmersiveMode = true;
String gameFolder = "";
try {
e.printStackTrace();
exit();
}
- CaveGame caveGame = new CaveGame(gameFolder, true);
+ CaveGame caveGame = new CaveGame(gameFolder, true, null);
caveGame.setDebug(BuildConfig.DEBUG);
initialize(caveGame, config);
}
diff --git a/build.gradle b/build.gradle
index ed846ef9ecd276fccf4cd108d0db9ce443052a1d..39d1d37f36ac0e4f478c3cb5cd03b7215bff7646 100644 (file)
--- a/build.gradle
+++ b/build.gradle
buildscript {
+ ext {
+ appName = "CaveDroid"
+ gdxVersion = '1.12.0'
+ guavaVersion = '28.1'
+ daggerVersion = '2.51.1'
+ kotlinVersion = '1.9.23'
+ kotlinSerializationVersion = '1.6.3'
+ }
+
repositories {
mavenLocal()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
- jcenter()
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
+ classpath 'com.android.tools.build:gradle:8.2.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
allprojects {
- version = 'alpha0.4'
- ext {
- appName = "CaveDroid"
- gdxVersion = '1.9.10'
- roboVMVersion = '2.3.7'
- box2DLightsVersion = '1.4'
- ashleyVersion = '1.7.0'
- aiVersion = '1.8.0'
- guavaVersion = '28.1'
- }
+ version = 'alpha0.7.0'
repositories {
mavenLocal()
mavenCentral()
- jcenter()
google()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url "https://oss.sonatype.org/content/repositories/releases/" }
+ maven { url "https://jitpack.io" }
}
}
dependencies {
implementation project(":core")
- api "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
+ 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"
}
}
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"
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:2.27'
- implementation 'org.jetbrains:annotations:15.0'
- implementation "org.jetbrains.kotlin:kotlin-stdlib"
- annotationProcessor 'com.google.dagger:dagger-compiler:2.27'
+ 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
diff --git a/core/build.gradle b/core/build.gradle
index f17b1029a7e1f15f5ac5a79a78cfcde1c1986525..cdf2ee7e4708ffec3ab49447cc63298a1f77caab 100644 (file)
--- a/core/build.gradle
+++ b/core/build.gradle
plugins {
id "org.jetbrains.kotlin.jvm"
- id "java"
+ id "kotlin"
id "idea"
- id "net.ltgt.apt" version "0.21"
+ id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlinVersion"
}
-sourceCompatibility = 1.8
+sourceCompatibility = 17
-[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
+sourceSets.main.java.srcDirs = [ "src/" ]
-sourceSets.main.java.srcDirs = [ "src/" ]
\ No newline at end of file
+java.targetCompatibility = JavaVersion.VERSION_17
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/CaveGame.java b/core/src/ru/deadsoftware/cavedroid/CaveGame.java
index 031b6135ae3fb58153427d52e76814d598898124..1d795521e22bc82367e0a9de9bf14ddc40a37091 100644 (file)
package ru.deadsoftware.cavedroid;
+import com.badlogic.gdx.Application;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
-import ru.deadsoftware.cavedroid.game.GameItems;
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.4";
+ public static final String VERSION = "alpha 0.7.0";
private final MainConfig mMainConfig;
private final MainComponent mMainComponent;
+ private final AssetLoader mAssetLoader;
private final String mGameFolder;
private final boolean mTouch;
private boolean mDebug;
- public CaveGame(String gameFolder, boolean touch) {
+ @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) {
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() {
+ public void newGame(int gameMode) {
GameScreen gameScreen = mMainComponent.getGameScreen();
- gameScreen.newGame();
+ gameScreen.newGame(gameMode);
setScreen(gameScreen);
}
}
public void quitGame() {
+ if (screen != null) {
+ screen.dispose();
+ }
setScreen(mMainComponent.getMenuScreen());
}
@Override
public void create() {
- Gdx.app.log(TAG, mGameFolder);
Gdx.files.absolute(mGameFolder).mkdirs();
-
- Assets.load();
- GameItems.load();
-
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/MainComponent.java b/core/src/ru/deadsoftware/cavedroid/MainComponent.java
index d8a05da03fbdeda134cc144a6af804f881ff48d9..c640fb6c2abae2e5b5d12bc4574622419ab2e9b1 100644 (file)
import dagger.Component;
import ru.deadsoftware.cavedroid.game.GameScreen;
import ru.deadsoftware.cavedroid.menu.MenuScreen;
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader;
import javax.inject.Singleton;
MenuScreen getMenuScreen();
MainConfig getMainConfig();
+
+ AssetLoader getAssetLoader();
}
diff --git a/core/src/ru/deadsoftware/cavedroid/MainConfig.java b/core/src/ru/deadsoftware/cavedroid/MainConfig.java
index 8cd0ffdac6920e2977f46ac10c156e8be4f4ac7f..6577f297254f82928b2e18d28f29911d32764750 100644 (file)
import ru.deadsoftware.cavedroid.game.GameUiWindow;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
private float mWidth;
private float mHeight;
+ @Nullable
+ private String mAssetsPackPath = null;
+
@Inject
public MainConfig(CaveGame caveGame) {
mCaveGame = caveGame;
return mGameUiWindow == gameUiWindow;
}
- public GameUiWindow getGameUiWindow() {
- return mGameUiWindow;
- }
-
public void setGameUiWindow(GameUiWindow gameUiWindow) {
mGameUiWindow = gameUiWindow;
}
public void setShowMap(boolean showMap) {
mShowMap = showMap;
}
+
+ @Nullable
+ public String getAssetsPackPath() {
+ return mAssetsPackPath;
+ }
+
+ public void setAssetsPackPath(@Nullable String assetsPackPath) {
+ mAssetsPackPath = assetsPackPath;
+ }
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameComponent.java b/core/src/ru/deadsoftware/cavedroid/game/GameComponent.java
index 25779f06851e234c83c818872bf290064eccd82d..2dafcd5e0346fb00a7d2fec91d132f7bce74a664 100644 (file)
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;
@GameScope
-@Component(dependencies = MainComponent.class, modules = GameModule.class)
+@Component(dependencies = MainComponent.class,
+ modules = {GameModule.class,
+ UseItemActionsModule.class,
+ UpdateBlockActionsModule.class,
+ PlaceBlockActionsModule.class,
+ RenderModule.class,
+ KeyboardInputHandlersModule.class,
+ MouseInputHandlersModule.class
+ })
public interface GameComponent {
GameProc getGameProc();
- GameInputProcessor getGameInputProcessor();
+ GameItemsHolder getGameItemsHolder();
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameFluidsThread.java b/core/src/ru/deadsoftware/cavedroid/game/GameFluidsThread.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package ru.deadsoftware.cavedroid.game;
-
-import com.badlogic.gdx.utils.TimeUtils;
-import ru.deadsoftware.cavedroid.game.mobs.MobsController;
-
-import java.util.Arrays;
-
-import static ru.deadsoftware.cavedroid.game.GameItems.*;
-
-class GameFluidsThread extends Thread {
-
- private static final int FLUID_UPDATE_INTERVAL_MS = 100;
- private static final int FLUID_STATES = 5;
-
- private static final int[] WATER_IDS = {8, 60, 61, 62, 63};
- private static final int[] LAVA_IDS = {9, 64, 65, 66, 67};
-
- private long mFluidLastUpdateTimestamp = 0;
-
- private final GameWorld mGameWorld;
- private final MobsController mMobsController;
-
- private final Thread mMainThread;
-
- GameFluidsThread(GameWorld gameWorld,
- MobsController mobsController,
- Thread mainThread) {
- mGameWorld = gameWorld;
- mMobsController = mobsController;
- mMainThread = mainThread;
- }
-
- private int getBlockState(int id) {
- return isWater(id) ? Arrays.binarySearch(WATER_IDS, id) : Arrays.binarySearch(LAVA_IDS, id);
- }
-
- private int getNextBlockState(int id) {
- if (!isFluid(id)) {
- return -1;
- }
- int state = getBlockState(id);
- if (state < FLUID_STATES - 1) {
- return state + 1;
- }
- return -1;
- }
-
- private int getNextBlockStateId(int id) {
- int nextState = getNextBlockState(id);
- if (nextState == -1) {
- return 0;
- }
- if (isWater(id)) {
- return WATER_IDS[nextState];
- }
- return LAVA_IDS[nextState];
- }
-
- private int id(int x, int y) {
- return mGameWorld.getForeMap(x, y);
- }
-
- private boolean sameFluid(int thisId, int thatId) {
- return isFluid(thatId) && isWater(thatId) == isWater(thisId);
- }
-
- private boolean noFluidNearby(int x, int y) {
- return !isFluid(id(x, y - 1)) &&
- (!isFluid(id(x - 1, y)) || id(x - 1, y) >= id(x, y)) &&
- (!isFluid(id(x + 1, y)) || id(x + 1, y) >= id(x, y));
- }
-
- private boolean drainFluid(int x, int y) {
- if (getBlockState(id(x, y)) > 0) {
- if (noFluidNearby(x, y)) {
- mGameWorld.setForeMap(x, y, getNextBlockStateId(id(x, y)));
- }
- if (!isFluid(id(x, y))) {
- mGameWorld.setForeMap(x, y, 0);
- return true;
- }
- }
- return false;
- }
-
- private void flowFluidTo(int thisId, int x, int y, int nextStateId) {
- int thatId = id(x, y);
- if (fluidCanFlowThere(thisId, thatId)) {
- mGameWorld.setForeMap(x, y, nextStateId);
- } else if (isWater(thisId) && isLava(thatId)) {
- if (getBlockState(thatId) > 0) {
- mGameWorld.setForeMap(x, y, 4); //cobblestone
- } else {
- mGameWorld.setForeMap(x, y, 68); //obsidian
- }
- } else if (isLava(thisId) && isWater(thatId)) {
- mGameWorld.setForeMap(x, y, 1); //stone
- }
- }
-
- private void flowFluid(int x, int y) {
- int id = id(x, y);
- if (getBlockState(id) < FLUID_STATES - 1 && getBlock(id(x, y + 1)).hasCollision()) {
- int nextState = getNextBlockState(id);
- int nextStateId = getNextBlockStateId(id);
- if (nextState == 1) {
- nextStateId++;
- }
- flowFluidTo(id, x - 1, y, nextStateId);
- flowFluidTo(id, x + 1, y, nextStateId);
- } else {
- flowFluidTo(id, x, y + 1, isWater(id) ? WATER_IDS[1] : LAVA_IDS[1]);
- }
-
- }
-
- private void updateFluids(int x, int y) {
- if (!isFluid(id(x, y))) {
- 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 <= mGameWorld.getWidth() / 2; x++) {
- updateFluids(midScreen + x, y);
- updateFluids(midScreen - x, y);
- }
- }
- }
-
- private boolean timeToUpdate() {
- if (TimeUtils.timeSinceMillis(mFluidLastUpdateTimestamp) >= FLUID_UPDATE_INTERVAL_MS) {
- mFluidLastUpdateTimestamp = TimeUtils.millis();
- return true;
- }
- return false;
- }
-
- @Override
- public void run() {
- while (!this.isInterrupted() && mMainThread.isAlive()) {
- if (timeToUpdate()) {
- fluidUpdater();
- }
- }
- }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameInput.java b/core/src/ru/deadsoftware/cavedroid/game/GameInput.java
+++ /dev/null
@@ -1,467 +0,0 @@
-package ru.deadsoftware.cavedroid.game;
-
-import com.badlogic.gdx.Gdx;
-import com.badlogic.gdx.Input;
-import com.badlogic.gdx.graphics.g2d.TextureRegion;
-import com.badlogic.gdx.utils.TimeUtils;
-import com.google.common.collect.Range;
-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.Pig;
-import ru.deadsoftware.cavedroid.game.mobs.Player;
-import ru.deadsoftware.cavedroid.game.objects.DropController;
-import ru.deadsoftware.cavedroid.misc.Assets;
-import ru.deadsoftware.cavedroid.misc.ControlMode;
-
-import javax.inject.Inject;
-
-import static ru.deadsoftware.cavedroid.game.GameItems.*;
-
-@GameScope
-public class GameInput {
-
- private final MainConfig mMainConfig;
- private final GameWorld mGameWorld;
- private final DropController mDropController;
- private final MobsController mMobsController;
- private final Player mPlayer;
-
- private ControlMode mControlMode;
-
- private boolean mKeyDown;
- private boolean mTouchedDown;
- private boolean mDragging;
-
- private int mKeyDownCode;
- private int mTouchDownBtn;
- private float mTouchDownX;
- private float mTouchDownY;
- private long mTouchDownTime;
-
- private int mCurX;
- private int mCurY;
- private int mCreativeScroll;
- private int mBlockDamage;
-
- @Inject
- public GameInput(MainConfig mainConfig,
- GameWorld gameWorld,
- DropController dropController,
- MobsController mobsController) {
- mMainConfig = mainConfig;
- mGameWorld = gameWorld;
- mDropController = dropController;
- mMobsController = mobsController;
-
- mPlayer = mMobsController.getPlayer();
-
- mControlMode = mMainConfig.isTouch() ? ControlMode.WALK : ControlMode.CURSOR;
- }
-
- private boolean checkSwim() {
- return GameItems.isFluid(mGameWorld.getForeMap(mPlayer.getMapX(), mPlayer.getLowerMapY()));
- }
-
- private void goUpwards() {
- if (checkSwim()) {
- mPlayer.swim = true;
- } else if (mPlayer.canJump()) {
- mPlayer.getMove().add(0, -7);
- } else if (!mPlayer.isFlyMode() && mPlayer.gameMode == 1) {
- mPlayer.setFlyMode(true);
- mPlayer.getMove().y = 0;
- } else if (mPlayer.isFlyMode()) {
- mPlayer.getMove().y = -GamePhysics.PL_SPEED;
- }
- }
-
- @SuppressWarnings("IntegerDivisionInFloatingPointContext")
- private boolean insideCreativeInv(float screenX, float screenY) {
- TextureRegion creative = Assets.textureRegions.get("creative");
- return (screenX > mMainConfig.getWidth() / 2 - creative.getRegionWidth() / 2 &&
- screenX < mMainConfig.getWidth() / 2 + creative.getRegionWidth() / 2 &&
- screenY > mMainConfig.getHeight() / 2 - creative.getRegionHeight() / 2 &&
- screenY < mMainConfig.getHeight() / 2 + creative.getRegionHeight() / 2);
- }
-
- private void wasdPressed(int keycode) {
- if (mControlMode == ControlMode.WALK || !mMainConfig.isTouch()) {
- switch (keycode) {
- case Input.Keys.A:
- mPlayer.getMove().x = -GamePhysics.PL_SPEED;
- mPlayer.setDir(Mob.Direction.LEFT);
- if (mMainConfig.isTouch() && checkSwim()) {
- mPlayer.swim = true;
- }
- break;
- case Input.Keys.D:
- mPlayer.getMove().x = GamePhysics.PL_SPEED;
- mPlayer.setDir(Mob.Direction.RIGHT);
- if (mMainConfig.isTouch() && checkSwim()) {
- mPlayer.swim = true;
- }
- break;
- case Input.Keys.W:
- case Input.Keys.SPACE:
- goUpwards();
- break;
- case Input.Keys.S:
- case Input.Keys.CONTROL_LEFT:
- mPlayer.getMove().y = GamePhysics.PL_SPEED;
- break;
- }
- } else {
- switch (keycode) {
- case Input.Keys.A:
- mCurX--;
- break;
- case Input.Keys.D:
- mCurX++;
- break;
- case Input.Keys.W:
- mCurY--;
- break;
- case Input.Keys.S:
- mCurY++;
- break;
- }
- mBlockDamage = 0;
- }
- }
-
- private boolean isNotAutoselectable(int x, int y) {
- return (!mGameWorld.hasForeAt(x, y) || !mGameWorld.getForeMapBlock(x, y).hasCollision());
- }
-
- private void checkCursorBounds() {
- if (mCurY < 0) {
- mCurY = 0;
- } else if (mCurY >= mGameWorld.getHeight()) {
- mCurY = mGameWorld.getHeight() - 1;
- }
-
- if (mControlMode == ControlMode.CURSOR) {
- if (mCurX * 16 + 8 < mPlayer.getX() + mPlayer.getWidth() / 2) {
- mPlayer.setDir(Mob.Direction.LEFT);
- } else {
- mPlayer.setDir(Mob.Direction.RIGHT);
- }
- }
- }
-
- public void moveCursor(GameRenderer gameRenderer) {
- int pastX = mCurX;
- int pastY = mCurY;
-
- if (mControlMode == ControlMode.WALK && mMainConfig.isTouch()) {
- mCurX = mPlayer.getMapX() + (mPlayer.looksLeft() ? -1 : 1);
- mCurY = mPlayer.getUpperMapY();
- for (int i = 0; i < 2 && isNotAutoselectable(mCurX, mCurY); i++) {
- mCurY++;
- }
- if (isNotAutoselectable(mCurX, mCurY)) {
- mCurX += mPlayer.looksLeft() ? 1 : -1;
- }
- } else if (!mMainConfig.isTouch()) {
- mCurX = (int) (Gdx.input.getX() * (mMainConfig.getWidth() /
- Gdx.graphics.getWidth()) + gameRenderer.getCamX()) / 16;
-
- mCurY = (int) (Gdx.input.getY() * (mMainConfig.getHeight() /
- Gdx.graphics.getHeight()) + gameRenderer.getCamY()) / 16;
- if (mCurX < 0) {
- mCurX--;
- }
- }
-
- if (pastX != mCurX || pastY != mCurY) {
- mBlockDamage = 0;
- }
-
- checkCursorBounds();
- }
-
- private void useItem(int x, int y, int id, boolean bg) {
- String key = getItem(id).isBlock() ? getBlockKey(id) : getItemKey(id);
- if (id > 0) {
- if (getItem(id).isBlock()) {
- if (!bg) {
- mGameWorld.placeToForeground(x, y, getBlockIdByItemId(id));
- } else {
- mGameWorld.placeToBackground(x, y, getBlockIdByItemId(id));
- }
- } else {
- switch (key) {
- case "bucket_water":
- mGameWorld.placeToForeground(x, y, getBlockId("water"));
- mPlayer.inventory[mPlayer.slot] = getItemId("bucket_empty");
- break;
- case "bucket_lava":
- mGameWorld.placeToForeground(x, y, getBlockId("lava"));
- mPlayer.inventory[mPlayer.slot] = getItemId("bucket_empty");
- break;
- }
- }
- }
- }
-
- private void pressLMB() {
- if (mMainConfig.checkGameUiWindow(GameUiWindow.NONE) &&
- ((mGameWorld.hasForeAt(mCurX, mCurY) && mGameWorld.getForeMapBlock(mCurX, mCurY).getHp() >= 0) ||
- (!mGameWorld.hasForeAt(mCurX, mCurY) && mGameWorld.hasBackAt(mCurX, mCurY) &&
- mGameWorld.getBackMapBlock(mCurX, mCurY).getHp() >= 0))) {
- if (mPlayer.gameMode == 0) {
- mBlockDamage++;
- if (mGameWorld.hasForeAt(mCurX, mCurY)) {
- if (mBlockDamage >= mGameWorld.getForeMapBlock(mCurX, mCurY).getHp()) {
- mGameWorld.destroyForeMap(mCurX, mCurY);
- mBlockDamage = 0;
- }
- } else if (mGameWorld.hasBackAt(mCurX, mCurY)) {
- if (mBlockDamage >= mGameWorld.getBackMapBlock(mCurX, mCurY).getHp()) {
- mGameWorld.destroyBackMap(mCurX, mCurY);
- mBlockDamage = 0;
- }
- }
- } else {
- if (mGameWorld.hasForeAt(mCurX, mCurY)) {
- mGameWorld.placeToForeground(mCurX, mCurY, 0);
- } else if (mGameWorld.hasBackAt(mCurX, mCurY)) {
- mGameWorld.placeToBackground(mCurX, mCurY, 0);
- }
- mTouchedDown = false;
- }
- }
- }
-
- private boolean insideHotbar(float x, float y) {
- TextureRegion hotbar = Assets.textureRegions.get("hotbar");
- return y < hotbar.getRegionHeight() &&
- Range.open(mMainConfig.getWidth() / 2 - (float) hotbar.getRegionWidth() / 2,
- mMainConfig.getWidth() / 2 + (float) hotbar.getRegionWidth() / 2).contains(x);
- }
-
- private void holdMB() {
- if (mTouchDownBtn == Input.Buttons.RIGHT) {
- useItem(mCurX, mCurY, mPlayer.inventory[mPlayer.slot], true);
- mTouchedDown = false;
- } else {
- if (insideHotbar(mTouchDownX, mTouchDownY)) {
- mMainConfig.setGameUiWindow(GameUiWindow.CREATIVE_INVENTORY);
- mTouchedDown = false;
- }
- }
- }
-
- public void keyDown(int keycode) {
- mKeyDown = true;
- mKeyDownCode = keycode;
- switch (keycode) {
- case Input.Keys.A:
- case Input.Keys.D:
- case Input.Keys.W:
- case Input.Keys.S:
- case Input.Keys.SPACE:
- case Input.Keys.CONTROL_LEFT:
- wasdPressed(keycode);
- break;
-
- case Input.Keys.ALT_LEFT:
- if (mMainConfig.isTouch()) {
- mControlMode = mControlMode == ControlMode.WALK ? ControlMode.CURSOR : ControlMode.WALK;
- }
- break;
-
- case Input.Keys.E:
- if (mMainConfig.checkGameUiWindow(GameUiWindow.NONE)) {
- switch (mPlayer.gameMode) {
- case 0:
- //TODO survival inv
- break;
- case 1:
- mMainConfig.setGameUiWindow(GameUiWindow.CREATIVE_INVENTORY);
- break;
- }
- } else {
- mMainConfig.setGameUiWindow(GameUiWindow.NONE);
- }
- break;
-
- case Input.Keys.G:
- mMobsController.addMob(Pig.class, mCurX * 16, mCurY * 16);
- break;
-
- case Input.Keys.Q:
- mGameWorld.placeToForeground(mCurX, mCurY, 8);
- break;
-
- case Input.Keys.ESCAPE:
- case Input.Keys.BACK:
- GameSaver.save(mMainConfig, mDropController, mMobsController, mGameWorld);
- mMainConfig.getCaveGame().quitGame();
- break;
-
- case Input.Keys.F1:
- mMainConfig.setShowInfo(!mMainConfig.isShowInfo());
- break;
-
- case Input.Keys.M:
- mMainConfig.setShowMap(!mMainConfig.isShowMap());
- break;
- }
- }
-
- public void keyUp(int keycode) {
- switch (keycode) {
- case Input.Keys.A:
- case Input.Keys.D:
- mPlayer.getMove().x = 0;
- if (mMainConfig.isTouch() && mPlayer.swim) {
- mPlayer.swim = false;
- }
- break;
-
- case Input.Keys.W:
- case Input.Keys.S:
- case Input.Keys.SPACE:
- case Input.Keys.CONTROL_LEFT:
- if (mPlayer.isFlyMode()) {
- mPlayer.getMove().y = 0;
- }
- if (mPlayer.swim) {
- mPlayer.swim = false;
- }
- break;
- }
- }
-
- public void touchDown(float touchX, float touchY, int button) {
- mTouchDownTime = TimeUtils.millis();
- mTouchedDown = true;
- mTouchDownBtn = button;
- mTouchDownX = touchX;
- mTouchDownY = touchY;
- }
-
- public void touchUp(float screenX, float screenY, int button) {
- if (mDragging) {
- mDragging = false;
- return;
- }
-
- if (mMainConfig.isTouch() && mKeyDown) {
- keyUp(mKeyDownCode);
- mKeyDown = false;
- }
- TextureRegion hotbar = Assets.textureRegions.get("hotbar");
- TextureRegion creative = Assets.textureRegions.get("creative");
- if (mTouchedDown) {
- if (mMainConfig.checkGameUiWindow(GameUiWindow.CREATIVE_INVENTORY) && insideCreativeInv(screenX, screenY)) {
- int ix = (int) (screenX - (mMainConfig.getWidth() / 2 - creative.getRegionWidth() / 2 + 8)) / 18;
- int iy = (int) (screenY - (mMainConfig.getHeight() / 2 - creative.getRegionHeight() / 2 + 18)) / 18;
- int item = mCreativeScroll * 8 + (ix + iy * 8);
- if (ix >= 8 || ix < 0 || iy < 0 || iy >= 5) {
- item = -1;
- }
- if (item >= 0 && item < GameItems.getItemsSize()) {
- System.arraycopy(mPlayer.inventory, 0, mPlayer.inventory, 1, 8);
- mPlayer.inventory[0] = item;
- }
- } else if (mMainConfig.checkGameUiWindow(GameUiWindow.CREATIVE_INVENTORY)) {
- mMainConfig.setGameUiWindow(GameUiWindow.NONE);
- } else if (screenY < hotbar.getRegionHeight() &&
- screenX > mMainConfig.getWidth() / 2 - (float) hotbar.getRegionWidth() / 2 &&
- screenX < mMainConfig.getWidth() / 2 + (float) hotbar.getRegionWidth() / 2) {
- mPlayer.slot = (int) ((screenX - (mMainConfig.getWidth() / 2 - hotbar.getRegionWidth() / 2)) / 20);
- } else if (button == Input.Buttons.RIGHT) {
- useItem(mCurX, mCurY,
- mPlayer.inventory[mPlayer.slot], false);
- } else if (button == Input.Buttons.LEFT) {
- mBlockDamage = 0;
- }
- }
- mTouchedDown = false;
- }
-
- public void touchDragged(float screenX, float screenY) {
- mDragging = true;
- if (mMainConfig.checkGameUiWindow(GameUiWindow.CREATIVE_INVENTORY) && Math.abs(screenY - mTouchDownY) > 16) {
- if (insideCreativeInv(screenX, screenY)) {
- mCreativeScroll -= (screenY - mTouchDownY) / 16;
- mTouchDownX = screenX;
- mTouchDownY = screenY;
- if (mCreativeScroll < 0) {
- mCreativeScroll = 0;
- }
- if (mCreativeScroll > GameProc.MAX_CREATIVE_SCROLL) {
- mCreativeScroll = GameProc.MAX_CREATIVE_SCROLL;
- }
- }
- }
- }
-
- public void scrolled(int amount) {
- switch (mMainConfig.getGameUiWindow()) {
- case NONE:
- mPlayer.slot += amount;
- if (mPlayer.slot < 0) {
- mPlayer.slot = 8;
- }
- if (mPlayer.slot > 8) {
- mPlayer.slot = 0;
- }
- break;
- case CREATIVE_INVENTORY:
- mCreativeScroll += amount;
- if (mCreativeScroll < 0) {
- mCreativeScroll = 0;
- }
- if (mCreativeScroll > GameProc.MAX_CREATIVE_SCROLL) {
- mCreativeScroll = GameProc.MAX_CREATIVE_SCROLL;
- }
- break;
- }
- }
-
- public int getKeyDownCode() {
- return mKeyDownCode;
- }
-
- public boolean isKeyDown() {
- return mKeyDown;
- }
-
- int getBlockDamage() {
- return mBlockDamage;
- }
-
- int getCurX() {
- return mCurX;
- }
-
- int getCurY() {
- return mCurY;
- }
-
- int getCreativeScroll() {
- return mCreativeScroll;
- }
-
- public ControlMode getControlMode() {
- return mControlMode;
- }
-
- public void setControlMode(ControlMode controlMode) {
- mControlMode = controlMode;
- }
-
- void update() {
- if (mTouchedDown && mTouchDownBtn == Input.Buttons.LEFT) {
- pressLMB();
- }
- if (mTouchedDown && TimeUtils.timeSinceMillis(mTouchDownTime) > 500) {
- holdMB();
- }
- }
-
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameInputProcessor.java b/core/src/ru/deadsoftware/cavedroid/game/GameInputProcessor.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package ru.deadsoftware.cavedroid.game;
-
-import com.badlogic.gdx.Gdx;
-import com.badlogic.gdx.Input;
-import com.badlogic.gdx.InputAdapter;
-import com.badlogic.gdx.math.Rectangle;
-import com.badlogic.gdx.utils.JsonValue;
-import ru.deadsoftware.cavedroid.MainConfig;
-import ru.deadsoftware.cavedroid.game.objects.TouchButton;
-import ru.deadsoftware.cavedroid.misc.Assets;
-
-import javax.inject.Inject;
-
-import static com.badlogic.gdx.utils.ObjectMap.Entry;
-
-@GameScope
-public class GameInputProcessor extends InputAdapter {
-
- private static final TouchButton nullButton = new TouchButton(null, -1, true);
-
- private final GameInput mGameInput;
- private final GameRenderer mGameRenderer;
- private final MainConfig mMainConfig;
-
- @Inject
- public GameInputProcessor(GameInput gameInput,
- GameRenderer gameRenderer,
- MainConfig mainConfig) {
- mGameInput = gameInput;
- mGameRenderer = gameRenderer;
- mMainConfig = mainConfig;
-
- loadTouchButtonsFromJSON();
- }
-
- private int getMouseKey(String name) {
- switch (name) {
- case "Left":
- return Input.Buttons.LEFT;
- case "Right":
- return Input.Buttons.RIGHT;
- case "Middle":
- return Input.Buttons.MIDDLE;
- case "Back":
- return Input.Buttons.BACK;
- case "Forward":
- return Input.Buttons.FORWARD;
- default:
- return -1;
- }
- }
-
- private void loadTouchButtonsFromJSON() {
- JsonValue json = Assets.jsonReader.parse(Gdx.files.internal("json/touch_buttons.json"));
- for (JsonValue key = json.child(); key != null; key = key.next()) {
- float x = key.getFloat("x");
- float y = key.getFloat("y");
- float w = key.getFloat("w");
- float h = key.getFloat("h");
- boolean mouse = Assets.getBooleanFromJson(key, "mouse", false);
- String name = key.getString("key");
- int code = mouse ? getMouseKey(name) : Input.Keys.valueOf(name);
- if (x < 0) {
- x = mGameRenderer.getWidth() + x;
- }
- if (y < 0) {
- y = mGameRenderer.getHeight() + y;
- }
- Assets.guiMap.put(key.name(), new TouchButton(new Rectangle(x, y, w, h), code, mouse));
- }
-
- }
-
- private float transformScreenX(int screenX) {
- return mGameRenderer.getWidth() / Gdx.graphics.getWidth() * screenX;
- }
-
- private float transformScreenY(int screenY) {
- return mGameRenderer.getHeight() / Gdx.graphics.getHeight() * screenY;
- }
-
- private TouchButton getTouchedKey(float touchX, float touchY) {
- for (Entry<String, TouchButton> entry : Assets.guiMap) {
- TouchButton button = entry.value;
- if (button.getRect().contains(touchX, touchY)) {
- return button;
- }
- }
- return nullButton;
- }
-
- @Override
- public boolean keyDown(int keycode) {
- mGameInput.keyDown(keycode);
- return false;
- }
-
- @Override
- public boolean keyUp(int keycode) {
- mGameInput.keyUp(keycode);
- return false;
- }
-
- @Override
- public boolean touchDown(int screenX, int screenY, int pointer, int button) {
- float touchX = transformScreenX(screenX);
- float touchY = transformScreenY(screenY);
-
- if (mMainConfig.isTouch()) {
- TouchButton touchedKey = getTouchedKey(touchX, touchY);
- if (touchedKey.isMouse()) {
- mGameInput.touchDown(touchX, touchY, touchedKey.getCode());
- } else {
- mGameInput.keyDown(touchedKey.getCode());
- }
- } else {
- mGameInput.touchDown(touchX, touchY, button);
- }
- return false;
- }
-
- @Override
- public boolean touchUp(int screenX, int screenY, int pointer, int button) {
- float touchX = transformScreenX(screenX);
- float touchY = transformScreenY(screenY);
-
- if (mMainConfig.isTouch()) {
- TouchButton touchedKey = getTouchedKey(touchX, touchY);
- if (touchedKey.isMouse()) {
- mGameInput.touchUp(touchX, touchY, touchedKey.getCode());
- } else {
- mGameInput.keyUp(mGameInput.getKeyDownCode());
- }
- } else {
- mGameInput.touchUp(touchX, touchY, button);
- }
- return false;
- }
-
- @Override
- public boolean touchDragged(int screenX, int screenY, int pointer) {
- float touchX = transformScreenX(screenX);
- float touchY = transformScreenY(screenY);
- if (mMainConfig.isTouch() && mGameInput.isKeyDown()) {
- if (getTouchedKey(touchX, touchY).getCode() == -1) {
- mGameInput.keyUp(mGameInput.getKeyDownCode());
- }
- } else {
- mGameInput.touchDragged(touchX, touchY);
- }
- return false;
- }
-
- @Override
- public boolean scrolled(int amount) {
- mGameInput.scrolled(amount);
- return false;
- }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameItems.java b/core/src/ru/deadsoftware/cavedroid/game/GameItems.java
+++ /dev/null
@@ -1,170 +0,0 @@
-package ru.deadsoftware.cavedroid.game;
-
-import com.badlogic.gdx.Gdx;
-import com.badlogic.gdx.graphics.Texture;
-import com.badlogic.gdx.graphics.g2d.Sprite;
-import com.badlogic.gdx.utils.ArrayMap;
-import com.badlogic.gdx.utils.GdxRuntimeException;
-import com.badlogic.gdx.utils.JsonValue;
-import ru.deadsoftware.cavedroid.game.objects.Block;
-import ru.deadsoftware.cavedroid.game.objects.Item;
-import ru.deadsoftware.cavedroid.misc.Assets;
-
-import java.util.HashMap;
-
-public class GameItems {
-
- private static final String TAG = "GameItems";
-
- private static final HashMap<String, Integer> blocksIds = new HashMap<>();
- private static final HashMap<String, Integer> itemsIds = new HashMap<>();
-
- private static final ArrayMap<String, Block> blocks = new ArrayMap<>();
- private static final ArrayMap<String, Item> items = new ArrayMap<>();
-
- public static boolean isFluid(int id) {
- return getBlock(id).isFluid();
- }
-
- public static boolean isWater(int id) {
- return getBlock(id).getMeta().equals("water");
- }
-
- public static boolean isLava(int id) {
- return getBlock(id).getMeta().equals("lava");
- }
-
- public static boolean isSlab(int id) {
- return getBlock(id).getMeta().equals("slab");
- }
-
- public static boolean fluidCanFlowThere(int thisId, int thatId) {
- return thatId == 0 || (!getBlock(thatId).hasCollision() && !isFluid(thatId)) ||
- (isWater(thisId) && isWater(thatId) && thisId < thatId) ||
- (isLava(thisId) && isLava(thatId) && thisId < thatId);
- }
-
- public static Block getBlock(int id) {
- return blocks.getValueAt(id);
- }
-
- public static Item getItem(int id) {
- return items.getValueAt(id);
- }
-
- public static Block getBlock(String key) {
- return blocks.getValueAt(blocksIds.get(key));
- }
-
- public static Item getItem(String key) {
- return items.getValueAt(itemsIds.get(key));
- }
-
- public static int getBlockId(String key) {
- return blocksIds.get(key);
- }
-
- public static int getItemId(String key) {
- return itemsIds.get(key);
- }
-
- public static String getBlockKey(int id) {
- return blocks.getKeyAt(id);
- }
-
- public static String getItemKey(int id) {
- return items.getKeyAt(id);
- }
-
- public static int getBlockIdByItemId(int id) {
- return getBlockId(items.getKeyAt(id));
- }
-
- public static int getBlocksSize() {
- return blocks.size;
- }
-
- public static int getItemsSize() {
- return items.size;
- }
-
- public static Sprite getBlockTex(int id) {
- return getBlock(id).getTexture();
- }
-
- public static Sprite getItemTex(int id) {
- return items.getValueAt(id).getType().equals("block") ? getBlockTex(id) : getItem(id).getTexture();
- }
-
- public static void load() {
- JsonValue json = Assets.jsonReader.parse(Gdx.files.internal("json/game_items.json"));
- for (JsonValue block = json.get("blocks").child(); block != null; block = block.next()) {
- try {
- String key = block.name();
- int left = Assets.getIntFromJson(block, "left", 0);
- int right = Assets.getIntFromJson(block, "right", 0);
- int top = Assets.getIntFromJson(block, "top", 0);
- int bottom = Assets.getIntFromJson(block, "bottom", 0);
- int clipX = Assets.getIntFromJson(block, "sprite_left", 0);
- int clipY = Assets.getIntFromJson(block, "sprite_top", 0);
- int clipWidth = Assets.getIntFromJson(block, "sprite_right", 0);
- int clipHeight = Assets.getIntFromJson(block, "sprite_bottom", 0);
- int hp = Assets.getIntFromJson(block, "hp", -1);
- boolean collision = Assets.getBooleanFromJson(block, "collision", true);
- boolean background = Assets.getBooleanFromJson(block, "background", false);
- boolean transparent = Assets.getBooleanFromJson(block, "transparent", false);
- boolean requiresBlock = Assets.getBooleanFromJson(block, "block_required", false);
- boolean fluid = Assets.getBooleanFromJson(block, "fluid", false);
- String drop = Assets.getStringFromJson(block, "drop", key);
- String meta = Assets.getStringFromJson(block, "meta", "");
- String tex = Assets.getStringFromJson(block, "texture", key);
- Texture texture = tex.equals("none") ? null :
- new Texture(Gdx.files.internal("textures/blocks/" + tex + ".png"));
- boolean animated = Assets.getBooleanFromJson(block, "animated", false);
- int frames = Assets.getIntFromJson(block, "frames", 0);
-
- Block newBlock = new Block(
- left,
- top,
- right,
- bottom,
- hp,
- drop,
- collision,
- background,
- transparent,
- requiresBlock,
- fluid,
- meta,
- texture,
- animated,
- frames,
- clipX,
- clipY,
- clipWidth,
- clipHeight
- );
-
- blocksIds.put(key, blocks.size);
- blocks.put(key, newBlock);
- } catch (GdxRuntimeException e) {
- Gdx.app.error(TAG, e.getMessage());
- }
- }
- for (JsonValue item = json.get("items").child(); item != null; item = item.next()) {
- try {
- String key = item.name();
- String name = Assets.getStringFromJson(item, "name", key);
- String type = Assets.getStringFromJson(item, "type", "item");
- String texture = Assets.getStringFromJson(item, "texture", key);
- Sprite sprite = type.equals("block") ? null :
- new Sprite(new Texture(Gdx.files.internal("textures/items/" + texture + ".png")));
- itemsIds.put(key, items.size);
- items.put(key, new Item(name, type, sprite));
- } catch (GdxRuntimeException e) {
- Gdx.app.error(TAG, e.getMessage());
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameItemsHolder.kt b/core/src/ru/deadsoftware/cavedroid/game/GameItemsHolder.kt
--- /dev/null
@@ -0,0 +1,167 @@
+package ru.deadsoftware.cavedroid.game
+
+import com.badlogic.gdx.Gdx
+import kotlinx.serialization.json.Json
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.craft.CraftingRecipe
+import ru.deadsoftware.cavedroid.game.model.craft.CraftingResult
+import ru.deadsoftware.cavedroid.game.model.dto.BlockDto
+import ru.deadsoftware.cavedroid.game.model.dto.CraftingDto
+import ru.deadsoftware.cavedroid.game.model.dto.GameItemsDto
+import ru.deadsoftware.cavedroid.game.model.dto.ItemDto
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.model.mapper.BlockMapper
+import ru.deadsoftware.cavedroid.game.model.mapper.ItemMapper
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader
+import java.util.LinkedList
+import javax.inject.Inject
+
+@GameScope
+class GameItemsHolder @Inject constructor(
+ private val assetLoader: AssetLoader,
+ private val blockMapper: BlockMapper,
+ private val itemMapper: ItemMapper,
+) {
+
+ private var _initialized: Boolean = false
+
+ private val blocksMap = LinkedHashMap<String, Block>()
+ private val itemsMap = LinkedHashMap<String, Item>()
+ private val craftingRecipes = LinkedList<CraftingRecipe>()
+
+ lateinit var fallbackBlock: Block
+ private set
+ lateinit var fallbackItem: Item
+ private set
+
+ init {
+ initialize()
+ }
+
+ private fun loadBlocks(dtoMap: Map<String, BlockDto>) {
+ dtoMap.forEach { (key, dto) ->
+ blocksMap[key] = blockMapper.map(key, dto)
+ .apply(Block::initialize)
+ }
+
+ fallbackBlock = blocksMap[FALLBACK_BLOCK_KEY]
+ ?: throw IllegalArgumentException("Fallback block key '$FALLBACK_BLOCK_KEY' not found")
+ }
+
+ private fun loadItems(dtoMap: Map<String, ItemDto>) {
+ if (dtoMap.isNotEmpty() && blocksMap.isEmpty()) {
+ throw IllegalStateException("items should be loaded after blocks")
+ }
+
+ dtoMap.forEach { (key, dto) ->
+ try {
+ itemsMap[key] = itemMapper.map(
+ key = key,
+ dto = dto,
+ block = blocksMap[key],
+ slabTopBlock = blocksMap[dto.topSlabBlock] as? Block.Slab,
+ slabBottomBlock = blocksMap[dto.bottomSlabBlock] as? Block.Slab
+ )
+ } catch (e: Exception) {
+ Gdx.app.error(TAG, "Failed to map item $key. Reason: ${e.message}")
+ e.printStackTrace()
+ }
+ }
+
+ fallbackItem = itemsMap[FALLBACK_ITEM_KEY]
+ ?: throw IllegalArgumentException("Fallback item key '$FALLBACK_ITEM_KEY' not found")
+ }
+
+ private fun loadCraftingRecipes() {
+ val jsonString = assetLoader.getAssetHandle("json/crafting.json").readString()
+ val jsonMap = JsonFormat.decodeFromString<Map<String, CraftingDto>>(jsonString)
+
+ if (jsonMap.isNotEmpty() && itemsMap.isEmpty()) {
+ throw IllegalStateException("items should be loaded before crafting")
+ }
+
+ jsonMap.forEach { (key, value) ->
+ craftingRecipes += CraftingRecipe(
+ input = value.input.map(::getItem),
+ output = CraftingResult(getItem(key), value.count)
+ )
+ }
+ }
+
+ fun initialize() {
+ if (_initialized) {
+ Gdx.app.debug(TAG, "Attempted to init when already initialized")
+ return
+ }
+
+ val jsonString = assetLoader.getAssetHandle("json/game_items.json").readString()
+ val gameItemsDto = JsonFormat.decodeFromString<GameItemsDto>(jsonString)
+
+ loadBlocks(gameItemsDto.blocks)
+ loadItems(gameItemsDto.items)
+
+ _initialized = true
+
+ loadCraftingRecipes()
+ }
+
+ private fun <T> Map<String, T>.getOrFallback(key: String, fallback: T, lazyErrorMessage: () -> String): T {
+ if (!_initialized) {
+ throw IllegalStateException("GameItemsHolder was not initialized before use")
+ }
+
+ val t = this[key] ?: run {
+ Gdx.app.error(TAG, lazyErrorMessage.invoke())
+ return fallback
+ }
+ return t
+ }
+
+ fun getBlock(key: String): Block {
+ return blocksMap.getOrFallback(key, fallbackBlock) {
+ "No block with key '$key' found. Returning $FALLBACK_BLOCK_KEY"
+ }
+ }
+
+ fun getItem(key: String): Item {
+ return itemsMap.getOrFallback(key, fallbackItem) {
+ "No item with key '$key' found. Returning $FALLBACK_BLOCK_KEY"
+ }
+ }
+
+ fun craftItem(input: List<Item>): InventoryItem? {
+ return try {
+ craftingRecipes.first { rec -> rec.input == input}.output.toInventoryItem()
+ } catch (e: NoSuchElementException) {
+ null
+ }
+ }
+
+ fun getAllItems(): Collection<Item> {
+ return itemsMap.values
+ }
+
+ fun getItemFromCreativeInventory(position: Int): Item {
+ return if (position in itemsMap.values.indices) {
+ itemsMap.values.elementAt(position)
+ } else {
+ fallbackItem
+ }
+ }
+
+ fun getMaxCreativeScrollAmount(): Int = itemsMap.size / 8
+
+ fun <T : Block> getBlocksByType(type: Class<T>): List<T> {
+ return blocksMap.values.filterIsInstance(type)
+ }
+
+ companion object {
+ private const val TAG = "GameItemsHolder"
+
+ private val JsonFormat = Json { ignoreUnknownKeys = true }
+
+ const val FALLBACK_BLOCK_KEY = "none"
+ const val FALLBACK_ITEM_KEY = "none"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameModule.java b/core/src/ru/deadsoftware/cavedroid/game/GameModule.java
index d7ccc2d6e44bb7744aa6fa8f84cafd4fcd439a55..29175da1968fba60e36f64a8ebb8ec746c73b630 100644 (file)
import dagger.Provides;
import ru.deadsoftware.cavedroid.MainConfig;
import ru.deadsoftware.cavedroid.game.mobs.MobsController;
+import ru.deadsoftware.cavedroid.game.model.block.Block;
import ru.deadsoftware.cavedroid.game.objects.DropController;
+import ru.deadsoftware.cavedroid.game.world.GameWorld;
import javax.annotation.CheckForNull;
@CheckForNull
private static GameSaver.Data data;
- public static void load(MainConfig mainConfig) {
- data = GameSaver.load(mainConfig);
+ public static boolean loaded = false;
+
+ private static void load(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
+ if (loaded) {
+ return;
+ }
+ data = GameSaver.load(mainConfig, gameItemsHolder);
+ loaded = true;
}
private static void makeDataNullIfEmpty() {
@Provides
@GameScope
- public static DropController provideDropController() {
+ public static DropController provideDropController(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
+ load(mainConfig, gameItemsHolder);
DropController controller = data != null ? data.retrieveDropController() : new DropController();
makeDataNullIfEmpty();
+ controller.initDrops(gameItemsHolder);
return controller;
}
@Provides
@GameScope
- public static MobsController provideMobsController() {
- MobsController controller = data != null ? data.retrieveMobsController() : new MobsController();
+ public static MobsController provideMobsController(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
+ load(mainConfig, gameItemsHolder);
+ MobsController controller = data != null ? data.retrieveMobsController() : new MobsController(gameItemsHolder);
makeDataNullIfEmpty();
+ controller.getPlayer().initInventory(gameItemsHolder);
return controller;
}
@Provides
@GameScope
- public static GameWorld provideGameWorld(DropController dropController, MobsController mobsController) {
- int[][] fm = data != null ? data.retrieveForeMap() : null;
- int[][] bm = data != null ? data.retrieveBackMap() : null;
+ public static GameWorld provideGameWorld(MainConfig mainConfig,
+ DropController dropController,
+ MobsController mobsController,
+ GameItemsHolder gameItemsHolder) {
+ load(mainConfig, gameItemsHolder);
+ Block[][] fm = data != null ? data.retrieveForeMap() : null;
+ Block[][] bm = data != null ? data.retrieveBackMap() : null;
makeDataNullIfEmpty();
- return new GameWorld(dropController, mobsController, fm, bm);
+ return new GameWorld(dropController, mobsController, gameItemsHolder, fm, bm);
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java b/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java
index d6b5a9aa5da8e907c07430e1670cce350a61a757..a0c8c378ef3c35e6006e99dab47b192fb669d75c 100644 (file)
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.model.block.Block;
import ru.deadsoftware.cavedroid.game.objects.Drop;
import ru.deadsoftware.cavedroid.game.objects.DropController;
+import ru.deadsoftware.cavedroid.game.world.GameWorld;
+import javax.annotation.CheckForNull;
import javax.inject.Inject;
import java.util.Iterator;
@GameScope
-class GamePhysics {
+public class GamePhysics {
- static final int PL_SPEED = 2;
+ public static final float PL_JUMP_VELOCITY = -133.332f;
+ public static final float PL_TERMINAL_VELOCITY = 1254.4f;
- private final Vector2 gravity = new Vector2(0, .9f);
+ private final Vector2 gravity = new Vector2(0, 444.44f);
private final GameWorld mGameWorld;
private final MainConfig mMainConfig;
private final MobsController mMobsController;
private final DropController mDropController;
+ private final GameItemsHolder mGameItemsHolder;
@Inject
public GamePhysics(GameWorld gameWorld,
MainConfig mainConfig,
MobsController mobsController,
- DropController dropController) {
+ DropController dropController,
+ GameItemsHolder gameItemsHolder) {
mGameWorld = gameWorld;
mMainConfig = mainConfig;
mMobsController = mobsController;
mDropController = dropController;
+ mGameItemsHolder = gameItemsHolder;
}
/**
int dir = mob.looksLeft() ? 0 : 1;
int blX = (int) (mob.getX() + mob.getWidth() * dir - 8 + 16 * dir);
int blY = (int) (mob.getY() + mob.getHeight() - 8);
- int block = mGameWorld.getForeMap(blX / 16, blY / 16);
+ Block block = mGameWorld.getForeMap(blX / 16, blY / 16);
if (checkColl(new Rectangle(blX, mob.getY() - 18, mob.getWidth(), mob.getHeight()))) {
- block = 0;
+ return false;
}
- return (block > 0 && GameItems.getBlock(block).toJump() &&
- (mob.getY() + mob.getHeight()) - GameItems.getBlock(block).getRectangle(blX / 16, blY / 16).y > 8);
+ return (block.toJump() &&
+ (mob.getY() + mob.getHeight()) - block.getRectangle(blX / 16, blY / 16).y > 8);
}
private boolean checkColl(Rectangle rect) {
maxY = mGameWorld.getHeight();
}
- int block;
+ Block block;
for (int y = minY; y < maxY; y++) {
for (int x = minX; x < maxX; x++) {
+ if (!mGameWorld.hasForeAt(x, y)) {
+ continue;
+ }
block = mGameWorld.getForeMap(x, y);
- if (block > 0 && GameItems.getBlock(block).hasCollision()) {
- if (Intersector.overlaps(rect, GameItems.getBlock(block).getRectangle(x, y))) {
+ if (block.hasCollision()) {
+ if (Intersector.overlaps(rect, block.getRectangle(x, y))) {
return true;
}
}
return false;
}
- private int getBlock(Rectangle rect) {
+ private Block getBlock(Rectangle rect) {
return mGameWorld.getForeMap((int) (rect.x + rect.width / 2) / 16,
(int) (rect.y + rect.height / 8 * 7) / 16);
}
- private void dropPhy(Drop drop) {
- int dropToPlayer = drop.closeToPlayer(mGameWorld, mMobsController.getPlayer());
- if (dropToPlayer > 0) {
- drop.moveToPlayer(mGameWorld, mMobsController.getPlayer(), dropToPlayer);
+ private Rectangle getShiftedPlayerRect(float shift) {
+ final Player player = mMobsController.getPlayer();
+ return new Rectangle(player.x + shift, player.y, player.width, player.height);
+ }
+
+ /**
+ * @return Rectangle representing magneting target for this drop
+ */
+ @CheckForNull
+ private Rectangle getShiftedMagnetingPlayerRect(Drop drop) {
+ final Player player = mMobsController.getPlayer();
+
+ if (drop.canMagnetTo(player)) {
+ return getShiftedPlayerRect(0);
+ }
+
+ final Rectangle shiftedLeft = getShiftedPlayerRect(-mGameWorld.getWidthPx());
+ if (drop.canMagnetTo(shiftedLeft)) {
+ return shiftedLeft;
+ }
+
+ final Rectangle shiftedRight = getShiftedPlayerRect(mGameWorld.getWidthPx());
+ if (drop.canMagnetTo(shiftedRight)) {
+ return shiftedRight;
+ }
+
+ return null;
+ }
+
+ private void pickUpDropIfPossible(Rectangle shiftedPlayerTarget, Drop drop) {
+ final Player player = mMobsController.getPlayer();
+
+ if (Intersector.overlaps(shiftedPlayerTarget, drop)) {
+ player.pickUpDrop(drop);
+ }
+ }
+
+ private void dropPhy(Drop drop, float delta) {
+ final Rectangle playerMagnetTarget = getShiftedMagnetingPlayerRect(drop);
+ final Vector2 dropVelocity = drop.getVelocity();
+
+
+ if (playerMagnetTarget != null) {
+ final Vector2 magnetVector = new Vector2(playerMagnetTarget.x - drop.x,
+ playerMagnetTarget.y - drop.y);
+ magnetVector.nor().scl(Drop.MAGNET_VELOCITY * delta);
+ dropVelocity.add(magnetVector);
} else {
- if (drop.getMove().x >= .5f) {
- drop.getMove().x -= .5f;
- } else if (drop.getMove().x <= -.5f) {
- drop.getMove().x += .5f;
- } else {
- drop.getMove().x = 0;
- }
- if (drop.getMove().y < 9) {
- drop.getMove().y += gravity.y / 4;
- }
+ dropVelocity.y += gravity.y * delta;
}
- drop.move();
+ dropVelocity.x = MathUtils.clamp(dropVelocity.x, -Drop.MAGNET_VELOCITY, Drop.MAGNET_VELOCITY);
+ dropVelocity.y = MathUtils.clamp(dropVelocity.y, -Drop.MAGNET_VELOCITY, Drop.MAGNET_VELOCITY);
+
+ drop.x += dropVelocity.x * delta;
+ drop.y += dropVelocity.y * delta;
if (checkColl(drop)) {
- drop.getMove().set(0, -1);
+ dropVelocity.setZero();
do {
- drop.move();
+ drop.y--;
} while (checkColl(drop));
- drop.getMove().setZero();
+ }
+
+ if (playerMagnetTarget != null) {
+ pickUpDropIfPossible(playerMagnetTarget, drop);
}
}
int d = 0;
- if (mob.getMove().x < 0) {
+ if (mob.getVelocity().x < 0) {
d = 1;
- } else if (mob.getMove().x > 0) {
+ } else if (mob.getVelocity().x > 0) {
d = -1;
}
if (checkColl(mob)) {
int d = -1;
- if (mob.getMove().y < 0) {
+ if (mob.getVelocity().y < 0) {
d = 1;
}
if (d == -1) {
mob.setCanJump(true);
mob.setFlyMode(false);
+
+ int dmg = ((int)Math.max(0f, (((mob.getVelocity().y * mob.getVelocity().y) / (2 * gravity.y)) - 48f) / 16f));
+ if (dmg > 0) {
+ mob.damage(dmg);
+ }
}
mob.y = MathUtils.round(mob.getY());
mob.y += d;
}
- mob.getMove().y = 0;
+ mob.getVelocity().y = 0;
} else {
- mob.setCanJump(false);
+ mob.y += 1;
+ mob.setCanJump(checkColl(mob));
+ mob.y -= 1;
}
if (mob.getY() > mGameWorld.getHeightPx()) {
}
}
- private void playerPhy(Player player) {
- player.y += player.getMove().y;
- mobYColl(player);
-
+ private void playerPhy(Player player, float delta) {
if (player.isDead()) {
return;
}
- if (GameItems.isFluid(getBlock(player))) {
- if (mMainConfig.isTouch() && player.getMove().x != 0 && !player.swim && !player.isFlyMode()) {
+ if (getBlock(player).isFluid()) {
+ if (mMainConfig.isTouch() && player.getVelocity().x != 0 && !player.swim && !player.isFlyMode()) {
player.swim = true;
}
if (!player.swim) {
- if (!player.isFlyMode() && player.getMove().y < 4.5f) {
- player.getMove().add(gravity.x / 4, gravity.y / 4);
+ if (!player.isFlyMode() && player.getVelocity().y < 32f) {
+ player.getVelocity().y += gravity.y * delta;
}
- if (!player.isFlyMode() && player.getMove().y > 4.5f) {
- player.getMove().add(0, -1f);
+ if (!player.isFlyMode() && player.getVelocity().y > 32f) {
+ player.getVelocity().y -= player.getVelocity().y * 32f * delta;
}
} else {
- player.getMove().add(0, -.5f);
- if (player.getMove().y < -3) {
- player.getMove().y = -3;
+ player.getVelocity().y += PL_JUMP_VELOCITY * delta;
+ if (player.getVelocity().y < -player.getSpeed()) {
+ player.getVelocity().y = -player.getSpeed();
}
}
} else {
- if (!player.isFlyMode() && player.getMove().y < 18) {
- player.getMove().add(gravity);
+ if (!player.isFlyMode() && player.getVelocity().y < PL_TERMINAL_VELOCITY) {
+ player.getVelocity().y += gravity.y * delta;
}
}
- player.x += player.getMove().x * (player.isFlyMode() ? 1.5f : 1) *
- (GameItems.isFluid(getBlock(player)) && !player.isFlyMode() ? .8f : 1);
+ player.y += player.getVelocity().y * delta;
+ mobYColl(player);
+
+ player.x += player.getVelocity().x * (player.isFlyMode() ? 1.5f : 1) *
+ (getBlock(player).isFluid() && !player.isFlyMode() ? .8f : 1) * delta;
mobXColl(player);
- if (mMainConfig.isTouch() && !player.isFlyMode() && player.canJump() && player.getMove().x != 0 && checkJump(player)) {
- player.getMove().add(0, -8);
+ if (mMainConfig.isTouch() && !player.isFlyMode() && player.canJump() && player.getVelocity().x != 0 && checkJump(player)) {
+ player.jump();
player.setCanJump(false);
}
}
- private void mobPhy(Mob mob) {
- if (mob.getType() == Mob.Type.MOB && GameItems.isFluid(getBlock(mob))) {
- if (mob.getMove().y > 9) {
- mob.getMove().add(0, -.9f);
+ private void mobPhy(Mob mob, float delta) {
+ if (mob.getType() == Mob.Type.MOB && getBlock(mob).isFluid()) {
+ if (mob.getVelocity().y > 32f) {
+ mob.getVelocity().y -= mob.getVelocity().y * 32f * delta;
}
- mob.getMove().add(0, -.5f);
+ mob.getVelocity().y += PL_JUMP_VELOCITY * delta;
- if (mob.getMove().y < -3) {
- mob.getMove().y = -3;
+ if (mob.getVelocity().y < -mob.getSpeed()) {
+ mob.getVelocity().y = -mob.getSpeed();
}
- } else if (!mob.isFlyMode() && mob.getMove().y < 18) {
- mob.getMove().add(gravity);
+ } else if (!mob.isFlyMode() && mob.getVelocity().y < PL_TERMINAL_VELOCITY) {
+ mob.getVelocity().y += gravity.y * delta;
}
- mob.y += mob.getMove().y;
+ mob.y += mob.getVelocity().y * delta;
mobYColl(mob);
if (mob.isDead()) {
return;
}
- mob.x += mob.getMove().x;
+ mob.x += mob.getVelocity().x * delta;
mobXColl(mob);
- if (mob.canJump() && mob.getMove().x != 0 && checkJump(mob)) {
- mob.getMove().add(0, -8);
+ if (mob.canJump() && mob.getVelocity().x != 0 && checkJump(mob)) {
+ mob.jump();
mob.setCanJump(false);
}
}
- void update() {
+ void update(float delta) {
Player player = mMobsController.getPlayer();
for (Iterator<Drop> it = mDropController.getIterator(); it.hasNext(); ) {
Drop drop = it.next();
- dropPhy(drop);
- if (Intersector.overlaps(drop, player)) {
- drop.pickUpDrop(player);
- }
- if (drop.isPickedUp()) {
+ dropPhy(drop, delta);
+ if (drop.getPickedUp()) {
it.remove();
}
}
- for (Iterator<Mob> it = mMobsController.getIterator(); it.hasNext(); ) {
+ for (Iterator<Mob> it = mMobsController.getMobs().iterator(); it.hasNext(); ) {
Mob mob = it.next();
- mob.ai(mGameWorld);
- mobPhy(mob);
+ mob.ai(mGameWorld, mGameItemsHolder, delta);
+ mobPhy(mob, delta);
if (mob.isDead()) {
it.remove();
}
}
- playerPhy(player);
+ playerPhy(player, delta);
+ player.ai(mGameWorld, mGameItemsHolder, delta);
if (player.isDead()) {
- player.respawn(mGameWorld);
+ player.respawn(mGameWorld, mGameItemsHolder);
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameProc.java b/core/src/ru/deadsoftware/cavedroid/game/GameProc.java
index 44a46878cff367a12cc83ed0b8b8619be6b16c54..94cd34ba9729bac41f22b0c6df010b5b9ac0c3f9 100644 (file)
package ru.deadsoftware.cavedroid.game;
+import com.badlogic.gdx.Gdx;
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.world.GameWorldBlocksLogicControllerTask;
+import ru.deadsoftware.cavedroid.game.world.GameWorldFluidsLogicControllerTask;
+import ru.deadsoftware.cavedroid.game.world.GameWorldMobDamageControllerTask;
import javax.inject.Inject;
@GameScope
public class GameProc implements Disposable {
- public static final int MAX_CREATIVE_SCROLL = GameItems.getItemsSize() / 8;
-
- private final GameWorld mGameWorld;
private final GamePhysics mGamePhysics;
- private final GameInput mGameInput;
private final GameRenderer mGameRenderer;
+ private final MobsController mMobsController;
+ private final GameWorldFluidsLogicControllerTask mGameWorldFluidsLogicControllerTask;
+ private final GameWorldBlocksLogicControllerTask mGameWorldBlocksLogicControllerTask;
+ private final GameWorldMobDamageControllerTask mGameWorldMobDamageControllerTask;
+
+ private final Timer mWorldLogicTimer = new Timer();
@Inject
- public GameProc(GameWorld gameWorld,
+ public GameProc(MainConfig mainConfig,
GamePhysics gamePhysics,
- GameInput gameInput,
- GameRenderer gameRenderer) {
- mGameWorld = gameWorld;
+ GameRenderer gameRenderer,
+ MobsController mobsController,
+ GameWorldFluidsLogicControllerTask gameWorldFluidsLogicControllerTask,
+ GameWorldBlocksLogicControllerTask gameWorldBlocksLogicControllerTask,
+ GameWorldMobDamageControllerTask gameWorldMobDamageControllerTask
+ ) {
mGamePhysics = gamePhysics;
- mGameInput = gameInput;
mGameRenderer = gameRenderer;
+ mMobsController = mobsController;
+ mGameWorldFluidsLogicControllerTask = gameWorldFluidsLogicControllerTask;
+ mGameWorldBlocksLogicControllerTask = gameWorldBlocksLogicControllerTask;
+ mGameWorldMobDamageControllerTask = gameWorldMobDamageControllerTask;
- mGameWorld.startFluidsThread();
+ mobsController.getPlayer().controlMode = mainConfig.isTouch() ? Player.ControlMode.WALK : Player.ControlMode.CURSOR;
+
+ mWorldLogicTimer.scheduleTask(gameWorldFluidsLogicControllerTask, 0,
+ GameWorldFluidsLogicControllerTask.FLUID_UPDATE_INTERVAL_SEC);
+ mWorldLogicTimer.scheduleTask(gameWorldBlocksLogicControllerTask, 0,
+ GameWorldBlocksLogicControllerTask.WORLD_BLOCKS_LOGIC_UPDATE_INTERVAL_SEC);
+ mWorldLogicTimer.scheduleTask(gameWorldMobDamageControllerTask, 0,
+ GameWorldMobDamageControllerTask.ENVIRONMENTAL_MOB_DAMAGE_INTERVAL_SEC);
+ }
+
+ public void setPlayerGameMode(int gameMode) {
+ mMobsController.getPlayer().gameMode = gameMode;
}
public void update(float delta) {
- mGamePhysics.update();
- mGameInput.update();
- mGameWorld.update();
+ mGamePhysics.update(delta);
mGameRenderer.render(delta);
}
+ public void show() {
+ Gdx.input.setInputProcessor(mGameRenderer);
+ }
+
@Override
public void dispose() {
- mGameWorld.dispose();
+ mWorldLogicTimer.stop();
+ mGameWorldFluidsLogicControllerTask.cancel();
+ mGameWorldBlocksLogicControllerTask.cancel();
+ mGameWorldMobDamageControllerTask.cancel();
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java b/core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java
index 19f2681b0df65020285bcdb4572076bc089fb969..e96f3af48e3936fb31ccb99ecfac36a8b67d6774 100644 (file)
package ru.deadsoftware.cavedroid.game;
import com.badlogic.gdx.Gdx;
-import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
-import com.badlogic.gdx.graphics.g2d.TextureRegion;
-import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
+import com.badlogic.gdx.utils.ObjectMap;
import ru.deadsoftware.cavedroid.MainConfig;
-import ru.deadsoftware.cavedroid.game.mobs.Mob;
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler;
+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;
+import ru.deadsoftware.cavedroid.game.input.handler.mouse.CursorMouseInputHandler;
+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.objects.Drop;
-import ru.deadsoftware.cavedroid.game.objects.DropController;
-import ru.deadsoftware.cavedroid.misc.ControlMode;
+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.misc.Assets;
import ru.deadsoftware.cavedroid.misc.Renderer;
+import javax.annotation.CheckForNull;
import javax.inject.Inject;
-
-import static ru.deadsoftware.cavedroid.misc.Assets.guiMap;
-import static ru.deadsoftware.cavedroid.misc.Assets.textureRegions;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
@GameScope
public class GameRenderer extends Renderer {
+ private static final TouchButton nullButton = new TouchButton(null, -1, true);
+
private final MainConfig mMainConfig;
- private final GameInput mGameInput;
- private final GameWorld mGameWorld;
private final MobsController mMobsController;
- private final DropController mDropController;
+ 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 GameWindowsManager mGameWindowsManager;
@Inject
GameRenderer(MainConfig mainConfig,
- GameInput gameInput,
- GameWorld gameWorld,
MobsController mobsController,
- DropController dropController) {
+ Set<IGameRenderer> renderers,
+ CursorMouseInputHandler cursorMouseInputHandler,
+ MouseInputActionMapper mouseInputActionMapper,
+ KeyboardInputActionMapper keyboardInputActionMapper,
+ Set<IGameInputHandler<MouseInputAction>> mouseInputHandlers,
+ Set<IGameInputHandler<KeyboardInputAction>> keyboardInputHandlers,
+ GameWindowsManager gameWindowsManager) {
super(mainConfig.getWidth(), mainConfig.getHeight());
mMainConfig = mainConfig;
- mGameInput = gameInput;
- mGameWorld = gameWorld;
mMobsController = mobsController;
- mDropController = dropController;
+ mRenderers = new ArrayList<>(renderers);
+ mRenderers.sort(Comparator.comparingInt(IGameRenderer::getRenderLayer));
+ mCursorMouseInputHandler = cursorMouseInputHandler;
+ mMouseInputActionMapper = mouseInputActionMapper;
+ mKeyboardInputActionMapper = keyboardInputActionMapper;
+ mMouseInputHandlers = mouseInputHandlers;
+ mKeyboardInputHandlers = keyboardInputHandlers;
+ mGameWindowsManager = gameWindowsManager;
Gdx.gl.glClearColor(0f, .6f, .6f, 1f);
}
- private float drawX(int x) {
- return x * 16 - getCamX();
+ private float mTouchDownX, mTouchDownY;
+
+ private void updateCameraPosition() {
+ Player player = mMobsController.getPlayer();
+ setCamPos(player.getX() + player.getWidth() / 2 - getWidth() / 2,
+ player.getY() + player.getHeight() / 2 - getHeight() / 2);
}
- private float drawY(int y) {
- return y * 16 - getCamY();
+ private float transformScreenX(int screenX) {
+ return getWidth() / Gdx.graphics.getWidth() * screenX;
}
- private void drawWreck(int bl) {
- if (mGameInput.getBlockDamage() > 0) {
- int index = 10 * mGameInput.getBlockDamage() / GameItems.getBlock(bl).getHp();
- String key = "break_" + index;
- spriter.draw(textureRegions.get(key), mGameInput.getCurX() * 16 - getCamX(),
- mGameInput.getCurY() * 16 - getCamY());
- }
+ private float transformScreenY(int screenY) {
+ return getHeight() / Gdx.graphics.getHeight() * screenY;
}
- private void drawBlock(int x, int y, boolean drawBG) {
- if (drawBG) {
- if ((!mGameWorld.hasForeAt(x, y) || mGameWorld.getForeMapBlock(x, y).isTransparent())
- && mGameWorld.hasBackAt(x, y)) {
- mGameWorld.getBackMapBlock(x, y).draw(spriter, drawX(x), drawY(y));
- if (!mGameWorld.hasForeAt(x, y) && x == mGameInput.getCurX() && y == mGameInput.getCurY()) {
- drawWreck(mGameWorld.getBackMap(mGameInput.getCurX(), mGameInput.getCurY()));
- }
- }
- }
- if (mGameWorld.hasForeAt(x, y) && mGameWorld.getForeMapBlock(x, y).isBackground() == drawBG) {
- mGameWorld.getForeMapBlock(x, y).draw(spriter, drawX(x), drawY(y));
- if (x == mGameInput.getCurX() && y == mGameInput.getCurY()) {
- drawWreck(mGameWorld.getForeMap(mGameInput.getCurX(), mGameInput.getCurY()));
- }
- }
+ private void handleMousePosition() {
+ final Rectangle viewport = getCameraViewport();
+ final MouseInputAction action = new MouseInputAction(
+ Gdx.input.getX() * (viewport.width / Gdx.graphics.getWidth()),
+ Gdx.input.getY() * (viewport.height / Gdx.graphics.getHeight()),
+ MouseInputActionKey.None.INSTANCE,
+ viewport);
+
+ mCursorMouseInputHandler.handle(action);
}
- private void drawWorld(boolean bg) {
- int minX = (int) (getCamX() / 16) - 1;
- int minY = (int) (getCamY() / 16) - 1;
- int maxX = (int) ((getCamX() + getWidth()) / 16) + 1;
- int maxY = (int) ((getCamY() + getHeight()) / 16) + 1;
- if (minY < 0) {
- minY = 0;
- }
- if (maxY > mGameWorld.getHeight()) {
- maxY = mGameWorld.getHeight();
- }
- for (int y = minY; y < maxY; y++) {
- for (int x = minX; x < maxX; x++) {
- drawBlock(x, y, bg);
- }
- }
- if (bg) {
- spriter.end();
- Gdx.gl.glEnable(GL20.GL_BLEND);
- Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
- shaper.begin(ShapeRenderer.ShapeType.Filled);
- shaper.setColor(0f, 0f, 0f, .5f);
- for (int y = minY; y < maxY; y++) {
- for (int x = minX; x < maxX; x++) {
- if ((!mGameWorld.hasForeAt(x, y) || mGameWorld.getForeMapBlock(x, y).isTransparent())
- && mGameWorld.hasBackAt(x, y)) {
- shaper.rect(drawX(x), drawY(y), 16, 16);
- }
- }
- }
- shaper.end();
- Gdx.gl.glDisable(GL20.GL_BLEND);
- spriter.begin();
+ private boolean handleMouseAction(@CheckForNull MouseInputAction action) {
+ if (action == null) {
+ return false;
}
- }
- private void drawMob(Mob mob) {
- float mobDrawX = mob.getX() - getCamX();
- float mobDrawY = mob.getY() - getCamY();
+ boolean anyProcessed = false;
- if (mobDrawX + mob.getWidth() < 0 && mobDrawX + mGameWorld.getWidthPx() > 0) {
- mobDrawX += mGameWorld.getWidthPx();
- } else if (mobDrawX > getWidth() && mobDrawX + mob.getWidth() - mGameWorld.getWidthPx() > 0) {
- mobDrawX -= mGameWorld.getWidthPx();
- } else if (mobDrawX + mob.getWidth() < 0 && mobDrawX > getWidth()) {
- return;
+ for (IGameInputHandler<MouseInputAction> handler : mMouseInputHandlers) {
+ final boolean conditions = handler.checkConditions(action);
+ if (conditions) {
+ anyProcessed = true;
+ handler.handle(action);
+ break;
+ }
+// anyProcessed = anyProcessed || conditions;
}
-
- mob.draw(spriter, mobDrawX, mobDrawY);
+ return anyProcessed;
}
- private void drawDrop(Drop drop) {
+ private boolean onMouseActionEvent(int mouseX, int mouseY, int button, boolean touchUp) {
+ @CheckForNull MouseInputAction action = mMouseInputActionMapper
+ .map((float) mouseX, (float) mouseY, getCameraViewport(), button, touchUp);
+ return handleMouseAction(action);
}
- @SuppressWarnings("IntegerDivisionInFloatingPointContext")
- private void drawCreative() {
- TextureRegion creative = textureRegions.get("creative");
- float x = getWidth() / 2 - (float) creative.getRegionWidth() / 2;
- float y = getHeight() / 2 - (float) creative.getRegionHeight() / 2;
- spriter.draw(creative, x, y);
- spriter.draw(textureRegions.get("handle"), x + 156,
- y + 18 + (mGameInput.getCreativeScroll() * (72f / GameProc.MAX_CREATIVE_SCROLL)));
- for (int i = mGameInput.getCreativeScroll() * 8; i < mGameInput.getCreativeScroll() * 8 + 40; i++) {
- if (i > 0 && i < GameItems.getItemsSize()) {
- if (GameItems.getItem(i).isBlock()) {
- spriter.draw(GameItems.getBlock(GameItems.getBlockIdByItemId(i)).getTexture(),
- x + 8 + ((i - mGameInput.getCreativeScroll() * 8) % 8) * 18,
- y + 18 + ((i - mGameInput.getCreativeScroll() * 8) / 8) * 18);
- } else {
- spriter.draw(GameItems.getItem(i).getTexture(),
- x + 8 + ((i - mGameInput.getCreativeScroll() * 8) % 8) * 18,
- y + 18 + ((i - mGameInput.getCreativeScroll() * 8) / 8) * 18);
- }
- }
- }
- for (int i = 0; i < 9; i++) {
- if (mMobsController.getPlayer().inventory[i] > 0) {
- if (GameItems.getItem(mMobsController.getPlayer().inventory[i]).isBlock()) {
- spriter.draw(GameItems.getBlock(GameItems.getBlockIdByItemId(mMobsController.getPlayer().inventory[i])).getTexture(),
- x + 8 + i * 18, y + creative.getRegionHeight() - 24);
- } else {
- spriter.draw(GameItems.getItem(mMobsController.getPlayer().inventory[i]).getTexture(),
- x + 8 + i * 18, y + creative.getRegionHeight() - 24);
- }
+ @Override
+ public boolean touchUp(int screenX, int screenY, int pointer, int button) {
+ float touchX = transformScreenX(screenX);
+ float touchY = transformScreenY(screenY);
+
+ if (mMainConfig.isTouch()) {
+ TouchButton touchedKey = getTouchedKey(touchX, touchY);
+ if (touchedKey.isMouse()) {
+ return onMouseActionEvent(screenX, screenY, touchedKey.getCode(), true);
+ } else {
+ return keyUp(touchedKey.getCode());
}
}
+ return onMouseActionEvent(screenX, screenY, button, true);
}
- private void drawGUI() {
- TextureRegion cursor = textureRegions.get("cursor");
- TextureRegion hotbar = textureRegions.get("hotbar");
- TextureRegion hotbarSelector = textureRegions.get("hotbar_selector");
-
- if (mGameWorld.hasForeAt(mGameInput.getCurX(), mGameInput.getCurY()) ||
- mGameWorld.hasBackAt(mGameInput.getCurX(), mGameInput.getCurY()) ||
- mGameInput.getControlMode() == ControlMode.CURSOR || mMainConfig.isTouch()) {
- spriter.draw(cursor, mGameInput.getCurX() * 16 - getCamX(), mGameInput.getCurY() * 16 - getCamY());
+ private TouchButton getTouchedKey(float touchX, float touchY) {
+ if (mGameWindowsManager.getCurrentWindowType() != GameUiWindow.NONE) {
+ return nullButton;
}
- spriter.draw(hotbar, getWidth() / 2 - (float) hotbar.getRegionWidth() / 2, 0);
- for (int i = 0; i < 9; i++) {
- if (mMobsController.getPlayer().inventory[i] > 0) {
- if (GameItems.getItem(mMobsController.getPlayer().inventory[i]).isBlock()) {
- spriter.draw(GameItems.getBlock(GameItems.getBlockIdByItemId(mMobsController.getPlayer().inventory[i])).getTexture(),
- getWidth() / 2 - (float) hotbar.getRegionWidth() / 2 + 3 + i * 20,
- 3);
- } else {
- spriter.draw(GameItems.getItem(mMobsController.getPlayer().inventory[i]).getTexture(),
- getWidth() / 2 - (float) hotbar.getRegionWidth() / 2 + 3 + i * 20,
- 3);
- }
+ for (ObjectMap.Entry<String, TouchButton> entry : Assets.guiMap) {
+ TouchButton button = entry.value;
+ if (button.getRect().contains(touchX, touchY)) {
+ return button;
}
}
- spriter.draw(hotbarSelector,
- getWidth() / 2 - (float) hotbar.getRegionWidth() / 2 - 1 + 20 * mMobsController.getPlayer().slot,
- -1);
+ return nullButton;
}
- private void drawTouchGui() {
- for (int i = 0; i < guiMap.size; i++) {
- Rectangle touchKey = guiMap.getValueAt(i).getRect();
- spriter.draw(textureRegions.get(guiMap.getKeyAt(i)),
- touchKey.x, touchKey.y, touchKey.width, touchKey.height);
- }
- if (mGameInput.getControlMode() == ControlMode.CURSOR) {
- spriter.draw(textureRegions.get("shade"), 83, getHeight() - 21);
+ @Override
+ public boolean touchDown(int screenX, int screenY, int pointer, int button) {
+ float touchX = transformScreenX(screenX);
+ float touchY = transformScreenY(screenY);
+
+ mTouchDownX = touchX;
+ mTouchDownY = touchY;
+
+ if (mMainConfig.isTouch()) {
+ TouchButton touchedKey = getTouchedKey(touchX, touchY);
+ if (touchedKey.isMouse()) {
+ return onMouseActionEvent(screenX, screenY, touchedKey.getCode(), false);
+ } else {
+ return keyDown(touchedKey.getCode());
+ }
}
+
+ return onMouseActionEvent(screenX, screenY, button, false);
}
- private void drawGamePlay() {
- Player player = mMobsController.getPlayer();
+ @Override
+ public boolean touchDragged(int screenX, int screenY, int pointer) {
+ float touchX = transformScreenX(screenX);
+ float touchY = transformScreenY(screenY);
- drawWorld(true);
- player.draw(spriter, player.getX() - getCamX() - player.getWidth() / 2, player.getY() - getCamY());
- mMobsController.forEach(this::drawMob);
- mDropController.forEach(this::drawDrop);
- drawWorld(false);
- drawGUI();
- }
+ if (Math.abs(touchX - mTouchDownX) < 16 && Math.abs(touchY - mTouchDownY) < 16) {
+ return false;
+ }
- private void updateCameraPosition() {
- Player player = mMobsController.getPlayer();
- setCamPos(player.getX() + player.getWidth() / 2 - getWidth() / 2,
- player.getY() + player.getHeight() / 2 - getHeight() / 2);
+ @CheckForNull MouseInputAction action =
+ mMouseInputActionMapper.mapDragged(screenX, screenY, getCameraViewport());
+ return handleMouseAction(action);
}
@Override
- public void render(float delta) {
- int fps = (int) (1 / delta);
- updateCameraPosition();
- mGameInput.moveCursor(this);
+ public boolean scrolled(float amountX, float amountY) {
+ @CheckForNull MouseInputAction action = mMouseInputActionMapper
+ .mapScrolled(Gdx.input.getX(), Gdx.input.getY(), amountX, amountY, getCameraViewport());
+ return handleMouseAction(action);
+ }
- Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
+ private boolean handleKeyboardAction(int keycode, boolean isKeyDown) {
+ @CheckForNull final KeyboardInputAction action = mKeyboardInputActionMapper
+ .map(keycode, isKeyDown);
- spriter.begin();
+ if (action == null) {
+ return false;
+ }
- drawGamePlay();
+ boolean anyProcessed = false;
- switch (mMainConfig.getGameUiWindow()) {
- case CREATIVE_INVENTORY:
- drawCreative();
+ for (IGameInputHandler<KeyboardInputAction> handler : mKeyboardInputHandlers) {
+ final boolean conditions = handler.checkConditions(action);
+ if (conditions) {
+ anyProcessed = true;
+ handler.handle(action);
break;
- //TODO draw other ui windows
+ }
}
+ return anyProcessed;
+ }
- if (mMainConfig.isTouch()) {
- drawTouchGui();
- }
+ @Override
+ public boolean keyDown(int keycode) {
+ return handleKeyboardAction(keycode, true);
+ }
- spriter.end();
+ @Override
+ public boolean keyUp(int keycode) {
+ return handleKeyboardAction(keycode, false);
+ }
- if (mMainConfig.isShowMap()) {
- //DRAW MAP
- shaper.begin(ShapeRenderer.ShapeType.Filled);
- shaper.setColor(Color.LIGHT_GRAY);
- shaper.rect(0, 0, mGameWorld.getWidth(), 128);
- for (int y = 128; y < 256; y++) {
- for (int x = 0; x < getWidth(); x++) {
- if (mGameWorld.hasForeAt(x, y) || mGameWorld.hasBackAt(x, y)) {
- if (GameItems.isWater(mGameWorld.getForeMap(x, y))) {
- shaper.setColor(Color.BLUE);
- } else if (GameItems.isLava(mGameWorld.getForeMap(x, y))) {
- shaper.setColor(Color.RED);
- } else {
- if (mGameWorld.hasForeAt(x, y)) {
- shaper.setColor(Color.BLACK);
- } else {
- shaper.setColor(Color.DARK_GRAY);
- }
- }
- shaper.rect(x, y - 128, 1, 1);
- }
- }
- }
- shaper.setColor(Color.OLIVE);
- shaper.rect(mMobsController.getPlayer().getMapX(), mMobsController.getPlayer().getUpperMapY() - 128, 1, 2);
- shaper.end();
- //=================
- }
+ @Override
+ public void render(float delta) {
+ updateCameraPosition();
+ handleMousePosition();
+// mGameInput.moveCursor(this);
- if (mMainConfig.isShowInfo()) {
- spriter.begin();
- Player player = mMobsController.getPlayer();
- drawString("FPS: " + fps, 0, 0);
- drawString("X: " + player.getMapX(), 0, 10);
- drawString("Y: " + player.getUpperMapY(), 0, 20);
- drawString("CurX: " + mGameInput.getCurX(), 0, 30);
- drawString("CurY: " + mGameInput.getCurY(), 0, 40);
- drawString("Mobs: " + mMobsController.getSize(), 0, 50);
- drawString("Drops: " + mDropController.getSize(), 0, 60);
- drawString("Block: " + GameItems.getBlockKey(mGameWorld.getForeMap(mGameInput.getCurX(), mGameInput.getCurY())), 0, 70);
- drawString("Hand: " + GameItems.getItemKey(mMobsController.getPlayer().inventory[mMobsController.getPlayer().slot]), 0, 80);
- drawString("Game mode: " + player.gameMode, 0, 90);
- spriter.end();
- }
+ Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
+ spriter.begin();
+ mRenderers.forEach(iGameRenderer -> iGameRenderer.draw(spriter, shaper, getCameraViewport(), delta));
+ spriter.end();
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java b/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java
index 1ff2bad9783788a68d6ff1858dc9185f2a9a6fbe..cb52b3c76b126812e355c43d9ce076c7d5749140 100644 (file)
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 int[][] mForeMap, mBackMap;
+ private Block[][] mForeMap, mBackMap;
- public Data(MobsController mobsController, DropController dropController, int[][] foreMap, int[][] backMap) {
+ public Data(MobsController mobsController, DropController dropController, Block[][] foreMap, Block[][] backMap) {
mMobsController = mobsController;
mDropController = dropController;
mForeMap = foreMap;
return dropController;
}
- public int[][] retrieveForeMap() {
+ public Block[][] retrieveForeMap() {
assert mForeMap != null;
- int[][] foreMap = mForeMap;
+ Block[][] foreMap = mForeMap;
mForeMap = null;
return foreMap;
}
- public int[][] retrieveBackMap() {
+ public Block[][] retrieveBackMap() {
assert mBackMap != null;
- int[][] backMap = mBackMap;
+ Block[][] backMap = mBackMap;
mBackMap = null;
return backMap;
}
return ByteBuffer.allocate(4).putInt(i).array();
}
- private static void saveMap(FileHandle file, int[][] map) throws IOException {
+ 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(intToBytes(SAVE_VERSION));
+ out.write(SAVE_VERSION);
out.write(intToBytes(width));
out.write(intToBytes(height));
for (int y = 0; y < height; y++) {
- block = map[0][y];
+ block = dict.get(map[0][y].getParams().getKey());
run = 0;
- for (int[] ints : map) {
- if (ints[y] != block) {
- out.write(intToBytes(run));
- out.write(intToBytes(block));
+ 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 = ints[y];
+ block = dict.get(blocks[y].getParams().getKey());
}
run++;
}
- out.write(intToBytes(run));
- out.write(intToBytes(block));
+ out.write(run);
+ out.write(block);
}
out.flush();
out.close();
}
- private static int[][] loadMap(FileHandle file) throws Exception {
- int[][] map;
+ 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.readInt();
+ version = in.readByte();
if (SAVE_VERSION == version) {
width = in.readInt();
height = in.readInt();
- map = new int[width][height];
+ map = new Block[width][height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x += run) {
- run = in.readInt();
- block = in.readInt();
+ run = in.readUnsignedByte();
+ block = in.readUnsignedByte();
for (int i = x; i < x + run; i++) {
- map[i][y] = block;
+ map[i][y] = gameItemsHolder.getBlock(dict[block]);
}
}
}
}
@CheckForNull
- public static Data load(MainConfig mainConfig) {
+ public static Data load(MainConfig mainConfig, GameItemsHolder gameItemsHolder) {
String folder = mainConfig.getGameFolder();
FileHandle file = Gdx.files.absolute(folder + "/saves/game.sav");
in.close();
- int[][] foreMap = loadMap(Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/foremap.sav"));
- int[][] backMap = loadMap(Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/backmap.sav"));
+ 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");
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();
- saveMap(Gdx.files.absolute(folder + "/saves/foremap.sav"), gameWorld.getFullForeMap());
- saveMap(Gdx.files.absolute(folder + "/saves/backmap.sav"), gameWorld.getFullBackMap());
+
+ 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();
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameScreen.java b/core/src/ru/deadsoftware/cavedroid/game/GameScreen.java
index 370d8bd79066baf1db5c385f4d89556b53c6f740..749d7d4055beeee9964e8f52cb7466188b093a1e 100644 (file)
@CheckForNull
private GameProc mGameProc;
@CheckForNull
- private GameInputProcessor mGameInputProcessor;
+ private GameItemsHolder mGameItemsHolder;
@Inject
public GameScreen(MainConfig mainConfig) {
mMainConfig = mainConfig;
}
- public void newGame() {
+ public void newGame(int gameMode) {
if (mGameProc != null) {
mGameProc.dispose();
}
+ GameModule.loaded = true;
+
GameComponent gameComponent = DaggerGameComponent.builder()
.mainComponent(mMainConfig.getMainComponent()).build();
mGameProc = gameComponent.getGameProc();
- mGameInputProcessor = gameComponent.getGameInputProcessor();
- Gdx.input.setInputProcessor(gameComponent.getGameInputProcessor());
+ mGameProc.setPlayerGameMode(gameMode);
}
public void loadGame() {
mGameProc.dispose();
}
- GameModule.load(mMainConfig);
+ GameModule.loaded = false;
GameComponent gameComponent = DaggerGameComponent.builder()
.mainComponent(mMainConfig.getMainComponent()).build();
mGameProc = gameComponent.getGameProc();
- mGameInputProcessor = gameComponent.getGameInputProcessor();
-
- Gdx.input.setInputProcessor(gameComponent.getGameInputProcessor());
}
@Override
@Override
public void show() {
- Gdx.input.setInputProcessor(mGameInputProcessor);
+// Gdx.input.setInputProcessor(mGameInputProcessor);
+ mGameProc.show();
}
@Override
@Override
public void dispose() {
+ if (mGameProc != null) {
+ mGameProc.dispose();
+ }
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameWorld.java b/core/src/ru/deadsoftware/cavedroid/game/GameWorld.java
+++ /dev/null
@@ -1,260 +0,0 @@
-package ru.deadsoftware.cavedroid.game;
-
-import com.badlogic.gdx.utils.Disposable;
-import com.badlogic.gdx.utils.TimeUtils;
-import kotlin.Pair;
-import ru.deadsoftware.cavedroid.game.mobs.FallingGravel;
-import ru.deadsoftware.cavedroid.game.mobs.FallingSand;
-import ru.deadsoftware.cavedroid.game.mobs.MobsController;
-import ru.deadsoftware.cavedroid.game.objects.Block;
-import ru.deadsoftware.cavedroid.game.objects.DropController;
-
-import javax.annotation.CheckForNull;
-import javax.inject.Inject;
-
-@GameScope
-public class GameWorld implements Disposable {
-
- private static final int DEFAULT_WIDTH = 1024;
- private static final int DEFAULT_HEIGHT = 256;
- private static final int UPDATE_RANGE = 16;
-
- private final DropController mDropController;
- private final MobsController mMobsController;
- private final GameFluidsThread mGameFluidsThread;
-
- private final int mWidth;
- private final int mHeight;
- private final int[][] mForeMap;
- private final int[][] mBackMap;
-
- private boolean mShouldUpdate;
- private int mUpdateX;
- private int mUpdateY;
-
- @Inject
- public GameWorld(DropController dropController,
- MobsController mobsController,
- @CheckForNull int[][] foreMap,
- @CheckForNull int[][] backMap) {
- mDropController = dropController;
- mMobsController = mobsController;
-
- boolean isNewGame = foreMap == null || backMap == null;
-
- if (isNewGame) {
- mWidth = DEFAULT_WIDTH;
- mHeight = DEFAULT_HEIGHT;
- Pair<int[][], int[][]> maps = GameWorldGeneratorKt.generate(mWidth, mHeight, TimeUtils.millis());
- mForeMap = maps.getFirst();
- mBackMap = maps.getSecond();
- mMobsController.getPlayer().respawn(this);
- } else {
- mForeMap = foreMap;
- mBackMap = backMap;
- mWidth = mForeMap.length;
- mHeight = mForeMap[0].length;
- }
-
- mGameFluidsThread = new GameFluidsThread(this, mMobsController, Thread.currentThread());
- }
-
- public int getWidth() {
- return mWidth;
- }
-
- public int getHeight() {
- return mHeight;
- }
-
- public float getWidthPx() {
- return mWidth * 16f;
- }
-
- public float getHeightPx() {
- return mHeight * 16f;
- }
-
- int[][] getFullForeMap() {
- return mForeMap;
- }
-
- int[][] getFullBackMap() {
- return mBackMap;
- }
-
- private int transformX(int x) {
- x = x % getWidth();
- if (x < 0) {
- x = getWidth() - Math.abs(x);
- }
- return x;
- }
-
- private int getMap(int x, int y, int layer) {
- int map = 0;
- try {
- x = transformX(x);
- map = (layer == 0) ? mForeMap[x][y] : mBackMap[x][y];
- } catch (ArrayIndexOutOfBoundsException ignored) {
- }
- return map;
- }
-
- private void setMap(int x, int y, int layer, int value) {
- try {
- x = transformX(x);
- if (layer == 0) {
- mForeMap[x][y] = value;
- } else {
- mBackMap[x][y] = value;
- }
- } catch (ArrayIndexOutOfBoundsException ignored) {
- }
- }
-
- public boolean hasForeAt(int x, int y) {
- return getMap(x, y, 0) != 0;
- }
-
- public boolean hasBackAt(int x, int y) {
- return getMap(x, y, 1) != 0;
- }
-
- public int getForeMap(int x, int y) {
- return getMap(x, y, 0);
- }
-
- public Block getForeMapBlock(int x, int y) {
- return GameItems.getBlock(getMap(x, y, 0));
- }
-
- public void setForeMap(int x, int y, int id) {
- setMap(x, y, 0, id);
- }
-
- public int getBackMap(int x, int y) {
- return getMap(x, y, 1);
- }
-
- public Block getBackMapBlock(int x, int y) {
- return GameItems.getBlock(getMap(x, y, 1));
- }
-
- public void setBackMap(int x, int y, int id) {
- setMap(x, y, 1, id);
- }
-
- private void placeSlab(int x, int y, int value) {
- switch (value) {
- case 51:
- setForeMap(x, y, 52);
- break;
- case 53:
- setForeMap(x, y, 21);
- break;
- case 54:
- setForeMap(x, y, 5);
- break;
- case 55:
- setForeMap(x, y, 4);
- break;
- case 56:
- setForeMap(x, y, 28);
- break;
- case 58:
- setForeMap(x, y, 57);
- break;
- }
- }
-
- public void placeToForeground(int x, int y, int value) {
- if (!hasForeAt(x, y) || value == 0 || !GameItems.getBlock(getForeMap(x, y)).hasCollision()) {
- setForeMap(x, y, value);
- } else if (GameItems.isSlab(value) && getForeMap(x, y) == value) {
- placeSlab(x, y, value);
- }
- mUpdateX = x - 8;
- mUpdateY = y - 8;
- mShouldUpdate = true;
- }
-
- public void placeToBackground(int x, int y, int value) {
- if (value == 0 || (getBackMap(x, y) == 0 && GameItems.getBlock(value).hasCollision()) &&
- (!GameItems.getBlock(value).isTransparent() || value == 18)) {
- setBackMap(x, y, value);
- }
- }
-
- public void destroyForeMap(int x, int y) {
- Block block = GameItems.getBlock(getForeMap(x, y));
- if (block.hasDrop()) {
- mDropController.addDrop(transformX(x) * 16 + 4, y * 16 + 4, GameItems.getItemId(block.getDrop()));
- }
- placeToForeground(x, y, 0);
- }
-
- public void destroyBackMap(int x, int y) {
- Block block = GameItems.getBlock(getBackMap(x, y));
- if (block.hasDrop()) {
- mDropController.addDrop(transformX(x) * 16 + 4, y * 16 + 4, GameItems.getItemId(block.getDrop()));
- }
- placeToBackground(x, y, 0);
- }
-
- private void updateBlock(int x, int y) {
- if (getForeMap(x, y) == 10) {
- if (!hasForeAt(x, y + 1) || !getForeMapBlock(x, y + 1).hasCollision()) {
- setForeMap(x, y, 0);
- mMobsController.addMob(FallingSand.class, x * 16, y * 16);
- updateBlock(x, y - 1);
- }
- }
-
- if (getForeMap(x, y) == 11) {
- if (!hasForeAt(x, y + 1) || !getForeMapBlock(x, y + 1).hasCollision()) {
- setForeMap(x, y, 0);
- mMobsController.addMob(FallingGravel.class, x * 16, y * 16);
- updateBlock(x, y - 1);
- }
- }
-
- if (hasForeAt(x, y) && getForeMapBlock(x, y).requiresBlock()) {
- if (!hasForeAt(x, y + 1) || !getForeMapBlock(x, y + 1).hasCollision()) {
- destroyForeMap(x, y);
- updateBlock(x, y - 1);
- }
- }
-
- if (getForeMap(x, y) == 2) {
- if (hasForeAt(x, y - 1) && (getForeMapBlock(x, y - 1).hasCollision() ||
- GameItems.isFluid(getForeMap(x, y - 1)))) {
- setForeMap(x, y, 3);
- }
- }
- }
-
- public void update() {
- if (mShouldUpdate) {
- for (int y = mUpdateY; y < mUpdateY + UPDATE_RANGE; y++) {
- for (int x = mUpdateX; x < mUpdateX + UPDATE_RANGE; x++) {
- updateBlock(x, y);
- }
- }
- mShouldUpdate = false;
- }
-
- if (!mGameFluidsThread.isAlive()) {
- mGameFluidsThread.start();
- }
- }
-
- public void startFluidsThread() {
- mGameFluidsThread.start();
- }
-
- @Override
- public void dispose() {
- mGameFluidsThread.interrupt();
- }
-}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameWorldGenerator.kt b/core/src/ru/deadsoftware/cavedroid/game/GameWorldGenerator.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package ru.deadsoftware.cavedroid.game
-
-import com.badlogic.gdx.utils.TimeUtils
-import kotlin.math.abs
-import kotlin.random.Random
-
-private fun generateHeights(width: Int, min: Int, max: Int, random: Random) = IntArray(width).apply {
- set(0, (min + max) / 2)
- for (x in 1 until width) {
- val previous = get(x - 1)
- var d = random.nextInt(-5, 6).let { if (it !in -4..4) it / abs(it) else 0 }
-
- if (previous + d !in min..max) { d = -d }
- if (lastIndex - x < abs(get(0) - previous) * 3) {
- d = get(0).compareTo(previous).let { if (it != 0) it / abs(it) else 0 }
- }
-
- set(x, get(x - 1) + d)
- }
-}
-
-/**
- * Generates world of given width and height with given seed
- * @param width world width
- * @param height world height
- * @param seed seed for random number generator
- * @return pair of foreground and background layers
- */
-fun generate(width: Int, height: Int, seed: Long = TimeUtils.millis()): Pair<Array<IntArray>, Array<IntArray>> {
- val random = Random(seed)
- val foreMap = Array(width) { IntArray(height) }
- val backMap = Array(width) { IntArray(width) }
- val heightsMap = generateHeights(width, height / 2, height * 3 / 4, random)
-
- for (x in 0 until width) {
- val xHeight = heightsMap[x]
-
- foreMap[x][xHeight] = GameItems.getBlockId("grass")
- foreMap[x][height - 1] = GameItems.getBlockId("bedrock")
- backMap[x][xHeight] = GameItems.getBlockId("grass")
- backMap[x][height - 1] = GameItems.getBlockId("bedrock")
-
- for (y in xHeight + 1 until height - 1) {
- foreMap[x][y] = when {
- y < xHeight + random.nextInt(5, 8) -> GameItems.getBlockId("dirt")
- else -> GameItems.getBlockId("stone")
- }
- backMap[x][y] = foreMap[x][y]
- }
- }
- return Pair(foreMap, backMap)
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/CommonBlockActionUtils.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/CommonBlockActionUtils.kt
--- /dev/null
@@ -0,0 +1,25 @@
+package ru.deadsoftware.cavedroid.game.actions
+
+import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.game.actions.placeblock.IPlaceBlockAction
+import ru.deadsoftware.cavedroid.game.actions.updateblock.IUpdateBlockAction
+import ru.deadsoftware.cavedroid.game.actions.updateblock.UpdateRequiresBlockAction
+import ru.deadsoftware.cavedroid.game.actions.placeblock.PlaceBlockItemToBackgroundAction
+import ru.deadsoftware.cavedroid.game.actions.placeblock.PlaceBlockItemToForegroundAction
+import ru.deadsoftware.cavedroid.game.model.item.Item
+
+private const val TAG = "PlaceBlockActionUtils"
+
+fun Map<String, IPlaceBlockAction>.placeToForegroundAction(item: Item.Placeable, x: Int, y: Int) {
+ get(PlaceBlockItemToForegroundAction.ACTION_KEY)?.place(item, x, y)
+ ?: Gdx.app.error(TAG, "action place_foreground_block not found")
+}
+
+fun Map<String, IPlaceBlockAction>.placeToBackgroundAction(item: Item.Placeable, x: Int, y: Int) {
+ get(PlaceBlockItemToBackgroundAction.ACTION_KEY)?.place(item, x, y)
+ ?: Gdx.app.error(TAG, "action place_background_block not found")
+}
+
+fun Map<String, IUpdateBlockAction>.getRequiresBlockAction(): IUpdateBlockAction {
+ return requireNotNull(get(UpdateRequiresBlockAction.ACTION_KEY)) { "action requires_block not found" }
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/PlaceBlockActionsModule.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/PlaceBlockActionsModule.kt
--- /dev/null
@@ -0,0 +1,40 @@
+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
--- /dev/null
@@ -0,0 +1,52 @@
+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
--- /dev/null
@@ -0,0 +1,37 @@
+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
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/IPlaceBlockAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/IPlaceBlockAction.kt
--- /dev/null
@@ -0,0 +1,9 @@
+package ru.deadsoftware.cavedroid.game.actions.placeblock
+
+import ru.deadsoftware.cavedroid.game.model.item.Item
+
+interface IPlaceBlockAction {
+
+ fun place(placeable: Item.Placeable, x: Int, y: Int)
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToBackgroundAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToBackgroundAction.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToBackgroundAction.kt
@@ -0,0 +1,27 @@
+package ru.deadsoftware.cavedroid.game.actions.placeblock
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class PlaceBlockItemToBackgroundAction @Inject constructor(
+ private val gameWorld: GameWorld,
+ private val gameItemsHolder: GameItemsHolder,
+ private val mobsController: MobsController,
+) : IPlaceBlockAction {
+
+ override fun place(placeable: Item.Placeable, x: Int, y: Int) {
+ if (gameWorld.placeToBackground(x, y, placeable.block)) {
+ mobsController.player.decreaseCurrentItemCount(gameItemsHolder)
+ }
+ }
+
+ companion object {
+ const val ACTION_KEY = "place_background_block"
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToForegroundAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToForegroundAction.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToForegroundAction.kt
@@ -0,0 +1,32 @@
+package ru.deadsoftware.cavedroid.game.actions.placeblock
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class PlaceBlockItemToForegroundAction @Inject constructor(
+ private val gameWorld: GameWorld,
+ private val placeSlabAction: PlaceSlabAction,
+ private val gameItemsHolder: GameItemsHolder,
+ private val mobsController: MobsController,
+) : IPlaceBlockAction {
+
+ override fun place(placeable: Item.Placeable, x: Int, y: Int) {
+ if (placeable.isSlab()) {
+ placeSlabAction.place(placeable, x, y)
+ } else {
+ if (gameWorld.placeToForeground(x, y, placeable.block)) {
+ mobsController.player.decreaseCurrentItemCount(gameItemsHolder)
+ }
+ }
+ }
+
+ companion object {
+ const val ACTION_KEY = "place_foreground_block"
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceSlabAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceSlabAction.kt
--- /dev/null
@@ -0,0 +1,42 @@
+package ru.deadsoftware.cavedroid.game.actions.placeblock
+
+import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class PlaceSlabAction @Inject constructor(
+ private val gameWorld: GameWorld,
+ private val mobsController: MobsController,
+ private val gameItemsHolder: GameItemsHolder,
+) : IPlaceBlockAction {
+
+ override fun place(placeable: Item.Placeable, x: Int, y: Int) {
+ if (placeable !is Item.Slab) {
+ Gdx.app.debug(TAG, "Place slab action called on ${placeable.params.key} which is not a slab")
+ return
+ }
+
+ val slabPart = if ((gameWorld.hasForeAt(x, y - 1)
+ || gameWorld.getForeMap(x - 1, y) == placeable.topPartBlock
+ || gameWorld.getForeMap(x + 1, y) == placeable.topPartBlock)
+ && !gameWorld.hasForeAt(x, y + 1)) {
+ placeable.topPartBlock
+ } else {
+ placeable.bottomPartBlock
+ }
+
+ if (gameWorld.placeToForeground(x, y, slabPart)) {
+ mobsController.player.decreaseCurrentItemCount(gameItemsHolder)
+ }
+ }
+
+ companion object {
+ private const val TAG = "PlaceSlabAction"
+ const val ACTION_KEY = "place_slab"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/IUpdateBlockAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/IUpdateBlockAction.kt
--- /dev/null
@@ -0,0 +1,7 @@
+package ru.deadsoftware.cavedroid.game.actions.updateblock
+
+interface IUpdateBlockAction {
+
+ fun update(x: Int, y: Int)
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGrassAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGrassAction.kt
--- /dev/null
@@ -0,0 +1,24 @@
+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 javax.inject.Inject
+
+@GameScope
+class UpdateGrassAction @Inject constructor(
+ private val gameWorld: GameWorld,
+ private val mGameItemsHolder: GameItemsHolder,
+) : IUpdateBlockAction {
+
+ 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"))
+ }
+ }
+
+ companion object {
+ const val BLOCK_KEY = "grass"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGravelAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGravelAction.kt
--- /dev/null
@@ -0,0 +1,28 @@
+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
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateRequiresBlockAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateRequiresBlockAction.kt
--- /dev/null
@@ -0,0 +1,21 @@
+package ru.deadsoftware.cavedroid.game.actions.updateblock
+
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UpdateRequiresBlockAction @Inject constructor(
+ private val gameWorld: GameWorld,
+) : IUpdateBlockAction {
+
+ override fun update(x: Int, y: Int) {
+ if (gameWorld.getForeMap(x, y + 1).params.hasCollision.not()) {
+ gameWorld.destroyForeMap(x, y)
+ }
+ }
+
+ companion object {
+ const val ACTION_KEY = "requires_block"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSandAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSandAction.kt
--- /dev/null
@@ -0,0 +1,28 @@
+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
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSnowedGrassAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSnowedGrassAction.kt
--- /dev/null
@@ -0,0 +1,24 @@
+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 javax.inject.Inject
+
+@GameScope
+class UpdateSnowedGrassAction @Inject constructor(
+ private val gameWorld: GameWorld,
+ private val mGameItemsHolder: GameItemsHolder,
+) : IUpdateBlockAction {
+
+ 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"))
+ }
+ }
+
+ companion object {
+ const val BLOCK_KEY = "grass_snowed"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/IUseItemAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/IUseItemAction.kt
--- /dev/null
@@ -0,0 +1,9 @@
+package ru.deadsoftware.cavedroid.game.actions.useitem
+
+import ru.deadsoftware.cavedroid.game.model.item.Item
+
+interface IUseItemAction {
+
+ fun perform(item: Item.Usable, x: Int, y: Int)
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseEmptyBucketAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseEmptyBucketAction.kt
--- /dev/null
@@ -0,0 +1,37 @@
+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.block.Block
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UseEmptyBucketAction @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 foregroundBlock = gameWorld.getForeMap(x, y)
+ if (!foregroundBlock.isFluid()) {
+ return
+ }
+ gameWorld.resetForeMap(x, y)
+
+ val filled = when (foregroundBlock) {
+ is Block.Lava -> gameItemsHolder.getItem("bucket_lava")
+ is Block.Water -> gameItemsHolder.getItem("bucket_water")
+ else -> throw IllegalStateException("unknown fluid")
+ }
+
+ mobsController.player.setCurrentInventorySlotItem(filled)
+ }
+
+ companion object {
+ const val ACTION_KEY = "use_empty_bucket"
+ }
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseLavaBucketAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseLavaBucketAction.kt
--- /dev/null
@@ -0,0 +1,28 @@
+package ru.deadsoftware.cavedroid.game.actions.useitem
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UseLavaBucketAction @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) {
+ gameWorld.placeToForeground(x, y, gameItemsHolder.getBlock("lava"))
+
+ if (mobsController.player.gameMode != 1) {
+ mobsController.player.setCurrentInventorySlotItem(gameItemsHolder.getItem("bucket_empty"))
+ }
+ }
+
+ companion object {
+ const val ACTION_KEY = "use_lava_bucket"
+ }
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseWaterBucketAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseWaterBucketAction.kt
--- /dev/null
@@ -0,0 +1,28 @@
+package ru.deadsoftware.cavedroid.game.actions.useitem
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class UseWaterBucketAction @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) {
+ gameWorld.placeToForeground(x, y, gameItemsHolder.getBlock("water"))
+ if (mobsController.player.gameMode != 1) {
+ mobsController.player.setCurrentInventorySlotItem(gameItemsHolder.getItem("bucket_empty"))
+ }
+ }
+
+ companion object {
+ const val ACTION_KEY = "use_water_bucket"
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/debug/DebugInfoStringsProvider.kt b/core/src/ru/deadsoftware/cavedroid/game/debug/DebugInfoStringsProvider.kt
--- /dev/null
@@ -0,0 +1,36 @@
+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.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class DebugInfoStringsProvider @Inject constructor(
+ private val mobsController: MobsController,
+ private val dropController: DropController,
+ private val gameWorld: GameWorld
+) {
+
+ fun getDebugStrings(): List<String> {
+ val player = mobsController.player
+
+ return listOf(
+ "FPS: ${Gdx.graphics.framesPerSecond}",
+ "X: ${player.mapX}",
+ "Y: ${gameWorld.height - player.upperMapY}",
+ "CurX: ${player.cursorX}",
+ "CurY: ${player.cursorY}",
+ "Velocity: ${player.velocity}",
+ "Swim: ${player.swim}",
+ "Mobs: ${mobsController.mobs.size}",
+ "Drops: ${dropController.size}",
+ "Block: ${gameWorld.getForeMap(player.cursorX, player.cursorY).params.key}",
+ "Hand: ${player.inventory[player.slot].item.params.key}",
+ "Game mode: ${player.gameMode}",
+ "Block damage: ${player.blockDamage}"
+ )
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/IGameInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/IGameInputHandler.kt
--- /dev/null
@@ -0,0 +1,19 @@
+package ru.deadsoftware.cavedroid.game.input
+
+import ru.deadsoftware.cavedroid.game.input.action.IGameInputAction
+
+interface IGameInputHandler<A : IGameInputAction> {
+
+ /**
+ * Implementation should check if conditions for handling an input are satisfied
+ * For example - inventory input handler should return false if inventory is closed
+ */
+ fun checkConditions(action: A): Boolean
+
+ /**
+ * Handle given input action.
+ * This will not be called if [checkConditions] returned false
+ */
+ fun handle(action: A)
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/InputUtils.kt b/core/src/ru/deadsoftware/cavedroid/game/input/InputUtils.kt
--- /dev/null
@@ -0,0 +1,21 @@
+package ru.deadsoftware.cavedroid.game.input
+
+import com.badlogic.gdx.graphics.g2d.TextureRegion
+import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.misc.Assets
+
+fun isInsideHotbar(action: MouseInputAction): Boolean {
+ val hotbar = requireNotNull(Assets.textureRegions["hotbar"])
+
+ return action.screenY <= hotbar.regionHeight &&
+ action.screenX >= action.cameraViewport.width / 2 - hotbar.regionWidth / 2 &&
+ action.screenX <= action.cameraViewport.width / 2 + hotbar.regionWidth / 2
+}
+
+fun isInsideWindow(action: MouseInputAction, windowTexture: TextureRegion): Boolean {
+ return action.screenY > action.cameraViewport.height / 2 - windowTexture.regionHeight / 2 &&
+ action.screenY < action.cameraViewport.height / 2 + windowTexture.regionHeight / 2 &&
+ action.screenX > action.cameraViewport.width / 2 - windowTexture.regionWidth / 2 &&
+ action.screenX < action.cameraViewport.width / 2 + windowTexture.regionWidth / 2
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/KeyboardInputHandlersModule.kt b/core/src/ru/deadsoftware/cavedroid/game/input/KeyboardInputHandlersModule.kt
--- /dev/null
@@ -0,0 +1,118 @@
+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
--- /dev/null
@@ -0,0 +1,75 @@
+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
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/action/IGameInputAction.kt b/core/src/ru/deadsoftware/cavedroid/game/input/action/IGameInputAction.kt
--- /dev/null
@@ -0,0 +1,3 @@
+package ru.deadsoftware.cavedroid.game.input.action
+
+interface IGameInputAction
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/action/KeyboardInputAction.kt b/core/src/ru/deadsoftware/cavedroid/game/input/action/KeyboardInputAction.kt
--- /dev/null
@@ -0,0 +1,8 @@
+package ru.deadsoftware.cavedroid.game.input.action
+
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+
+data class KeyboardInputAction(
+ val actionKey: KeyboardInputActionKey,
+ val isKeyDown: Boolean,
+) : IGameInputAction
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/action/MouseInputAction.kt b/core/src/ru/deadsoftware/cavedroid/game/input/action/MouseInputAction.kt
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.game.input.action
+
+import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+
+data class MouseInputAction(
+ val screenX: Float,
+ val screenY: Float,
+ val actionKey: MouseInputActionKey,
+ val cameraViewport: Rectangle
+) : IGameInputAction
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/action/keys/KeyboardInputActionKey.kt b/core/src/ru/deadsoftware/cavedroid/game/input/action/keys/KeyboardInputActionKey.kt
--- /dev/null
@@ -0,0 +1,24 @@
+package ru.deadsoftware.cavedroid.game.input.action.keys
+
+sealed interface KeyboardInputActionKey {
+
+ data object Left : KeyboardInputActionKey
+ data object Right : KeyboardInputActionKey
+ data object Down : KeyboardInputActionKey
+
+ data object Jump : KeyboardInputActionKey
+
+ data object Crouch : KeyboardInputActionKey
+
+ data object SwitchControlsMode : KeyboardInputActionKey
+
+ data object OpenInventory : KeyboardInputActionKey
+
+ data object Pause : KeyboardInputActionKey
+
+ data object ShowDebug : KeyboardInputActionKey
+ data object SpawnPig : KeyboardInputActionKey
+ data object SwitchGameMode : KeyboardInputActionKey
+ data object ShowMap : KeyboardInputActionKey
+ data object OpenCraft : KeyboardInputActionKey
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/action/keys/MouseInputActionKey.kt b/core/src/ru/deadsoftware/cavedroid/game/input/action/keys/MouseInputActionKey.kt
--- /dev/null
@@ -0,0 +1,41 @@
+package ru.deadsoftware.cavedroid.game.input.action.keys
+
+sealed interface MouseInputActionKey {
+
+ val touchUp: Boolean
+
+ data object None : MouseInputActionKey {
+ override val touchUp: Boolean
+ get() = throw IllegalAccessException("not applicable for mouse move action")
+ }
+
+ data object Dragged : MouseInputActionKey {
+ override val touchUp: Boolean
+ get() = throw IllegalAccessException("not applicable for mouse dragged action")
+ }
+
+ data class Left(
+ override val touchUp: Boolean
+ ) : MouseInputActionKey
+
+ data class Right(
+ override val touchUp: Boolean
+ ) : MouseInputActionKey
+
+ data class Middle(
+ override val touchUp: Boolean
+ ) : MouseInputActionKey
+
+ data class Touch(
+ override val touchUp: Boolean
+ ) : MouseInputActionKey
+
+ data class Scroll(
+ val amountX: Float,
+ val amountY: Float
+ ) : MouseInputActionKey {
+ override val touchUp: Boolean
+ get() = throw IllegalAccessException("not applicable for mouse scroll action")
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/CloseGameWindowKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/CloseGameWindowKeyboardInputHandler.kt
--- /dev/null
@@ -0,0 +1,39 @@
+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 CloseGameWindowKeyboardInputHandler @Inject constructor(
+ private val gameWindowsManager: GameWindowsManager,
+ private val mobsController: MobsController,
+ private val dropController: DropController,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.OpenInventory &&
+ action.isKeyDown && gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ val selectedItem = gameWindowsManager.currentWindow?.selectedItem
+ if (selectedItem != null) {
+ for (i in 1 .. selectedItem.amount) {
+ dropController.addDrop(
+ /* x = */ mobsController.player.x + (32f * mobsController.player.direction.basis),
+ /* y = */ mobsController.player.y,
+ /* item = */ selectedItem.item
+ )
+ }
+ gameWindowsManager.currentWindow?.selectedItem = null
+ }
+ gameWindowsManager.closeWindow()
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/FlyDownKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/FlyDownKeyboardInputHandler.kt
--- /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.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player
+import javax.inject.Inject
+
+@GameScope
+class FlyDownKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.Down &&
+ mobsController.player.isFlyMode &&
+ (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ if (action.isKeyDown) {
+ mobsController.player.velocity.y = mobsController.player.speed
+ } else {
+ mobsController.player.velocity.y = 0f
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/FlyUpKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/FlyUpKeyboardInputHandler.kt
--- /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.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player
+import javax.inject.Inject
+
+@GameScope
+class FlyUpKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.Jump &&
+ mobsController.player.isFlyMode &&
+ (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ if (action.isKeyDown) {
+ mobsController.player.velocity.y = -mobsController.player.speed
+ } else {
+ mobsController.player.velocity.y = 0f
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/GoLeftKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/GoLeftKeyboardInputHandler.kt
--- /dev/null
@@ -0,0 +1,33 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.Mob
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player
+import javax.inject.Inject
+
+@GameScope
+class GoLeftKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.Left &&
+ (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch) &&
+ (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ if (action.isKeyDown) {
+ mobsController.player.velocity.x = -mobsController.player.speed
+ mobsController.player.setDir(Mob.Direction.LEFT)
+ } else {
+ mobsController.player.velocity.x = 0f
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/GoRightKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/GoRightKeyboardInputHandler.kt
--- /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.IGameInputHandler
+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 javax.inject.Inject
+
+@GameScope
+class GoRightKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.Right &&
+ (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ if (action.isKeyDown) {
+ mobsController.player.velocity.x = mobsController.player.speed
+ mobsController.player.setDir(Mob.Direction.RIGHT)
+ } else {
+ mobsController.player.velocity.x = 0f
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/JumpKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/JumpKeyboardInputHandler.kt
--- /dev/null
@@ -0,0 +1,29 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player
+import javax.inject.Inject
+
+@GameScope
+class JumpKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.Jump &&
+ mobsController.player.canJump() && !mobsController.player.isFlyMode &&
+ action.isKeyDown &&
+ (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ mobsController.player.jump()
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/MoveCursorControlsModeKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/MoveCursorControlsModeKeyboardInputHandler.kt
--- /dev/null
@@ -0,0 +1,62 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import com.badlogic.gdx.math.MathUtils
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class MoveCursorControlsModeKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+ private val gameWorld: GameWorld,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ 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.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.Down -> player.cursorY++
+ else -> return
+ }
+
+ checkCursorBounds()
+ }
+
+ companion object {
+ private const val SURVIVAL_CURSOR_RANGE = 4
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenCraftingKeyboardInputHandler.kt
@@ -0,0 +1,26 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.objects.DropController
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import javax.inject.Inject
+
+@GameScope
+class OpenCraftingKeyboardInputHandler @Inject constructor(
+ private val gameWindowsManager: GameWindowsManager,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.OpenCraft &&
+ action.isKeyDown && gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ gameWindowsManager.openCrafting()
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenInventoryKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenInventoryKeyboardInputHandler.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/OpenInventoryKeyboardInputHandler.kt
@@ -0,0 +1,26 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.objects.DropController
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import javax.inject.Inject
+
+@GameScope
+class OpenInventoryKeyboardInputHandler @Inject constructor(
+ private val gameWindowsManager: GameWindowsManager,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.OpenInventory &&
+ action.isKeyDown && gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ gameWindowsManager.openInventory()
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/PauseGameKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/PauseGameKeyboardInputHandler.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/PauseGameKeyboardInputHandler.kt
@@ -0,0 +1,30 @@
+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.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.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class PauseGameKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val dropController: DropController,
+ private val mobsController: MobsController,
+ private val gameWorld: GameWorld,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ 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)
+ mainConfig.caveGame.quitGame()
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleControlsModeKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleControlsModeKeyboardInputHandler.kt
--- /dev/null
@@ -0,0 +1,31 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player
+import javax.inject.Inject
+
+@GameScope
+class ToggleControlsModeKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.SwitchControlsMode && action.isKeyDown
+ && mainConfig.isTouch
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ if (mobsController.player.controlMode == Player.ControlMode.WALK) {
+ mobsController.player.controlMode = Player.ControlMode.CURSOR
+ } else {
+ mobsController.player.controlMode = Player.ControlMode.WALK
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleDebugInfoKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleDebugInfoKeyboardInputHandler.kt
--- /dev/null
@@ -0,0 +1,22 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import javax.inject.Inject
+
+@GameScope
+class ToggleDebugInfoKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.ShowDebug && action.isKeyDown
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ mainConfig.isShowInfo = !mainConfig.isShowInfo
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleGameModeKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleGameModeKeyboardInputHandler.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleGameModeKeyboardInputHandler.kt
@@ -0,0 +1,29 @@
+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.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import javax.inject.Inject
+
+@GameScope
+class ToggleGameModeKeyboardInputHandler @Inject constructor(
+ private val mobsController: MobsController
+) : IGameInputHandler<KeyboardInputAction> {
+
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.SwitchGameMode && action.isKeyDown
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ if (mobsController.player.gameMode == 1) {
+ mobsController.player.gameMode = 0
+ } else if (mobsController.player.gameMode == 0) {
+ mobsController.player.gameMode = 1
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleMinimapKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleMinimapKeyboardInputHandler.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/ToggleMinimapKeyboardInputHandler.kt
@@ -0,0 +1,22 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import javax.inject.Inject
+
+@GameScope
+class ToggleMinimapKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return action.actionKey is KeyboardInputActionKey.ShowMap && action.isKeyDown
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ mainConfig.isShowMap = !mainConfig.isShowMap
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/TurnOnFlyModeKeyboardInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/TurnOnFlyModeKeyboardInputHandler.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/input/handler/keyboard/TurnOnFlyModeKeyboardInputHandler.kt
@@ -0,0 +1,29 @@
+package ru.deadsoftware.cavedroid.game.input.handler.keyboard
+
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player
+import javax.inject.Inject
+
+@GameScope
+class TurnOnFlyModeKeyboardInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+) : IGameInputHandler<KeyboardInputAction> {
+
+ override fun checkConditions(action: KeyboardInputAction): Boolean {
+ return mobsController.player.gameMode == 1 && action.actionKey is KeyboardInputActionKey.Jump &&
+ !mobsController.player.isFlyMode && !mobsController.player.canJump() && action.isKeyDown &&
+ (mobsController.player.controlMode == Player.ControlMode.WALK || !mainConfig.isTouch)
+ }
+
+ override fun handle(action: KeyboardInputAction) {
+ mobsController.player.isFlyMode = true
+ mobsController.player.velocity.y = 0f
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/AttackMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/AttackMouseInputHandler.kt
--- /dev/null
@@ -0,0 +1,35 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+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.isInsideHotbar
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+
+@GameScope
+class AttackMouseInputHandler @Inject constructor(
+ private val mobsController: MobsController,
+ private val gameWorld: GameWorld,
+ private val gameWindowsManager: GameWindowsManager
+) : IGameInputHandler<MouseInputAction> {
+
+ override fun checkConditions(action: MouseInputAction): Boolean {
+ return gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE &&
+ !isInsideHotbar(action) &&
+ action.actionKey is MouseInputActionKey.Left
+
+ }
+
+ override fun handle(action: MouseInputAction) {
+ if (action.actionKey.touchUp) {
+ mobsController.player.stopHitting()
+ } else {
+ mobsController.player.startHitting()
+ };
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CloseGameWindowMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CloseGameWindowMouseInputHandler.kt
--- /dev/null
+++ b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CloseGameWindowMouseInputHandler.kt
@@ -0,0 +1,58 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.graphics.g2d.TextureRegion
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+import ru.deadsoftware.cavedroid.game.input.isInsideWindow
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.objects.DropController
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class CloseGameWindowMouseInputHandler @Inject constructor(
+ private val gameWindowsManager: GameWindowsManager,
+ private val mobsController: MobsController,
+ private val dropController: DropController,
+) : IGameInputHandler<MouseInputAction> {
+
+ private val creativeInventoryTexture get() = requireNotNull(Assets.textureRegions["creative"])
+ private val survivalInventoryTexture get() = requireNotNull(Assets.textureRegions["survival"])
+ private val craftingInventoryTexture get() = requireNotNull(Assets.textureRegions["crafting_table"])
+
+ override fun checkConditions(action: MouseInputAction): Boolean {
+ return gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE &&
+ (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) &&
+ !action.actionKey.touchUp &&
+ !isInsideWindow(action, getCurrentWindowTexture())
+ }
+
+ private fun getCurrentWindowTexture(): TextureRegion {
+ return when (val window = gameWindowsManager.getCurrentWindow()) {
+ GameUiWindow.CREATIVE_INVENTORY -> creativeInventoryTexture
+ GameUiWindow.SURVIVAL_INVENTORY -> survivalInventoryTexture
+ GameUiWindow.CRAFTING_TABLE -> craftingInventoryTexture
+ else -> throw UnsupportedOperationException("Cant close window ${window.name}")
+ }
+ }
+
+ override fun handle(action: MouseInputAction) {
+ val selectedItem = gameWindowsManager.currentWindow?.selectedItem
+ if (selectedItem != null) {
+ for (i in 1 .. selectedItem.amount) {
+ dropController.addDrop(
+ /* x = */ mobsController.player.x + (32f * mobsController.player.direction.basis),
+ /* y = */ mobsController.player.y,
+ /* item = */ selectedItem.item
+ )
+ }
+ gameWindowsManager.currentWindow?.selectedItem = null
+ }
+ gameWindowsManager.closeWindow()
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CreativeInventoryScrollMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CreativeInventoryScrollMouseInputHandler.kt
--- /dev/null
@@ -0,0 +1,88 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.math.MathUtils
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+import ru.deadsoftware.cavedroid.game.input.isInsideWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+import kotlin.math.abs
+
+@GameScope
+class CreativeInventoryScrollMouseInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val gameWindowsManager: GameWindowsManager,
+ private val gameItemsHolder: GameItemsHolder,
+) : IGameInputHandler<MouseInputAction> {
+
+ private val creativeInventoryTexture get() = requireNotNull(Assets.textureRegions["creative"])
+
+ private var dragStartY = 0f
+
+ override fun checkConditions(action: MouseInputAction): Boolean {
+ return gameWindowsManager.getCurrentWindow() == GameUiWindow.CREATIVE_INVENTORY &&
+ (gameWindowsManager.isDragging || isInsideWindow(action, creativeInventoryTexture)) &&
+ (checkStartDragConditions(action) || checkEndDragConditions(action) ||
+ checkDragConditions(action) || action.actionKey is MouseInputActionKey.Scroll)
+
+ }
+
+ private fun checkStartDragConditions(action: MouseInputAction): Boolean {
+ return (action.actionKey is MouseInputActionKey.Touch) &&
+ !action.actionKey.touchUp && !gameWindowsManager.isDragging
+ }
+
+ private fun checkEndDragConditions(action: MouseInputAction): Boolean {
+ return action.actionKey is MouseInputActionKey.Touch &&
+ action.actionKey.touchUp && gameWindowsManager.isDragging
+ }
+
+ private fun checkDragConditions(action: MouseInputAction): Boolean {
+ return mainConfig.isTouch && action.actionKey is MouseInputActionKey.Dragged &&
+ abs(action.screenY - dragStartY) >= DRAG_SENSITIVITY
+ }
+
+ private fun clampScrollAmount() {
+ gameWindowsManager.creativeScrollAmount =
+ MathUtils.clamp(gameWindowsManager.creativeScrollAmount, 0, gameItemsHolder.getMaxCreativeScrollAmount())
+ }
+
+ private fun handleStartOrEndDrag(action: MouseInputAction) {
+ if (gameWindowsManager.isDragging) {
+ gameWindowsManager.isDragging = false
+ } else {
+ dragStartY = action.screenY
+ }
+ }
+
+ private fun handleDrag(action: MouseInputAction) {
+ gameWindowsManager.isDragging = true
+ gameWindowsManager.creativeScrollAmount += ((dragStartY - action.screenY) / DRAG_SENSITIVITY).toInt()
+ clampScrollAmount()
+ dragStartY = action.screenY
+ }
+
+ private fun handleScroll(action: MouseInputAction) {
+ gameWindowsManager.creativeScrollAmount += (action.actionKey as MouseInputActionKey.Scroll).amountY.toInt()
+ clampScrollAmount()
+ }
+
+ override fun handle(action: MouseInputAction) {
+ when (action.actionKey) {
+ is MouseInputActionKey.Touch -> handleStartOrEndDrag(action)
+ is MouseInputActionKey.Dragged -> handleDrag(action)
+ is MouseInputActionKey.Scroll -> handleScroll(action)
+ else -> return
+ }
+ }
+
+ companion object {
+ private const val DRAG_SENSITIVITY = 16f
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CursorMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/CursorMouseInputHandler.kt
--- /dev/null
@@ -0,0 +1,120 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.math.MathUtils
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.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.model.block.Block
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.utils.bl
+import ru.deadsoftware.cavedroid.misc.utils.px
+import javax.inject.Inject
+
+@GameScope
+class CursorMouseInputHandler @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+ private val gameWorld: GameWorld,
+) : IGameInputHandler<MouseInputAction> {
+
+ private val player get() = mobsController.player
+
+ 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
+ }
+
+ if (player.cursorX.px + 8 < player.x + player.width / 2) {
+ player.setDir(Mob.Direction.LEFT)
+ } else {
+ player.setDir(Mob.Direction.RIGHT)
+ }
+ }
+
+ private fun handleWalkTouch() {
+ player.cursorX = player.mapX + player.direction.basis
+ player.cursorY = player.upperMapY
+
+ for (i in 1..2) {
+ if (gameWorld.isCurrentBlockAutoselectable()) {
+ break
+ }
+ player.cursorY++
+ }
+
+ if (!gameWorld.isCurrentBlockAutoselectable()) {
+ player.cursorX -= player.direction.basis
+ }
+ }
+
+ private fun getPlayerHeadRotation(mouseWorldX: Float, mouseWorldY: Float): Float {
+ val h = mouseWorldX - player.x
+ val v = mouseWorldY - player.y
+
+ return MathUtils.atan(v / h) * MathUtils.radDeg
+ }
+
+ private fun handleMouse(action: MouseInputAction) {
+ val worldX = action.screenX + action.cameraViewport.x
+ val worldY = action.screenY + action.cameraViewport.y
+
+ // when worldX < 0, need to subtract 1 to avoid negative zero
+// val fixCycledWorld = if (worldX < 0) 1 else 0
+
+ player.cursorX = worldX.bl - 0
+ player.cursorY = worldY.bl
+
+ player.headRotation = getPlayerHeadRotation(worldX, worldY)
+ }
+
+ override fun checkConditions(action: MouseInputAction): Boolean {
+ return action.actionKey is MouseInputActionKey.None
+ }
+
+ override fun handle(action: MouseInputAction) {
+ val pastCursorX = player.cursorX
+ val pastCursorY = player.cursorY
+
+ when {
+ player.controlMode == Player.ControlMode.WALK && mainConfig.isTouch -> handleWalkTouch()
+ !mainConfig.isTouch -> handleMouse(action)
+ }
+
+ checkCursorBounds()
+ setPlayerDirectionToCursor()
+
+ if (player.cursorX != pastCursorX || player.cursorY != pastCursorY) {
+ player.blockDamage = 0f
+ }
+ }
+
+ companion object {
+ private const val SURVIVAL_CURSOR_RANGE = 4
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/HotbarMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/HotbarMouseInputHandler.kt
--- /dev/null
@@ -0,0 +1,97 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.utils.Timer
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.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.isInsideHotbar
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class HotbarMouseInputHandler @Inject constructor(
+ private val gameWindowsManager: GameWindowsManager,
+ private val mobsController: MobsController,
+) : IGameInputHandler<MouseInputAction> {
+
+ private val hotbarTexture get() = requireNotNull(Assets.textureRegions["hotbar"])
+
+ private var buttonHoldTask: Timer.Task? = null
+
+ override fun checkConditions(action: MouseInputAction): Boolean {
+ return buttonHoldTask?.isScheduled == true ||
+ ((action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch)
+ && isInsideHotbar(action)
+ || action.actionKey is MouseInputActionKey.Scroll) &&
+ gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE
+ }
+
+ private fun cancelHold() {
+ buttonHoldTask?.cancel()
+ buttonHoldTask = null
+ }
+
+ private fun handleHold() {
+ buttonHoldTask = null
+ gameWindowsManager.openInventory()
+ }
+
+ private fun handleDown(action: MouseInputAction) {
+ buttonHoldTask = object : Timer.Task() {
+ override fun run() {
+ handleHold()
+ }
+ }
+
+ Timer.schedule(buttonHoldTask, TOUCH_HOLD_TIME_SEC)
+ }
+
+ private fun handleUp(action: MouseInputAction) {
+ mobsController.player.slot =
+ ((action.screenX -
+ (action.cameraViewport.width / 2 - hotbarTexture.regionWidth / 2))
+ / HOTBAR_CELL_WIDTH).toInt()
+ }
+
+ 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
+ }
+ }
+
+ override fun handle(action: MouseInputAction) {
+ if (buttonHoldTask != null && buttonHoldTask?.isScheduled == true) {
+ cancelHold()
+ }
+
+ if (action.actionKey !is MouseInputActionKey.Left && action.actionKey !is MouseInputActionKey.Touch ) {
+ if (action.actionKey is MouseInputActionKey.Scroll) {
+ handleScroll(action)
+ }
+ return
+ }
+
+ if (action.actionKey.touchUp) {
+ handleUp(action)
+ } else {
+ handleDown(action)
+ }
+ }
+
+ 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/SelectCraftingInventoryItemMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCraftingInventoryItemMouseInputHandler.kt
--- /dev/null
@@ -0,0 +1,161 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+import ru.deadsoftware.cavedroid.game.input.isInsideWindow
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.windows.inventory.CraftingInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class SelectCraftingInventoryItemMouseInputHandler @Inject constructor(
+ private val gameWindowsManager: GameWindowsManager,
+ private val mobsController: MobsController,
+ private val gameItemsHolder: GameItemsHolder,
+) : IGameInputHandler<MouseInputAction> {
+
+ private val survivalWindowTexture get() = requireNotNull(Assets.textureRegions["survival"])
+
+ override fun checkConditions(action: MouseInputAction): Boolean {
+ return gameWindowsManager.getCurrentWindow() == GameUiWindow.CRAFTING_TABLE &&
+ isInsideWindow(action, survivalWindowTexture) &&
+ (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Right || action.actionKey is MouseInputActionKey.Touch)
+ && action.actionKey.touchUp
+ }
+
+ private fun onLeftCLick(items: MutableList<InventoryItem?>, window: CraftingInventoryWindow, index: Int) {
+ val selectedItem = window.selectedItem
+ val clickedItem = items[index]
+
+ if (clickedItem != null && selectedItem != null && items[index]!!.item == selectedItem.item &&
+ items[index]!!.amount + selectedItem.amount <= selectedItem.item.params.maxStack) {
+ items[index]!!.amount += selectedItem.amount
+ window.selectedItem = null
+ return
+ }
+
+ val item = items[index]
+ items[index] = selectedItem ?: gameItemsHolder.fallbackItem.toInventoryItem()
+ window.selectedItem = item
+ }
+
+ private fun onRightClick(items: MutableList<InventoryItem?>, window: CraftingInventoryWindow, index: Int) {
+ val clickedItem = items[index]
+ val selectedItem = window.selectedItem
+ ?.takeIf { clickedItem == null || clickedItem.item.isNone() || it.item == items[index]!!.item && items[index]!!.amount + 1 < it.item.params.maxStack }
+ ?: return
+
+ val newItem = selectedItem.item.toInventoryItem((clickedItem?.takeIf { !it.item.isNone() }?.amount ?: 0) + 1)
+ items[index] = newItem
+ selectedItem.amount --
+
+ if (selectedItem.amount <= 0) {
+ window.selectedItem = null
+ }
+ }
+
+ private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
+ val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
+
+ var itemIndex = ((xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Crafting.itemsInRow))
+ itemIndex += GameWindowsConfigs.Crafting.hotbarCells
+
+ if (itemIndex >= 36) {
+ itemIndex -= 36
+ }
+
+ if (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) {
+ onLeftCLick(mobsController.player.inventory, window, itemIndex)
+ } else {
+ onRightClick(mobsController.player.inventory, window, itemIndex)
+ }
+
+ Gdx.app.debug(
+ TAG,
+ "selected item: ${window.selectedItem?.item?.params?.key ?: "null"}; index $itemIndex, grid ($xOnGrid;$yOnGrid)"
+ )
+ }
+
+ private fun handleInsideCraft(action: MouseInputAction, xOnCraft: Int, yOnCraft: Int) {
+ val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
+ val index = xOnCraft + yOnCraft * GameWindowsConfigs.Crafting.craftGridSize
+
+ if (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) {
+ onLeftCLick(window.craftingItems, window, index)
+ } else {
+ onRightClick(window.craftingItems, window, index)
+ }
+
+ window.craftResult =
+ gameItemsHolder.craftItem(window.craftingItems.map { it?.item ?: gameItemsHolder.fallbackItem })
+ }
+
+ override fun handle(action: MouseInputAction) {
+ val survivalTexture = survivalWindowTexture
+ val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
+
+ val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - survivalTexture.regionWidth / 2)
+ val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - survivalTexture.regionHeight / 2)
+
+ val xOnGrid = (xOnWindow - GameWindowsConfigs.Crafting.itemsGridMarginLeft) /
+ GameWindowsConfigs.Crafting.itemsGridColWidth
+ val yOnGrid = (yOnWindow - GameWindowsConfigs.Crafting.itemsGridMarginTop) /
+ GameWindowsConfigs.Crafting.itemsGridRowHeight
+
+ val xOnCraft = (xOnWindow - GameWindowsConfigs.Crafting.craftOffsetX) /
+ GameWindowsConfigs.Crafting.itemsGridColWidth
+ val yOnCraft = (yOnWindow - GameWindowsConfigs.Crafting.craftOffsetY) /
+ GameWindowsConfigs.Crafting.itemsGridRowHeight
+
+ val isInsideInventoryGrid = xOnGrid >= 0 && xOnGrid < GameWindowsConfigs.Crafting.itemsInRow &&
+ yOnGrid >= 0 && yOnGrid < GameWindowsConfigs.Crafting.itemsInCol
+
+ val isInsideCraftGrid = xOnCraft >= 0 && xOnCraft < GameWindowsConfigs.Crafting.craftGridSize &&
+ yOnCraft >= 0 && yOnCraft < GameWindowsConfigs.Crafting.craftGridSize
+
+ val isInsideCraftResult = xOnWindow > GameWindowsConfigs.Crafting.craftResultOffsetX &&
+ xOnWindow < GameWindowsConfigs.Crafting.craftResultOffsetX + GameWindowsConfigs.Crafting.itemsGridColWidth &&
+ yOnWindow > GameWindowsConfigs.Crafting.craftResultOffsetY &&
+ yOnWindow < GameWindowsConfigs.Crafting.craftResultOffsetY + GameWindowsConfigs.Crafting.itemsGridRowHeight
+
+ if (isInsideInventoryGrid) {
+ handleInsideInventoryGrid(action, xOnGrid.toInt(), yOnGrid.toInt())
+ } else if (isInsideCraftGrid) {
+ handleInsideCraft(action, xOnCraft.toInt(), yOnCraft.toInt())
+ } else if (isInsideCraftResult) {
+ val selectedItem = window.selectedItem
+ if (selectedItem == null || selectedItem.item.isNone() ||
+ (selectedItem.item == window.craftResult?.item && selectedItem.amount + (window.craftResult?.amount ?: 0) <= selectedItem.item.params.maxStack)) {
+ for (i in window.craftingItems.indices) {
+ if ((window.craftingItems[i]?.amount ?: 0) > 1) {
+ window.craftingItems[i]?.amount = window.craftingItems[i]?.amount!! - 1
+ } else {
+ window.craftingItems[i] = null
+ }
+ }
+ if (selectedItem != null && !selectedItem.item.isNone()) {
+ selectedItem.amount += (window.craftResult?.amount ?: 0)
+ } else {
+ window.selectedItem = window.craftResult
+ }
+ window.craftResult = gameItemsHolder.craftItem(window.craftingItems
+ .map { it?.item ?: gameItemsHolder.fallbackItem })
+ }
+ }
+
+ }
+
+ companion object {
+ private const val TAG = "SelectCraftingInventoryItemMouseInputHandler"
+
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCreativeInventoryItemMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectCreativeInventoryItemMouseInputHandler.kt
--- /dev/null
@@ -0,0 +1,58 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+import ru.deadsoftware.cavedroid.game.input.isInsideWindow
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class SelectCreativeInventoryItemMouseInputHandler @Inject constructor(
+ private val gameItemsHolder: GameItemsHolder,
+ private val gameWindowsManager: GameWindowsManager,
+ private val mobsController: MobsController,
+) : IGameInputHandler<MouseInputAction> {
+
+ 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.touchUp && isInsideWindow(action, creativeInventoryTexture)
+ }
+
+ override fun handle(action: MouseInputAction) {
+ 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
+ }
+
+ 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)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectSurvivalInventoryItemMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/SelectSurvivalInventoryItemMouseInputHandler.kt
--- /dev/null
@@ -0,0 +1,161 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.input.IGameInputHandler
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+import ru.deadsoftware.cavedroid.game.input.isInsideWindow
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.windows.inventory.SurvivalInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class SelectSurvivalInventoryItemMouseInputHandler @Inject constructor(
+ private val gameWindowsManager: GameWindowsManager,
+ private val mobsController: MobsController,
+ private val gameItemsHolder: GameItemsHolder,
+) : IGameInputHandler<MouseInputAction> {
+
+ private val survivalWindowTexture get() = requireNotNull(Assets.textureRegions["survival"])
+
+ override fun checkConditions(action: MouseInputAction): Boolean {
+ return gameWindowsManager.getCurrentWindow() == GameUiWindow.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
+ }
+ }
+
+ private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) {
+ val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
+
+ var itemIndex = ((xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Survival.itemsInRow))
+ itemIndex += GameWindowsConfigs.Survival.hotbarCells
+
+ if (itemIndex >= 36) {
+ itemIndex -= 36
+ }
+
+ if (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) {
+ onLeftCLick(mobsController.player.inventory, window, itemIndex)
+ } else {
+ onRightClick(mobsController.player.inventory, window, itemIndex)
+ }
+
+ Gdx.app.debug(
+ TAG,
+ "selected item: ${window.selectedItem?.item?.params?.key ?: "null"}; index $itemIndex, grid ($xOnGrid;$yOnGrid)"
+ )
+ }
+
+ private fun handleInsideCraft(action: MouseInputAction, xOnCraft: Int, yOnCraft: Int) {
+ val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
+ val index = xOnCraft + yOnCraft * GameWindowsConfigs.Crafting.craftGridSize // this is crafting on purpose!!
+
+ if (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Touch) {
+ onLeftCLick(window.craftingItems, window, index)
+ } else {
+ onRightClick(window.craftingItems, window, index)
+ }
+
+ window.craftResult =
+ gameItemsHolder.craftItem(window.craftingItems.map { it?.item ?: gameItemsHolder.fallbackItem })
+ }
+
+ override fun handle(action: MouseInputAction) {
+ val survivalTexture = survivalWindowTexture
+ val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
+
+ val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - survivalTexture.regionWidth / 2)
+ val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - survivalTexture.regionHeight / 2)
+
+ val xOnGrid = (xOnWindow - GameWindowsConfigs.Survival.itemsGridMarginLeft) /
+ GameWindowsConfigs.Survival.itemsGridColWidth
+ val yOnGrid = (yOnWindow - GameWindowsConfigs.Survival.itemsGridMarginTop) /
+ GameWindowsConfigs.Survival.itemsGridRowHeight
+
+ val xOnCraft = (xOnWindow - GameWindowsConfigs.Survival.craftOffsetX) /
+ GameWindowsConfigs.Survival.itemsGridColWidth
+ val yOnCraft = (yOnWindow - GameWindowsConfigs.Survival.craftOffsetY) /
+ GameWindowsConfigs.Survival.itemsGridRowHeight
+
+ val isInsideInventoryGrid = xOnGrid >= 0 && xOnGrid < GameWindowsConfigs.Survival.itemsInRow &&
+ yOnGrid >= 0 && yOnGrid < GameWindowsConfigs.Survival.itemsInCol
+
+ val isInsideCraftGrid = xOnCraft >= 0 && xOnCraft < GameWindowsConfigs.Survival.craftGridSize &&
+ yOnCraft >= 0 && yOnCraft < GameWindowsConfigs.Survival.craftGridSize
+
+ val isInsideCraftResult = xOnWindow > GameWindowsConfigs.Survival.craftResultOffsetX &&
+ xOnWindow < GameWindowsConfigs.Survival.craftResultOffsetX + GameWindowsConfigs.Survival.itemsGridColWidth &&
+ yOnWindow > GameWindowsConfigs.Survival.craftResultOffsetY &&
+ yOnWindow < GameWindowsConfigs.Survival.craftResultOffsetY + GameWindowsConfigs.Survival.itemsGridRowHeight
+
+ if (isInsideInventoryGrid) {
+ handleInsideInventoryGrid(action, xOnGrid.toInt(), yOnGrid.toInt())
+ } else if (isInsideCraftGrid) {
+ handleInsideCraft(action, xOnCraft.toInt(), yOnCraft.toInt())
+ } else if (isInsideCraftResult) {
+ val selectedItem = window.selectedItem
+ if (selectedItem == null || selectedItem.item.isNone() ||
+ (selectedItem.item == window.craftResult?.item && selectedItem.amount + (window.craftResult?.amount ?: 0) <= selectedItem.item.params.maxStack)) {
+ for (i in window.craftingItems.indices) {
+ if ((window.craftingItems[i]?.amount ?: 0) > 1) {
+ window.craftingItems[i]?.amount = window.craftingItems[i]?.amount!! - 1
+ } else {
+ window.craftingItems[i] = null
+ }
+ }
+ if (selectedItem != null && !selectedItem.item.isNone()) {
+ selectedItem.amount += (window.craftResult?.amount ?: 0)
+ } else {
+ window.selectedItem = window.craftResult
+ }
+ window.craftResult = gameItemsHolder.craftItem(window.craftingItems
+ .map { it?.item ?: gameItemsHolder.fallbackItem })
+ }
+ }
+
+ }
+
+ companion object {
+ private const val TAG = "SelectSurvivalInventoryItemMouseInputHandler"
+
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/UseItemMouseInputHandler.kt b/core/src/ru/deadsoftware/cavedroid/game/input/handler/mouse/UseItemMouseInputHandler.kt
--- /dev/null
@@ -0,0 +1,109 @@
+package ru.deadsoftware.cavedroid.game.input.handler.mouse
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.utils.Timer
+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.useitem.IUseItemAction
+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.isInsideHotbar
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import javax.inject.Inject
+
+@GameScope
+class UseItemMouseInputHandler @Inject constructor(
+ private val mobsController: MobsController,
+ private val useItemActionMap: Map<String, @JvmSuppressWildcards IUseItemAction>,
+ private val placeBlockActionMap: Map<String, @JvmSuppressWildcards IPlaceBlockAction>,
+ private val gameWindowsManager: GameWindowsManager,
+) : IGameInputHandler<MouseInputAction> {
+
+ private var buttonHoldTask: Timer.Task? = null
+
+ override fun checkConditions(action: MouseInputAction): Boolean {
+ return buttonHoldTask?.isScheduled == true ||
+ !isInsideHotbar(action) &&
+ gameWindowsManager.getCurrentWindow() == GameUiWindow.NONE &&
+ action.actionKey is MouseInputActionKey.Right
+ }
+
+ private fun cancelHold() {
+ buttonHoldTask?.cancel()
+ buttonHoldTask = null
+ }
+
+ private fun handleHold(action: MouseInputAction) {
+ cancelHold()
+
+ val player = mobsController.player
+ val item = player.currentItem.item
+ player.startHitting(false)
+ player.stopHitting()
+
+ if (item is Item.Placeable) {
+ placeBlockActionMap.placeToBackgroundAction(
+ item = player.currentItem.item as Item.Placeable,
+ x = player.cursorX,
+ y = player.cursorY
+ )
+ }
+ }
+
+ private fun handleDown(action: MouseInputAction) {
+ cancelHold()
+ buttonHoldTask = object : Timer.Task() {
+ override fun run() {
+ handleHold(action)
+ }
+
+ }
+ Timer.schedule(buttonHoldTask, TOUCH_HOLD_TIME_SEC)
+ }
+
+ private fun handleUp(action: MouseInputAction) {
+ val player = mobsController.player
+ val item = player.currentItem.item
+ cancelHold()
+
+ player.startHitting(false)
+ player.stopHitting()
+
+ if (item is Item.Placeable) {
+ placeBlockActionMap.placeToForegroundAction(
+ item = item,
+ x = player.cursorX,
+ y = player.cursorY
+ )
+ } 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");
+ }
+ }
+
+ override fun handle(action: MouseInputAction) {
+ if (action.actionKey !is MouseInputActionKey.Right) {
+ if (buttonHoldTask?.isScheduled == true) {
+ cancelHold()
+ }
+ return
+ }
+
+ if (action.actionKey.touchUp && buttonHoldTask?.isScheduled == true) {
+ handleUp(action)
+ } else if (!action.actionKey.touchUp) {
+ handleDown(action)
+ }
+ }
+
+ companion object {
+ private const val TAG = "UseItemMouseInputActionHandler"
+ private const val TOUCH_HOLD_TIME_SEC = 0.5f
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/mapper/KeyboardInputActionMapper.kt b/core/src/ru/deadsoftware/cavedroid/game/input/mapper/KeyboardInputActionMapper.kt
--- /dev/null
@@ -0,0 +1,36 @@
+package ru.deadsoftware.cavedroid.game.input.mapper
+
+import com.badlogic.gdx.Input
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.KeyboardInputActionKey
+import javax.inject.Inject
+
+@GameScope
+class KeyboardInputActionMapper @Inject constructor() {
+
+ fun map(key: Int, isKeyDown: Boolean): KeyboardInputAction? {
+ 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.S -> KeyboardInputActionKey.Down
+
+ Input.Keys.E -> KeyboardInputActionKey.OpenInventory
+ Input.Keys.ALT_LEFT -> KeyboardInputActionKey.SwitchControlsMode
+
+ Input.Keys.ESCAPE, Input.Keys.BACK -> KeyboardInputActionKey.Pause
+
+ Input.Keys.F1 -> KeyboardInputActionKey.ShowDebug
+ Input.Keys.GRAVE -> KeyboardInputActionKey.SwitchGameMode
+ Input.Keys.M -> KeyboardInputActionKey.ShowMap
+
+ Input.Keys.T -> KeyboardInputActionKey.OpenCraft
+
+ else -> null
+ }
+
+ return actionKey?.let { KeyboardInputAction(it, isKeyDown) }
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/input/mapper/MouseInputActionMapper.kt b/core/src/ru/deadsoftware/cavedroid/game/input/mapper/MouseInputActionMapper.kt
--- /dev/null
@@ -0,0 +1,80 @@
+package ru.deadsoftware.cavedroid.game.input.mapper
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.Input
+import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction
+import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey
+import javax.inject.Inject
+
+@GameScope
+class MouseInputActionMapper @Inject constructor(
+ val mainConfig: MainConfig,
+) {
+
+ fun map(
+ mouseX: Float,
+ mouseY: Float,
+ cameraViewport: Rectangle,
+ button: Int,
+ touchUp: Boolean
+ ): MouseInputAction? {
+ val actionKey = mapActionKey(button, touchUp) ?: return null
+
+ return MouseInputAction(
+ screenX = getScreenX(mouseX),
+ screenY = getScreenY(mouseY),
+ actionKey = actionKey,
+ cameraViewport = cameraViewport,
+ )
+ }
+
+ fun mapDragged(
+ mouseX: Float,
+ mouseY: Float,
+ cameraViewport: Rectangle,
+ ): MouseInputAction {
+ return MouseInputAction(
+ screenX = getScreenX(mouseX),
+ screenY = getScreenY(mouseY),
+ actionKey = MouseInputActionKey.Dragged,
+ cameraViewport = cameraViewport,
+ )
+ }
+
+ fun mapScrolled(
+ mouseX: Float,
+ mouseY: Float,
+ amountX: Float,
+ amountY: Float,
+ cameraViewport: Rectangle,
+ ): MouseInputAction {
+ return MouseInputAction(
+ screenX = getScreenX(mouseX),
+ screenY = getScreenY(mouseY),
+ actionKey = MouseInputActionKey.Scroll(amountX, amountY),
+ cameraViewport = cameraViewport,
+ )
+ }
+
+ private fun mapActionKey(button: Int, touchUp: Boolean): 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)
+ else -> null
+ }
+ }
+
+ private fun getScreenX(mouseX: Float): Float {
+ return mouseX * (mainConfig.width / Gdx.graphics.width)
+ }
+
+ private fun getScreenY(mouseY: Float): Float {
+ return mouseY * (mainConfig.height / Gdx.graphics.height)
+ }
+
+}
\ 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
index 23cf1dcc836de3b434aa7e9deea52ca53b691568..641ffe554ec84255bbe0b1099b3f67a9aded0464 100644 (file)
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.GameItems;
-import ru.deadsoftware.cavedroid.game.GameWorld;
+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,
*/
public class FallingGravel extends Mob {
+ private static final String TAG = "FallingGravel";
+
/**
* Creates a FallingGravel mob at coordinates
*
* @param y Y in pixels
*/
public FallingGravel(float x, float y) {
- super(x, y, 16, 16, Direction.LEFT, Type.GRAVEL);
- mMove = new Vector2(0, 1);
+ 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) {
- if (mMove.isZero()) {
- gameWorld.setForeMap(getMapX(), getMiddleMapY(), 11);
+ public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+ if (mVelocity.isZero()) {
+ gameWorld.setForeMap(getMapX(), getUpperMapY(), gameItemsHolder.getBlock("gravel"));
kill();
}
}
}
@Override
- public void draw(SpriteBatch spriteBatch, float x, float y) {
- spriteBatch.draw(GameItems.getBlockTex(11), x, y);
+ 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
index 9a383a21315cce839145870231c0e6f9bbb8cb35..f41da96872c12aa7fc32ce6dffc885822d13935d 100644 (file)
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.GameItems;
-import ru.deadsoftware.cavedroid.game.GameWorld;
+import ru.deadsoftware.cavedroid.game.GameItemsHolder;
+import ru.deadsoftware.cavedroid.game.world.GameWorld;
+import ru.deadsoftware.cavedroid.misc.Assets;
+
+import javax.annotation.CheckForNull;
/**
*/
public class FallingSand extends Mob {
+ private static final String TAG = "FallingSand";
+
/**
* Creates a FallingSand mob at coordinates
*
* @param y Y in pixels
*/
public FallingSand(float x, float y) {
- super(x, y, 16, 16, Direction.LEFT, Type.SAND);
- mMove = new Vector2(0, 1);
+ 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) {
- if (mMove.isZero()) {
- gameWorld.setForeMap(getMapX(), getMiddleMapY(), 10);
+ public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+ if (mVelocity.isZero()) {
+ gameWorld.setForeMap(getMapX(), getUpperMapY(), gameItemsHolder.getBlock("sand"));
kill();
}
}
}
@Override
- public void draw(SpriteBatch spriteBatch, float x, float y) {
- spriteBatch.draw(GameItems.getBlockTex(10), x, y);
+ 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);
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java
index 0f765925bc1679bed83830f29011a5f0bd88def8..415ef30376503a491a3eefd9eb8c127d53089b77 100644 (file)
package ru.deadsoftware.cavedroid.game.mobs;
+import com.badlogic.gdx.Gdx;
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 ru.deadsoftware.cavedroid.game.GameWorld;
+import ru.deadsoftware.cavedroid.game.GameItemsHolder;
+import ru.deadsoftware.cavedroid.game.world.GameWorld;
import java.io.Serializable;
*/
public abstract class Mob extends Rectangle implements Serializable {
+ protected static int ANIMATION_SPEED = 360;
+
public enum Type {
MOB,
SAND,
}
public enum Direction {
- LEFT,
- RIGHT
+
+ LEFT(0, -1),
+ RIGHT(1, 1);
+
+ private final int index;
+ private final int basis;
+
+ /**
+ * Index for this direction (left = 0, right = 1)
+ */
+ public final int getIndex() {
+ return index;
+ }
+
+ /**
+ * Basis for this direction (left = -1, right = 1)
+ */
+ public final int getBasis() {
+ return basis;
+ }
+
+ Direction(int index, int basis) {
+ this.index = index;
+ this.basis = basis;
+ }
}
- protected Vector2 mMove;
+ protected Vector2 mVelocity;
protected Type mType;
- protected int mAnimDelta = 6;
- protected int mAnim;
+ protected int mAnimDelta = ANIMATION_SPEED;
+ protected float mAnim;
private Direction mDirection;
- private boolean mDead;
+ protected boolean mDead;
private boolean mCanJump;
private boolean mFlyMode;
+ private final int mMaxHealth;
+ private int mHealth;
+
/**
* @param x in pixels
* @param y in pixels
* @param height in pixels
* @param mDirection Direction in which mob is looking
*/
- protected Mob(float x, float y, float width, float height, Direction mDirection, Type type) {
+ protected Mob(float x, float y, float width, float height, Direction mDirection, Type type, int maxHealth) {
super(x, y, width, height);
- mMove = new Vector2(0, 0);
+ mVelocity = new Vector2(0, 0);
mCanJump = false;
mDead = false;
this.mDirection = mDirection;
this.mType = type;
+ this.mMaxHealth = maxHealth;
+ this.mHealth = mMaxHealth;
}
protected static Direction randomDir() {
return MathUtils.randomBoolean(.5f) ? Direction.LEFT : Direction.RIGHT;
}
+ private boolean isAnimationIncreasing() {
+ return mAnim > 0 && mAnimDelta > 0 || mAnim < 0 && mAnimDelta < 0;
+ }
+
+ private void checkHealth() {
+ mHealth = MathUtils.clamp(mHealth, 0, mMaxHealth);
+
+ if (mHealth <= 0) {
+ kill();
+ }
+ }
+
+ protected final void updateAnimation(float delta) {
+ if (mVelocity.x != 0f || Math.abs(mAnim) > mAnimDelta * delta) {
+ mAnim += mAnimDelta * delta;
+ } else {
+ mAnim = 0;
+ }
+
+ if (mAnim > 60f) {
+ mAnim = 60f;
+ mAnimDelta = -ANIMATION_SPEED;
+ } else if (mAnim < -60f) {
+ mAnim = -60f;
+ mAnimDelta = ANIMATION_SPEED;
+ }
+
+ if (mVelocity.x == 0f && isAnimationIncreasing()) {
+ mAnimDelta = -mAnimDelta;
+ }
+ }
+
/**
* @return The X coordinate of a mob in blocks
*/
mDirection = looksLeft() ? Direction.RIGHT : Direction.LEFT;
}
- protected final int dirMultiplier() {
- return looksLeft() ? 0 : 1;
- }
-
public final boolean isDead() {
return mDead;
}
- public final int getAnim() {
+ public final float getAnim() {
return mAnim;
}
mDead = true;
}
- public final void move() {
- x += mMove.x;
- y += mMove.y;
+ public final void move(float delta) {
+ x += mVelocity.x * delta;
+ y += mVelocity.y * delta;
+ }
+
+ public final Vector2 getVelocity() {
+ return mVelocity;
}
- public final Vector2 getMove() {
- return mMove;
+ protected final void setVelocity(Vector2 velocity) {
+ mVelocity = velocity;
}
public final boolean canJump() {
return mType;
}
- public void checkWorldBounds(GameWorld gameWorld) {
+ public final void checkWorldBounds(GameWorld gameWorld) {
if (x + width / 2 < 0) {
x += gameWorld.getWidthPx();
}
}
}
- public abstract void draw(SpriteBatch spriteBatch, float x, float y);
+ public final int getHealth() {
+ return mHealth;
+ }
+
+ public final void attachToController(MobsController controller) {
+ controller.addMob(this);
+ }
+
+ public void damage(int damage) {
+ if (damage == 0) {
+ return;
+ }
+
+ if (damage < 0) {
+ Gdx.app.error(this.getClass().getSimpleName(), "Damage cant be negative!");
+ return;
+ }
- public abstract void ai(GameWorld gameWorld);
+ if (mHealth <= Integer.MIN_VALUE + damage) {
+ mHealth = Integer.MIN_VALUE + damage;
+ }
+
+ mHealth -= damage;
+ checkHealth();
+ }
+
+ public void heal(int heal) {
+ if (heal < 0) {
+ Gdx.app.error(this.getClass().getSimpleName(), "Heal cant be negative!");
+ return;
+ }
+
+ if (mHealth >= Integer.MAX_VALUE - heal) {
+ mHealth = Integer.MAX_VALUE - heal;
+ }
+
+ mHealth += heal;
+ checkHealth();
+ }
+
+ 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 changeDir();
+
+ public abstract float getSpeed();
+
+ public abstract void jump();
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package ru.deadsoftware.cavedroid.game.mobs;
-
-import com.badlogic.gdx.Gdx;
-import ru.deadsoftware.cavedroid.game.GameScope;
-
-import javax.inject.Inject;
-import java.io.Serializable;
-import java.util.Iterator;
-import java.util.LinkedList;
-
-@GameScope
-public class MobsController implements Serializable {
-
- public interface Callback {
- void run(Mob mob);
- }
-
- private static final String TAG = "MobsController";
-
- private final Player mPlayer;
- private final LinkedList<Mob> mMobs = new LinkedList<>();
-
- @Inject
- public MobsController() {
- mPlayer = new Player();
- }
-
- public Player getPlayer() {
- return mPlayer;
- }
-
- public void addMob(Class<? extends Mob> mobClass, float x, float y) {
- try {
- mMobs.add(mobClass.getConstructor(float.class, float.class).newInstance(x, y));
- } catch (Exception e) {
- Gdx.app.error(TAG, e.getMessage());
- }
- }
-
- public int getSize() {
- return mMobs.size();
- }
-
- public void forEach(Callback callback) {
- mMobs.forEach(callback::run);
- }
-
- public Iterator<Mob> getIterator() {
- return mMobs.iterator();
- }
-
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.kt
--- /dev/null
@@ -0,0 +1,28 @@
+package ru.deadsoftware.cavedroid.game.mobs
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import java.io.Serializable
+import java.util.*
+import javax.inject.Inject
+
+@GameScope
+class MobsController @Inject constructor(
+ gameItemsHolder: GameItemsHolder
+) : Serializable {
+
+ private val _mobs = LinkedList<Mob>()
+
+ val player: Player = Player(gameItemsHolder)
+
+ val mobs: List<Mob>
+ get() = _mobs
+
+ fun addMob(mob: Mob) {
+ _mobs.add(mob)
+ }
+
+ companion object {
+ private const val TAG = "MobsController"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.java
+++ /dev/null
@@ -1,62 +0,0 @@
-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.GameWorld;
-import ru.deadsoftware.cavedroid.misc.Assets;
-
-import static ru.deadsoftware.cavedroid.misc.Assets.pigSprite;
-
-public class Pig extends Mob {
-
- public Pig(float x, float y) {
- super(x, y, 25, 18, randomDir(), Type.MOB);
- mMove = new Vector2(looksLeft() ? -1 : 1, 0);
- }
-
- @Override
- public void changeDir() {
- switchDir();
- mMove.x = -1 + 2 * dirMultiplier();
- }
-
- @Override
- public void ai(GameWorld gameWorld) {
- if (MathUtils.randomBoolean(.0025f)) {
- if (mMove.x != 0f) {
- mMove.x = 0;
- } else {
- changeDir();
- }
- }
-
- if (mMove.x != 0f) {
- mAnim += mAnimDelta;
- } else {
- mAnim = 0;
- }
-
- if (mAnim >= 60 || mAnim <= -60) {
- mAnimDelta = -mAnimDelta;
- }
- }
-
- @Override
- public void draw(SpriteBatch spriteBatch, float x, float y) {
- pigSprite[0][1].setRotation(getAnim());
- pigSprite[1][1].setRotation(-getAnim());
- //back legs
- pigSprite[1][1].setPosition(x + (9 - dirMultiplier() * 9), y + 12);
- pigSprite[1][1].draw(spriteBatch);
- pigSprite[1][1].setPosition(x + 21 - (9 * dirMultiplier()), y + 12);
- pigSprite[1][1].draw(spriteBatch);
- //head & body
- spriteBatch.draw(Assets.pigSprite[dirMultiplier()][0], x, y);
- //front legs
- pigSprite[0][1].setPosition(x + (9 - dirMultiplier() * 9), y + 12);
- pigSprite[0][1].draw(spriteBatch);
- pigSprite[0][1].setPosition(x + 21 - (9 * dirMultiplier()), y + 12);
- pigSprite[0][1].draw(spriteBatch);
- }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.kt b/core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.kt
--- /dev/null
@@ -0,0 +1,63 @@
+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.misc.utils.drawSprite
+import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getBackgroundLeg
+import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getBody
+import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getForegroundLeg
+import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getLeftLegRelativeX
+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) {
+
+ override fun getSpeed(): Float {
+ return SPEED
+ }
+
+ override fun changeDir() {
+ switchDir()
+ velocity = Vector2(direction.basis * speed, 0f)
+ }
+
+ 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 draw(spriteBatch: SpriteBatch, x: Float, y: Float, delta: Float) {
+ updateAnimation(delta)
+
+ val leftLegX = x + getLeftLegRelativeX(direction)
+ 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)
+ }
+
+
+ private companion object {
+ private const val WIDTH = 25f
+ private const val HEIGHT = 18f
+ private const val SPEED = 69.072f
+ private const val JUMP_VELOCITY = -133.332f
+ private const val MAX_HEALTH = 10;
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/Player.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/Player.java
index 0b7ab8bc8148c48a24b02a3d7d266ec43e7cd3de..0e8af6e0eed0407b1cc3a4be057ae2dc344f5b87 100644 (file)
package ru.deadsoftware.cavedroid.game.mobs;
+import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
+import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
-import ru.deadsoftware.cavedroid.game.GameWorld;
+import ru.deadsoftware.cavedroid.game.GameItemsHolder;
+import ru.deadsoftware.cavedroid.game.model.block.Block;
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
+import ru.deadsoftware.cavedroid.game.model.item.Item;
+import ru.deadsoftware.cavedroid.game.objects.Drop;
+import ru.deadsoftware.cavedroid.game.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 {
- public final int[] inventory;
+ private static final float SPEED = 69.072f;
+ private static final float JUMP_VELOCITY = -133.332f;
+ private static final int MAX_HEALTH = 20;
+
+ private boolean hitting = false, hittingWithDamage = false;
+ private float hitAnim = 0f;
+ private float hitAnimDelta = ANIMATION_SPEED;
+
+ public final ArrayList<InventoryItem> inventory;
public int slot;
- public final int gameMode;
+ public int gameMode;
public boolean swim;
+ public float headRotation = 0f;
+
+ public float blockDamage = 0f;
+ public int cursorX = 0;
+ public int cursorY = 0;
+
+ @CheckForNull
+ private Vector2 spawnPoint = null;
+
+ public ControlMode controlMode = ControlMode.WALK;
+
+ public enum ControlMode {
+ WALK,
+ CURSOR
+ }
- public Player() {
- super(0, 0, 4, 30, randomDir(), Type.MOB);
- this.gameMode = 1;
- inventory = new int[9];
+ public Player(GameItemsHolder gameItemsHolder) {
+ 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());
+ }
swim = false;
}
- public void respawn(GameWorld gameWorld) {
- Vector2 pos = getSpawnPoint(gameWorld);
+ public void initInventory(GameItemsHolder gameItemsHolder) {
+ for (InventoryItem invItem : inventory) {
+ invItem.init(gameItemsHolder);
+ }
+ }
+
+ @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;
this.y = pos.y;
- mMove.setZero();
+ mVelocity.setZero();
+ mDead = false;
+ heal(MAX_HEALTH);
+ }
+
+ public void decreaseCurrentItemCount(GameItemsHolder gameItemsHolder) {
+ if (gameMode == 1) {
+ return;
+ }
+ getCurrentItem().setAmount(getCurrentItem().getAmount() - 1);
+ if (getCurrentItem().getAmount() <= 0) {
+ setCurrentInventorySlotItem(gameItemsHolder.getFallbackItem());
+ }
+ }
+
+ public InventoryItem getCurrentItem() {
+ return inventory.get(slot);
}
- private Vector2 getSpawnPoint(GameWorld gameWorld) {
- int y;
- for (y = 0; y < gameWorld.getHeight(); y++) {
- if (y == gameWorld.getHeight() - 1) {
- y = 60;
- gameWorld.setForeMap(0, y, 1);
+ 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;
+ }
+ }
+
+ 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;
}
- if (gameWorld.hasForeAt(0, y) && gameWorld.getForeMapBlock(0, y).hasCollision()) {
+ }
+ }
+
+ private Vector2 getSpawnPoint(GameWorld gameWorld, GameItemsHolder itemsHolder) {
+ if (spawnPoint != null) {
+ return spawnPoint;
+ }
+
+ int y, x = gameWorld.getWidth() / 2;
+ for (y = 0; y <= gameWorld.getWorldConfig().getSeaLevel(); y++) {
+ if (y == gameWorld.getWorldConfig().getSeaLevel()) {
+ for (x = 0; x < gameWorld.getWidth(); x++) {
+ if (gameWorld.getForeMap(x, y).getParams().getHasCollision()) {
+ break;
+ }
+ if (x == gameWorld.getWidth() - 1) {
+ gameWorld.setForeMap(x, y, itemsHolder.getBlock("grass"));
+ break;
+ }
+ }
+ break;
+ }
+ if (gameWorld.hasForeAt(x, y) && gameWorld.getForeMap(x, y).hasCollision()) {
break;
}
}
- return new Vector2(8 - getWidth() / 2, (float) y * 16 - getHeight());
+ spawnPoint = new Vector2(x * 16 + 8 - getWidth() / 2, (float) y * 16 - getHeight());
+ return spawnPoint;
}
public void setDir(Direction dir) {
}
}
+ public void setCurrentInventorySlotItem(Item item) {
+ inventory.set(slot, item.toInventoryItem());
+ }
+
@Override
- public void ai(GameWorld gameWorld) {
+ public float getSpeed() {
+ return SPEED;
+ }
+
+ @Override
+ public void jump() {
+ mVelocity.y = JUMP_VELOCITY;
+ }
+
+ private void hitBlock(GameWorld gameWorld, GameItemsHolder gameItemsHolder) {
+ if (!hitting || !hittingWithDamage) {
+ return;
+ }
+
+ 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 (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;
+ }
+ } else {
+ if (!foregroundBlock.isNone()) {
+ gameWorld.placeToForeground(cursorX, cursorY, gameItemsHolder.getFallbackBlock());
+ } else if (!backgroundBlock.isNone()) {
+ gameWorld.placeToBackground(cursorX, cursorY, gameItemsHolder.getFallbackBlock());
+ }
+ stopHitting();
+ }
+ } else {
+ stopHitting();
+ }
+ }
+
+ @Override
+ public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+ updateAnimation(delta);
+ hitBlock(gameWorld, gameItemsHolder);
+
+ if (gameMode == 1) {
+ return;
+ }
+
+ final Block foregroundBlock = gameWorld.getForeMap(cursorX, cursorY);
+ final Block backgroundBlock = gameWorld.getBackMap(cursorX, cursorY);
+ @CheckForNull final Block target;
+
+ if (!foregroundBlock.isNone() && foregroundBlock.getParams().getHitPoints() >= 0) {
+ target = foregroundBlock;
+ } else if (!backgroundBlock.isNone() && backgroundBlock.getParams().getHitPoints() >= 0) {
+ target = backgroundBlock;
+ } else {
+ target = null;
+ }
+
+ final boolean canHitBlock = target != null;
+
+ float multiplier = 1f;
+ final Item currentItem = inventory.get(slot).getItem();
+ if (currentItem instanceof Item.Tool && canHitBlock) {
+ if (target.getParams().getToolType() == currentItem.getClass()
+ && ((Item.Tool)currentItem).getLevel() >= target.getParams().getToolLevel()) {
+ multiplier = 2f * ((Item.Tool)currentItem).getLevel();
+ }
+ multiplier *= ((Item.Tool)currentItem).getBlockDamageMultiplier();
+ }
+
+ if (hitting && hittingWithDamage && canHitBlock) {
+ blockDamage += 60f * delta * multiplier;
+ } else {
+ blockDamage = 0f;
+ }
}
@Override
}
@Override
- public void draw(SpriteBatch spriteBatch, float x, float y) {
- if (mMove.x != 0 || Assets.playerSprite[0][2].getRotation() != 0) {
- Assets.playerSprite[0][2].rotate(mAnimDelta);
- Assets.playerSprite[1][2].rotate(-mAnimDelta);
- Assets.playerSprite[0][3].rotate(-mAnimDelta);
- Assets.playerSprite[1][3].rotate(mAnimDelta);
+ public void damage(int damage) {
+ if (gameMode == 1) {
+ return;
+ }
+
+ if (damage > 0) {
+ getVelocity().y += JUMP_VELOCITY / 3f;
+ }
+
+ super.damage(damage);
+ }
+
+ @Override
+ public void heal(int heal) {
+ if (gameMode == 1) {
+ return;
+ }
+ super.heal(heal);
+ }
+
+ private void drawItem(SpriteBatch spriteBatch, float x, float y, float anim) {
+ final Item item = inventory(slot);
+
+ if (item == null || item.isNone()) {
+ return;
+ }
+
+ final Sprite sprite = item.getSprite();
+ final boolean smallSprite = !item.isTool() || item.isShears();
+
+ final float originalWidth = sprite.getWidth();
+ final float originalHeight = sprite.getHeight();
+
+ if (smallSprite) {
+ sprite.setSize(Drop.DROP_SIZE, Drop.DROP_SIZE);
+ }
+
+ final float handLength = Assets.playerSprite[0][2].getHeight();
+
+ final SpriteOrigin spriteOrigin = item.getParams().getInHandSpriteOrigin();
+ final int handMultiplier = -getDirection().getBasis();
+ final float xOffset = (-1 + getDirection().getIndex()) * sprite.getWidth() + 4 + handMultiplier * (sprite.getWidth() * spriteOrigin.getX());
+ final float yOffset = !smallSprite ? -sprite.getHeight() / 2 : 0;
+
+ float rotate = anim + 30;
+
+ if (item.isTool()) {
+ sprite.rotate90(looksLeft());
+ }
+
+ final float itemX = x + handLength * MathUtils.sin(handMultiplier * anim * MathUtils.degRad) + xOffset;
+ final float itemY = y + handLength * MathUtils.cos(handMultiplier * anim * MathUtils.degRad) + yOffset;
+
+ if (looksLeft()) {
+ sprite.setFlip(!item.isTool(), sprite.isFlipY());
+ SpriteUtilsKt.applyOrigin(sprite, spriteOrigin.getFlipped(true, false));
+ } else {
+ sprite.setFlip(item.isTool(), sprite.isFlipY());
+ SpriteUtilsKt.applyOrigin(sprite, spriteOrigin);
+ }
+
+ sprite.setRotation(-handMultiplier * rotate);
+ sprite.setPosition(itemX, itemY);
+ sprite.draw(spriteBatch);
+
+ // dont forget to reset
+ sprite.setFlip(false, sprite.isFlipY());
+ sprite.setRotation(0);
+ sprite.setOriginCenter();
+ sprite.setSize(originalWidth, originalHeight);
+ if (item.isTool()) {
+ sprite.rotate90(looksRight());
+ }
+ }
+
+ public void startHitting(boolean withDamage) {
+ if (hitting) {
+ return;
+ }
+
+ hitting = true;
+ hittingWithDamage = withDamage;
+ hitAnim = 90f;
+ hitAnimDelta = ANIMATION_SPEED;
+ }
+
+ public void startHitting() {
+ startHitting(true);
+ }
+
+ public void stopHitting() {
+ blockDamage = 0f;
+ hitting = false;
+ }
+
+ private float getRightHandAnim(float delta) {
+ hitAnim -= hitAnimDelta * delta;
+
+ if (hitAnim < 30f || hitAnim > 90f) {
+ if (hitting) {
+ hitAnim = MathUtils.clamp(hitAnim, 30f, 90f);
+ hitAnimDelta = -hitAnimDelta;
+ } else {
+ hitAnimDelta = ANIMATION_SPEED;
+ }
+ }
+
+ if (!hitting) {
+ if (hitAnim < hitAnimDelta * delta) {
+ hitAnim = 0;
+ hitAnimDelta = 0;
+ return -mAnim;
+ }
+ }
+
+ return hitAnim;
+ }
+
+ @Override
+ public void draw(SpriteBatch spriteBatch, float x, float y, float delta) {
+ final Sprite backHand = Assets.playerSprite[1][2];
+ final Sprite backLeg = Assets.playerSprite[1][3];
+ final Sprite frontLeg = Assets.playerSprite[0][3];
+ final Sprite head = Assets.playerSprite[getDirection().getIndex()][0];
+ final Sprite body = Assets.playerSprite[getDirection().getIndex()][1];
+ final Sprite frontHand = Assets.playerSprite[0][2];
+
+ float backHandAnim, frontHandAnim;
+
+ final float rightHandAnim = getRightHandAnim(delta);
+
+ if (looksLeft()) {
+ backHandAnim = rightHandAnim;
+ frontHandAnim = mAnim;
} else {
- Assets.playerSprite[0][2].setRotation(0);
- Assets.playerSprite[1][2].setRotation(0);
- Assets.playerSprite[0][3].setRotation(0);
- Assets.playerSprite[1][3].setRotation(0);
- }
- if (Assets.playerSprite[0][2].getRotation() >= 60 || Assets.playerSprite[0][2].getRotation() <= -60) {
- mAnimDelta = -mAnimDelta;
- }
-
- //back hand
- Assets.playerSprite[1][2].setPosition(x + 2, y + 8);
- Assets.playerSprite[1][2].draw(spriteBatch);
- //back leg
- Assets.playerSprite[1][3].setPosition(x + 2, y + 20);
- Assets.playerSprite[1][3].draw(spriteBatch);
- //front leg
- Assets.playerSprite[0][3].setPosition(x + 2, y + 20);
- Assets.playerSprite[0][3].draw(spriteBatch);
- //head
- spriteBatch.draw(Assets.playerSprite[dirMultiplier()][0], x, y);
- //body
- spriteBatch.draw(Assets.playerSprite[dirMultiplier()][1], x + 2, y + 8);
- //front hand
- Assets.playerSprite[0][2].setPosition(x + 2, y + 8);
- Assets.playerSprite[0][2].draw(spriteBatch);
+ backHandAnim = -mAnim;
+ frontHandAnim = -rightHandAnim;
+ }
+
+ SpriteUtilsKt.drawSprite(spriteBatch, backHand, x + 2, y + 8, backHandAnim);
+
+ if (looksLeft()) {
+ drawItem(spriteBatch, x, y, -backHandAnim);
+ }
+
+ SpriteUtilsKt.drawSprite(spriteBatch, backLeg, x + 2, y + 20, mAnim);
+ SpriteUtilsKt.drawSprite(spriteBatch, frontLeg, x + 2, y + 20, -mAnim);
+ SpriteUtilsKt.drawSprite(spriteBatch, head, x, y, headRotation);
+ SpriteUtilsKt.drawSprite(spriteBatch, body, x + 2, y + 8);
+
+ if (looksRight()) {
+ drawItem(spriteBatch, x, y, frontHandAnim);
+ }
+
+ SpriteUtilsKt.drawSprite(spriteBatch, frontHand, x + 2, y + 8, frontHandAnim);
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/block/Block.kt b/core/src/ru/deadsoftware/cavedroid/game/model/block/Block.kt
--- /dev/null
@@ -0,0 +1,180 @@
+package ru.deadsoftware.cavedroid.game.model.block
+
+import com.badlogic.gdx.graphics.g2d.Sprite
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import com.badlogic.gdx.math.Rectangle
+import com.badlogic.gdx.utils.TimeUtils
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.misc.utils.colorFromHexString
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.contract
+
+@OptIn(ExperimentalContracts::class)
+sealed class Block {
+
+ abstract val params: CommonBlockParams
+
+ val width: Float get() = 16f - params.collisionMargins.left - params.collisionMargins.right
+ val height: Float get() = 16f - params.collisionMargins.top - params.collisionMargins.bottom
+
+ 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
+
+ private var _sprite: Sprite? = null
+ get() {
+ return animation?.get(currentAnimationFrame) ?: field
+ }
+
+ val sprite: Sprite
+ get() = requireNotNull(_sprite) { "null sprite for block '${params.key}'" }
+
+ private val currentAnimationFrame: Int
+ get() {
+ return params.animationInfo?.let { animInfo ->
+ ((TimeUtils.millis() / ANIMATION_FRAME_DURATION_MS) % animInfo.framesCount).toInt()
+ } ?: 0
+ }
+
+ override fun hashCode(): Int {
+ return params.key.hashCode()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return params.key == (other as Item).params.key
+ }
+
+ fun initialize() {
+ initAnimation()
+ initSprite()
+ }
+
+ private fun initAnimation() {
+ animation = params.animationInfo?.let { animInfo ->
+ requireNotNull(params.texture) { "Cannot derive animation frames from null sprite" }
+ Array(animInfo.framesCount) { y ->
+ val width = 16 - params.spriteMargins.left - params.spriteMargins.right
+ val height = 16 - params.spriteMargins.top - params.spriteMargins.bottom
+ Sprite(params.texture, params.spriteMargins.left, 16 * y + params.spriteMargins.top, width, height)
+ .apply {
+ flip(false, true)
+ params.tint?.let { tint -> color = colorFromHexString(tint) }
+ }
+ }
+ }
+ }
+
+ private fun initSprite() {
+ _sprite = animation?.get(0) ?: params.texture?.let { tex ->
+ val width = 16 - params.spriteMargins.left - params.spriteMargins.right
+ val height = 16 - params.spriteMargins.top - params.spriteMargins.bottom
+ Sprite(tex, params.spriteMargins.left, params.spriteMargins.top, width, height)
+ .apply {
+ flip(false, true)
+ params.tint?.let { tint -> color = colorFromHexString(tint) }
+ }
+ }
+ }
+
+ fun requireSprite() = requireNotNull(sprite)
+
+ fun draw(spriter: SpriteBatch, x: Float, y: Float) {
+ sprite.apply {
+ setBounds(
+ /* x = */ x + params.spriteMargins.left,
+ /* y = */ y + params.spriteMargins.top,
+ /* width = */ spriteWidth,
+ /* height = */ spriteHeight
+ )
+ draw(spriter)
+ }
+ }
+
+ fun isFluid(): Boolean {
+ contract { returns(true) implies (this@Block is Fluid) }
+ return this is Fluid
+ }
+
+ fun isWater(): Boolean {
+ contract { returns(true) implies (this@Block is Water) }
+ return this is Water
+ }
+
+ fun isLava(): Boolean {
+ contract { returns(true) implies (this@Block is Lava) }
+ return this is Lava
+ }
+
+ fun isSlab(): Boolean {
+ contract { returns(true) implies (this@Block is Slab) }
+ return this is Slab
+ }
+
+ fun isNone(): Boolean {
+ contract { returns(true) implies (this@Block is None) }
+ return this is None
+ }
+
+ fun getRectangle(x: Int, y: Int): Rectangle {
+ return Rectangle(
+ /* x = */ x * 16f + params.collisionMargins.left,
+ /* y = */ y * 16f + params.collisionMargins.top,
+ /* width = */ width,
+ /* height = */ height
+ )
+ }
+
+ data class None(
+ override val params: CommonBlockParams
+ ) : Block()
+
+ data class Normal(
+ override val params: CommonBlockParams,
+ ) : Block()
+
+ data class Slab(
+ override val params: CommonBlockParams,
+ val fullBlockKey: String,
+ val otherPartBlockKey: String,
+ ): Block()
+
+ sealed class Fluid: Block() {
+ abstract val state: Int
+ }
+
+ data class Water(
+ override val params: CommonBlockParams,
+ override val state: Int,
+ ) : Fluid()
+
+ data class Lava(
+ override val params: CommonBlockParams,
+ override val state: Int,
+ ) : Fluid()
+
+ /* Legacy accessors below */
+
+ // collision margins
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val left: Int get() = params.collisionMargins.left
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val right: Int get() = params.collisionMargins.left
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val top: Int get() = params.collisionMargins.left
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val bottom: Int get() = params.collisionMargins.left
+
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val hp: Int get() = params.hitPoints
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val collision: Boolean get() = params.hasCollision
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val animated: Boolean get() = params.animationInfo != null
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val frames: Int get() = params.animationInfo?.framesCount ?: 0
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val drop: String get() = params.dropInfo?.itemKey ?: "none"
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun hasDrop() = params.dropInfo != null
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun toJump() = params.hasCollision && params.collisionMargins.top < 8
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun hasCollision() = params.hasCollision
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun isBackground() = params.isBackground
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun isTransparent() = params.isTransparent
+ @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun getTexture() = sprite
+
+ companion object {
+ private const val LEGACY_ACCESSOR_DEPRECATION = "legacy accessors will be removed"
+ private const val ANIMATION_FRAME_DURATION_MS = 100L
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/block/BlockAnimationInfo.kt b/core/src/ru/deadsoftware/cavedroid/game/model/block/BlockAnimationInfo.kt
--- /dev/null
@@ -0,0 +1,5 @@
+package ru.deadsoftware.cavedroid.game.model.block
+
+data class BlockAnimationInfo(
+ val framesCount: Int
+)
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/block/BlockDropInfo.kt b/core/src/ru/deadsoftware/cavedroid/game/model/block/BlockDropInfo.kt
--- /dev/null
@@ -0,0 +1,6 @@
+package ru.deadsoftware.cavedroid.game.model.block
+
+data class BlockDropInfo(
+ val itemKey: String,
+ val count: Int,
+)
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/block/BlockMargins.kt b/core/src/ru/deadsoftware/cavedroid/game/model/block/BlockMargins.kt
--- /dev/null
@@ -0,0 +1,15 @@
+package ru.deadsoftware.cavedroid.game.model.block
+
+data class BlockMargins(
+ val left: Int,
+ val top: Int,
+ val right: Int,
+ val bottom: Int
+) {
+
+ init {
+ assert(left + right < 16)
+ assert(top + bottom < 16)
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/block/CommonBlockParams.kt b/core/src/ru/deadsoftware/cavedroid/game/model/block/CommonBlockParams.kt
--- /dev/null
@@ -0,0 +1,23 @@
+package ru.deadsoftware.cavedroid.game.model.block
+
+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,
+ val dropInfo: BlockDropInfo?,
+ val hasCollision: Boolean,
+ val isBackground: Boolean,
+ val isTransparent: Boolean,
+ val requiresBlock: Boolean,
+ val animationInfo: BlockAnimationInfo?,
+ val texture: Texture?,
+ val spriteMargins: BlockMargins,
+ val toolLevel: Int,
+ val toolType: Class<out Item.Tool>?,
+ val damage: Int,
+ val tint: String?,
+)
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/craft/CraftingRecipe.kt b/core/src/ru/deadsoftware/cavedroid/game/model/craft/CraftingRecipe.kt
--- /dev/null
@@ -0,0 +1,16 @@
+package ru.deadsoftware.cavedroid.game.model.craft
+
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.model.item.Item
+
+data class CraftingRecipe(
+ val input: List<Item>,
+ val output: CraftingResult
+)
+
+data class CraftingResult(
+ val item: Item,
+ val amount: Int,
+) {
+ fun toInventoryItem() = InventoryItem(item, amount)
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/dto/BlockDto.kt b/core/src/ru/deadsoftware/cavedroid/game/model/dto/BlockDto.kt
--- /dev/null
@@ -0,0 +1,35 @@
+package ru.deadsoftware.cavedroid.game.model.dto
+
+import kotlinx.serialization.SerialName
+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,
+ @SerialName("bottom") val bottom: Int = 0,
+ @SerialName("sprite_left") val spriteLeft: Int = 0,
+ @SerialName("sprite_top") val spriteTop: Int = 0,
+ @SerialName("sprite_right") val spriteRight: Int = 0,
+ @SerialName("sprite_bottom") val spriteBottom: Int = 0,
+ @SerialName("hp") val hp: Int = -1,
+ @SerialName("collision") val collision: Boolean = true,
+ @SerialName("background") val background: Boolean = false,
+ @SerialName("transparent") val transparent: Boolean = false,
+ @SerialName("block_required") val blockRequired: Boolean = false,
+ @SerialName("drop") val drop: String,
+ @SerialName("meta") val meta: String? = null,
+ @SerialName("texture") val texture: String,
+ @SerialName("animated") val animated: Boolean = false,
+ @SerialName("frames") val frames: Int = 0,
+ @SerialName("drop_count") val dropCount: Int = 1,
+ @SerialName("full_block") val fullBlock: String? = null,
+ @SerialName("state") val state: Int? = null,
+ @SerialName("other_part") val otherPart: String? = null,
+ @SerialName("tool_level") val toolLevel: Int = 0,
+ @SerialName("tool_type") val toolType: String? = null,
+ @SerialName("damage") val damage: Int = 0,
+ @SerialName("tint") val tint: String? = null,
+)
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/dto/CraftingDto.kt b/core/src/ru/deadsoftware/cavedroid/game/model/dto/CraftingDto.kt
--- /dev/null
@@ -0,0 +1,9 @@
+package ru.deadsoftware.cavedroid.game.model.dto
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class CraftingDto(
+ val input: List<String>,
+ val count: Int = 1,
+)
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/dto/GameItemsDto.kt b/core/src/ru/deadsoftware/cavedroid/game/model/dto/GameItemsDto.kt
--- /dev/null
@@ -0,0 +1,11 @@
+package ru.deadsoftware.cavedroid.game.model.dto
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.*
+
+@Serializable
+data class GameItemsDto(
+ @SerialName("blocks") val blocks: Map<String, BlockDto>,
+ @SerialName("items") val items: Map<String, ItemDto>,
+)
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/dto/ItemDto.kt b/core/src/ru/deadsoftware/cavedroid/game/model/dto/ItemDto.kt
--- /dev/null
@@ -0,0 +1,21 @@
+package ru.deadsoftware.cavedroid.game.model.dto
+
+import kotlinx.serialization.SerialName
+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,
+ @SerialName("origin_x") val originX: Float = 0f,
+ @SerialName("origin_y") val origin_y: Float = 1f,
+ @SerialName("action_key") val actionKey: String? = null,
+ @SerialName("mob_damage_multiplier") val mobDamageMultiplier: Float = 1f,
+ @SerialName("block_damage_multiplier") val blockDamageMultiplier: Float = 1f,
+ @SerialName("top_slab_block") val topSlabBlock: String? = null,
+ @SerialName("bottom_slab_block") val bottomSlabBlock: String? = null,
+ @SerialName("tool_level") val toolLevel: Int? = null,
+ @SerialName("max_stack") val maxStack: Int = 64,
+)
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/item/CommonItemParams.kt b/core/src/ru/deadsoftware/cavedroid/game/model/item/CommonItemParams.kt
--- /dev/null
@@ -0,0 +1,11 @@
+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,
+)
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/item/InventoryItem.kt b/core/src/ru/deadsoftware/cavedroid/game/model/item/InventoryItem.kt
--- /dev/null
@@ -0,0 +1,90 @@
+package ru.deadsoftware.cavedroid.game.model.item
+
+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.misc.Assets
+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
+
+class InventoryItem @JvmOverloads constructor(
+ val itemKey: String,
+ var amount: Int = 1,
+) : Serializable {
+
+ @Transient
+ lateinit var item: Item
+ private set
+
+ @JvmOverloads
+ constructor(_item: Item, amount: Int = 1) : this(_item.params.key, amount) {
+ item = _item
+ }
+
+ fun init(gameItemsHolder: GameItemsHolder) {
+ if (this::item.isInitialized) {
+ return
+ }
+ item = gameItemsHolder.getItem(itemKey)
+ }
+
+ private fun drawAmountText(spriteBatch: SpriteBatch, text: String, x: Float, y: Float) {
+ spriteBatch.drawString(text, x + 1, y + 1, Color.BLACK)
+ spriteBatch.drawString(text, x, y, Color.WHITE)
+ }
+
+ fun drawSelected(spriteBatch: SpriteBatch, x: Float, y: Float) {
+ if (item.isNone()) {
+ return
+ }
+
+ val sprite = item.sprite
+ val amountString = amount.toString()
+ spriteBatch.drawSprite(sprite, x - 10f, y - 10f, rotation = 0f, width = 20f, height = 20f)
+ drawAmountText(
+ spriteBatch = spriteBatch,
+ text = amountString,
+ x = x + 10f - Assets.getStringWidth(amountString) + 1f,
+ y = y + 10f - Assets.getStringHeight(amountString) + 1f
+ )
+ }
+
+ fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, x: Float, y: Float) {
+ if (item.isNone()) {
+ return
+ }
+
+ val sprite = item.sprite
+ spriteBatch.drawSprite(sprite, x, y)
+
+ if (amount < 2) {
+ return
+ }
+
+ if (item.isTool()) {
+ spriteBatch.end()
+ shapeRenderer.begin(ShapeRenderer.ShapeType.Filled)
+ shapeRenderer.color = Color.GREEN
+ shapeRenderer.rect(
+ /* x = */ x,
+ /* y = */ y + 1.px - 2,
+ /* width = */ 1.px * (amount.toFloat() / item.params.maxStack.toFloat()),
+ /* height = */ 2f
+ )
+ shapeRenderer.end()
+ spriteBatch.begin()
+ } else {
+ val amountString = amount.toString()
+ drawAmountText(
+ spriteBatch = spriteBatch,
+ text = amountString,
+ x = x + 1.px - Assets.getStringWidth(amountString),
+ y = y + 1.px - Assets.getStringHeight(amountString)
+ )
+ }
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/item/Item.kt b/core/src/ru/deadsoftware/cavedroid/game/model/item/Item.kt
--- /dev/null
@@ -0,0 +1,144 @@
+package ru.deadsoftware.cavedroid.game.model.item
+
+import com.badlogic.gdx.graphics.g2d.Sprite
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.block.Block as BlockModel
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.contract
+
+@OptIn(ExperimentalContracts::class)
+sealed class Item {
+
+ abstract val params: CommonItemParams
+ abstract val sprite: Sprite
+
+ override fun hashCode(): Int {
+ return params.key.hashCode()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return params.key == (other as Item).params.key
+ }
+
+ fun isNone(): Boolean {
+ contract { returns(true) implies (this@Item is None) }
+ return this is None
+ }
+
+ fun isPlaceable(): Boolean {
+ contract { returns(true) implies (this@Item is Placeable) }
+ return this is Placeable
+ }
+
+ fun isSlab(): Boolean {
+ contract { returns(true) implies (this@Item is Slab) }
+ return this is Slab
+ }
+
+ fun isTool(): Boolean {
+ contract { returns(true) implies (this@Item is Tool) }
+ return this is Tool
+ }
+
+ fun isShears(): Boolean {
+ contract { returns(true) implies (this@Item is Shears) }
+ return this is Shears
+ }
+
+ fun isUsable(): Boolean {
+ contract { returns(true) implies (this@Item is Placeable) }
+ return this is Placeable
+ }
+
+ @JvmOverloads
+ fun toInventoryItem(amount: Int = 1): InventoryItem {
+ return InventoryItem(this, amount)
+ }
+
+ data class Normal(
+ override val params: CommonItemParams,
+ override val sprite: Sprite
+ ) : Item()
+
+ sealed class Tool : Item() {
+ abstract val mobDamageMultiplier: Float
+ abstract val blockDamageMultiplier: Float
+ 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
+ }
+
+ data class None(
+ override val params: CommonItemParams,
+ ): Item() {
+ override val sprite: Sprite
+ get() = throw IllegalAccessException("Trying to get sprite of None")
+ }
+
+ data class Block(
+ override val params: CommonItemParams,
+ override val block: BlockModel
+ ) : Placeable()
+
+ data class Slab(
+ override val params: CommonItemParams,
+ val topPartBlock: BlockModel.Slab,
+ val bottomPartBlock: BlockModel.Slab
+ ) : Placeable() {
+ override val block get() = bottomPartBlock
+ }
+
+ data class Sword(
+ override val params: CommonItemParams,
+ override val sprite: Sprite,
+ override val mobDamageMultiplier: Float,
+ override val blockDamageMultiplier: Float,
+ override val level: Int,
+ ) : Tool()
+
+ data class Shovel(
+ override val params: CommonItemParams,
+ override val sprite: Sprite,
+ override val mobDamageMultiplier: Float,
+ override val blockDamageMultiplier: Float,
+ override val level: Int,
+ ) : Tool()
+
+ data class Axe(
+ override val params: CommonItemParams,
+ override val sprite: Sprite,
+ override val mobDamageMultiplier: Float,
+ override val blockDamageMultiplier: Float,
+ override val level: Int,
+ ) : Tool()
+
+ data class Pickaxe(
+ override val params: CommonItemParams,
+ override val sprite: Sprite,
+ override val mobDamageMultiplier: Float,
+ override val blockDamageMultiplier: Float,
+ override val level: Int,
+ ) : Tool()
+
+ data class Shears(
+ override val params: CommonItemParams,
+ override val sprite: Sprite,
+ override val mobDamageMultiplier: Float,
+ override val blockDamageMultiplier: Float,
+ override val level: Int,
+ ) : Tool()
+
+ data class Bucket(
+ override val params: CommonItemParams,
+ override val sprite: Sprite,
+ override val useActionKey: String
+ ) : Usable()
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/mapper/BlockMapper.kt b/core/src/ru/deadsoftware/cavedroid/game/model/mapper/BlockMapper.kt
--- /dev/null
@@ -0,0 +1,106 @@
+package ru.deadsoftware.cavedroid.game.model.mapper
+
+import com.badlogic.gdx.graphics.Texture
+import dagger.Reusable
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.block.*
+import ru.deadsoftware.cavedroid.game.model.block.Block.*
+import ru.deadsoftware.cavedroid.game.model.dto.BlockDto
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader
+import javax.inject.Inject
+
+@Reusable
+class BlockMapper @Inject constructor(
+ private val assetLoader: AssetLoader,
+) {
+
+ fun map(key: String, dto: BlockDto): Block {
+ val commonBlockParams = mapCommonParams(key, dto)
+
+ return when (dto.meta) {
+ "water" -> Water(commonBlockParams, requireNotNull(dto.state))
+ "lava" -> Lava(commonBlockParams, requireNotNull(dto.state))
+ "slab" -> Slab(commonBlockParams, requireNotNull(dto.fullBlock), requireNotNull(dto.otherPart))
+ "none" -> None(commonBlockParams)
+ else -> Normal(commonBlockParams)
+ }
+ }
+
+ private fun mapCommonParams(key: String, dto: BlockDto): CommonBlockParams {
+ return CommonBlockParams(
+ id = dto.id,
+ key = key,
+ collisionMargins = BlockMargins(
+ left = dto.left,
+ top = dto.top,
+ right = dto.right,
+ bottom = dto.bottom
+ ),
+ hitPoints = dto.hp,
+ dropInfo = mapBlockDropInfo(dto),
+ hasCollision = dto.collision,
+ isBackground = dto.background,
+ isTransparent = dto.transparent,
+ requiresBlock = dto.blockRequired,
+ animationInfo = mapBlockAnimationInfo(dto),
+ texture = getTexture(dto.texture),
+ spriteMargins = BlockMargins(
+ left = dto.spriteLeft,
+ top = dto.spriteTop,
+ right = dto.spriteRight,
+ bottom = dto.spriteBottom,
+ ),
+ toolLevel = dto.toolLevel,
+ toolType = mapToolType(dto),
+ damage = dto.damage,
+ tint = dto.tint,
+ )
+ }
+
+ private fun mapToolType(dto: BlockDto): Class<out Item.Tool>? {
+ return when(dto.toolType) {
+ "shovel" -> Item.Shovel::class.java
+ "sword" -> Item.Sword::class.java
+ "pickaxe" -> Item.Pickaxe::class.java
+ "axe" -> Item.Axe::class.java
+ "shears" -> Item.Shears::class.java
+
+ else -> null
+ }
+ }
+
+ private fun mapBlockDropInfo(dto: BlockDto): BlockDropInfo? {
+ val drop = dto.drop
+ val dropCount = dto.dropCount
+
+ if (drop == GameItemsHolder.FALLBACK_ITEM_KEY || dropCount == 0) {
+ return null
+ }
+
+ return BlockDropInfo(
+ itemKey = drop,
+ count = dropCount,
+ )
+ }
+
+ private fun mapBlockAnimationInfo(dto: BlockDto): BlockAnimationInfo? {
+ if (!dto.animated) {
+ return null
+ }
+
+ return BlockAnimationInfo(
+ framesCount = dto.frames,
+ )
+ }
+
+ private fun getTexture(textureName: String): Texture? {
+ if (textureName == GameItemsHolder.FALLBACK_BLOCK_KEY) {
+ return null
+ }
+
+ return Assets.resolveBlockTexture(assetLoader, textureName)
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/mapper/ItemMapper.kt b/core/src/ru/deadsoftware/cavedroid/game/model/mapper/ItemMapper.kt
--- /dev/null
@@ -0,0 +1,62 @@
+package ru.deadsoftware.cavedroid.game.model.mapper
+
+import com.badlogic.gdx.graphics.g2d.Sprite
+import dagger.Reusable
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.dto.ItemDto
+import ru.deadsoftware.cavedroid.game.model.item.CommonItemParams
+import ru.deadsoftware.cavedroid.game.model.item.Item
+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 javax.inject.Inject
+
+@Reusable
+class ItemMapper @Inject constructor(
+ private val assetLoader: AssetLoader,
+) {
+
+ fun map(key: String, dto: ItemDto, block: Block?, slabTopBlock: Block.Slab?, slabBottomBlock: Block.Slab?): Item {
+ val params = mapCommonParams(key, dto)
+
+ return when (dto.type) {
+ "normal" -> Normal(params, requireNotNull(loadSprite(dto)))
+ "bucket" -> Bucket(params, requireNotNull(loadSprite(dto)), requireNotNull(dto.actionKey))
+ "shovel" -> Shovel(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier, requireNotNull(dto.toolLevel))
+ "sword" -> Sword(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier, requireNotNull(dto.toolLevel))
+ "pickaxe" -> Pickaxe(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier, requireNotNull(dto.toolLevel))
+ "axe" -> Axe(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier, requireNotNull(dto.toolLevel))
+ "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))
+ "none" -> None(params)
+ else -> throw IllegalArgumentException("Unknown item type ${dto.type}")
+ }
+ }
+
+ private fun mapCommonParams(key: String, dto: ItemDto): CommonItemParams {
+ return CommonItemParams(
+ id = dto.id,
+ key = key,
+ name = dto.name,
+ inHandSpriteOrigin = SpriteOrigin(
+ x = dto.originX,
+ y = dto.origin_y,
+ ),
+ maxStack = dto.maxStack,
+ )
+ }
+
+ private fun loadSprite(dto: ItemDto): Sprite? {
+ if (dto.type == "none" || dto.type == "block" || dto.texture == GameItemsHolder.FALLBACK_ITEM_KEY) {
+ return null
+ }
+
+ val texture = Assets.resolveItemTexture(assetLoader, dto.texture)
+ return Sprite(texture)
+ .apply { flip(false, true) }
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/world/Biome.kt b/core/src/ru/deadsoftware/cavedroid/game/model/world/Biome.kt
--- /dev/null
@@ -0,0 +1,7 @@
+package ru.deadsoftware.cavedroid.game.model.world
+
+enum class Biome {
+ PLAINS,
+ DESERT,
+ WINTER
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/world/generator/WorldGeneratorConfig.kt b/core/src/ru/deadsoftware/cavedroid/game/model/world/generator/WorldGeneratorConfig.kt
--- /dev/null
@@ -0,0 +1,34 @@
+package ru.deadsoftware.cavedroid.game.model.world.generator
+
+import com.badlogic.gdx.utils.TimeUtils
+import ru.deadsoftware.cavedroid.game.model.world.Biome
+
+data class WorldGeneratorConfig(
+ val width: Int,
+ val height: Int,
+ val seed: Long,
+ val minSurfaceHeight: Int,
+ val maxSurfaceHeight: Int,
+ val biomes: List<Biome>,
+ val minBiomeSize: Int,
+ val seaLevel: Int,
+) {
+
+ companion object {
+
+ fun getDefault(): WorldGeneratorConfig {
+ return WorldGeneratorConfig(
+ width = 1024,
+ height = 256,
+ seed = TimeUtils.millis(),
+ maxSurfaceHeight = 208,
+ minSurfaceHeight = 128,
+ biomes = Biome.entries.toList(),
+ minBiomeSize = 64,
+ seaLevel = 192,
+ )
+ }
+
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/Block.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/Block.kt
+++ /dev/null
@@ -1,144 +0,0 @@
-@file:Suppress("DeprecatedCallableAddReplaceWith")
-
-package ru.deadsoftware.cavedroid.game.objects
-
-import com.badlogic.gdx.graphics.Texture
-import com.badlogic.gdx.graphics.g2d.Sprite
-import com.badlogic.gdx.graphics.g2d.SpriteBatch
-import com.badlogic.gdx.math.Rectangle
-
-private const val ANIMATION_FRAME_DURATION = 100L
-private const val DEPRECATION_MESSAGE =
- "Deprecated since moved to Kotlin. Use generated getter or kotlin property access."
-
-/**
- * @param left margin from left edge
- * @param top margin from top edge
- * @param right margin from right edge
- * @param bottom margin from bottom edge
- * @param hp hit points
- * @param drop id of an item the block will drop when destroyed
- * @param collision true if block has collision
- * @param background true if block should be drawn behind player
- * @param transparent true if block is transparent and renderer should draw a block behind it
- * @param requiresBlock true if block should break when there is no block with collision under it
- * @param fluid true if fluid
- * @param meta extra info for storing
- * @param texture block's texture
- * @param animated indicates if block has animation
- * @param frames number of animation frames. ignored if animated is false
- * @param spriteLeft block's sprite x on texture
- * @param spriteTop block's sprite y on texture
- * @param spriteRight block's sprite right edge on texture
- * @param spriteBottom block's sprite bottom on texture
- */
-data class Block(
- val left: Int,
- val top: Int,
- val right: Int,
- val bottom: Int,
- val hp: Int,
- val drop: String,
- val collision: Boolean,
- val background: Boolean,
- val transparent: Boolean,
- val requiresBlock: Boolean,
- val fluid: Boolean,
- val meta: String,
- private val texture: Texture?,
- val animated: Boolean,
- val frames: Int,
- private val spriteLeft: Int,
- private val spriteTop: Int,
- private val spriteRight: Int,
- private val spriteBottom: Int
-) {
-
- val width = 16 - right - left
- val height = 16 - top - bottom
-
- private val spriteWidth = 16 - spriteLeft - spriteRight
- private val spriteHeight = 16 - spriteTop - spriteBottom
-
- private val sprite: Sprite?
- get() {
- return if (animated) {
- animation[currentFrame()]
- } else {
- field
- }
- }
-
-
- private val animation: Array<Sprite>
-
- init {
- if (frames !in 0..Int.MAX_VALUE) {
- throw IllegalArgumentException("Animation frames must be in range [0, ${Int.MAX_VALUE}]")
- }
-
- animation = if (animated) {
- if (texture == null) {
- throw IllegalArgumentException("Cannot derive animation frames from null sprite")
- }
- Array(frames) { y ->
- Sprite(texture, spriteLeft, 16 * y + spriteTop, spriteWidth, spriteHeight).apply {
- flip(false, true)
- }
- }
- } else {
- emptyArray()
- }
-
- sprite = if (animated) { animation[0] } else {
- if (texture != null) {
- Sprite(texture, spriteLeft, spriteTop, spriteWidth, spriteHeight).apply {
- flip(false, true)
- }
- } else {
- null
- }
- }
- }
-
- private fun currentFrame() = if (animated) {
- ((System.currentTimeMillis() / ANIMATION_FRAME_DURATION) % frames).toInt()
- } else {
- 0
- }
-
- fun requireSprite() = sprite ?: throw IllegalStateException("Sprite is null")
-
- fun draw(spriter: SpriteBatch, x: Float, y: Float) {
- requireSprite().apply {
- setBounds(x + spriteLeft, y + spriteTop, spriteWidth.toFloat(), spriteHeight.toFloat())
- draw(spriter)
- }
- }
-
- fun getRectangle(x: Int, y: Int) =
- Rectangle(x * 16f + left, y * 16f + this.top, width.toFloat(), height.toFloat())
-
- fun hasDrop() = drop != "none"
-
- fun toJump() = top < 8 && collision
-
- @Deprecated(DEPRECATION_MESSAGE)
- fun hasCollision() = collision
-
- @Deprecated(DEPRECATION_MESSAGE)
- fun isBackground() = background
-
- @Deprecated(DEPRECATION_MESSAGE)
- fun isTransparent() = transparent
-
- @Deprecated(DEPRECATION_MESSAGE)
- fun isFluid() = fluid
-
- @Deprecated(DEPRECATION_MESSAGE)
- fun requiresBlock() = requiresBlock
-
- @Deprecated("Was renamed to Sprite to comply with variable type.", ReplaceWith("requireSprite()"))
- fun getTexture() = sprite
-
-}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.java b/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package ru.deadsoftware.cavedroid.game.objects;
-
-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 ru.deadsoftware.cavedroid.game.GameWorld;
-import ru.deadsoftware.cavedroid.game.mobs.Player;
-
-import java.io.Serializable;
-
-public class Drop extends Rectangle implements Serializable {
-
- private final int id;
- private final Vector2 move;
- private boolean pickedUp = false;
-
- Drop(float x, float y, int id) {
- super(x, y, 8, 8);
- this.id = id;
- this.move = new Vector2(0, -1);
- }
-
- public Vector2 getMove() {
- return move;
- }
-
- public int closeToPlayer(GameWorld gameWorld, Player player) {
- boolean[] c = new boolean[3];
-
- c[0] = Intersector.overlaps(new Rectangle(player.getX() - 16,
- player.getY() - 16, player.getWidth() + 32, player.getHeight() + 32), this);
- c[1] = Intersector.overlaps(new Rectangle((player.getX() + gameWorld.getWidthPx()) - 16,
- player.getY() - 16, player.getWidth() + 32, player.getHeight() + 32), this);
- c[2] = Intersector.overlaps(new Rectangle((player.getX() - gameWorld.getWidthPx()) - 16,
- player.getY() - 16, player.getWidth() + 32, player.getHeight() + 32), this);
-
- for (int i = 0; i < 3; i++) {
- if (c[i]) {
- return i + 1;
- }
- }
-
- return 0;
- }
-
- public void moveToPlayer(GameWorld gameWorld, Player player, int ctp) {
- if (ctp > 0) {
- float px = player.getX();
- float py = player.getY();
-
- switch (ctp) {
- case 2:
- px += gameWorld.getWidthPx();
- break;
- case 3:
- px -= gameWorld.getWidthPx();
- break;
- }
-
- float dx = 0, dy = 0;
-
- if (px + player.getWidth() < x + 4) {
- dx = -.5f;
- } else if (px > x + 4) {
- dx = .5f;
- }
-
- if (py + player.getHeight() < y + 4) {
- dy = -.5f;
- } else if (py > y + 4) {
- dy = .5f;
- }
-
- move.add(dx, dy);
-
- if (move.x > 2) {
- move.x = 1;
- } else if (move.x < -2) {
- move.x = -1;
- }
-
- if (move.y > 2) {
- move.y = 1;
- } else if (move.y < -2) {
- move.y = -1;
- }
- }
- }
-
- public void pickUpDrop(Player pl) {
- for (int i = 0; i < pl.inventory.length; i++) {
- if (pl.inventory[i] == 0 || pl.inventory[i] == id) {
- pl.inventory[i] = id;
- pickedUp = true;
- break;
- }
- }
- }
-
- private void checkWorldBounds() {
-// if (x + 8 > world.getWidthPx()) {
-// x -= world.getWidthPx();
-// } else if (x < 0) {
-// x += world.getWidthPx();
-// }
- }
-
- public void move() {
- x += move.x;
- y += move.y;
- checkWorldBounds();
- y = MathUtils.round(y);
- }
-
- public int getId() {
- return id;
- }
-
- public boolean isPickedUp() {
- return pickedUp;
- }
-
- public void setPickedUp(boolean pickedUp) {
- this.pickedUp = pickedUp;
- }
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.kt
--- /dev/null
@@ -0,0 +1,57 @@
+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
index 0e4c6592abe3badac802872be953ab84c68b2324..798a84ce5b77415bbf88f87187c39d51e636ff04 100644 (file)
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;
public DropController() {
}
- public void addDrop(float x, float y, int id) {
- mDrops.add(new Drop(x, y, id));
+ 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() {
diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/Item.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/Item.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ru.deadsoftware.cavedroid.game.objects
-
-import com.badlogic.gdx.graphics.g2d.Sprite
-
-data class Item(
- val name: String,
- val type: String,
- val sprite: Sprite?
-) {
-
- init {
- sprite?.flip(false, true)
- }
-
- fun requireSprite() = sprite ?: throw IllegalStateException("Sprite is null")
-
- fun isBlock() = type == "block"
-
- @Deprecated("Was renamed to Sprite to comply with variable type.", ReplaceWith("requireSprite()"))
- fun getTexture() = sprite
-
-}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/BackgroundBlocksRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/BackgroundBlocksRenderer.kt
--- /dev/null
@@ -0,0 +1,53 @@
+package ru.deadsoftware.cavedroid.game.render
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.graphics.GL20
+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.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.utils.forEachBlockInArea
+import javax.inject.Inject
+
+@GameScope
+class BackgroundBlocksRenderer @Inject constructor(
+ gameWorld: GameWorld,
+ mobsController: MobsController
+) : BlocksRenderer(gameWorld, mobsController) {
+
+ override val renderLayer get() = RENDER_LAYER
+
+ override val background = true
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ forEachBlockInArea(viewport) { x, y ->
+ drawBackMap(spriteBatch, viewport, x, y)
+ }
+
+ drawBlockDamage(spriteBatch, viewport)
+
+ spriteBatch.end()
+ Gdx.gl.glEnable(GL20.GL_BLEND)
+ Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)
+ shapeRenderer.begin(ShapeRenderer.ShapeType.Filled)
+ shapeRenderer.setColor(0f, 0f, 0f, .5f)
+
+ forEachBlockInArea(viewport) { x, y ->
+ shadeBackMap(shapeRenderer, viewport, x, y)
+ }
+
+ shapeRenderer.end()
+ Gdx.gl.glDisable(GL20.GL_BLEND)
+ spriteBatch.begin()
+
+ forEachBlockInArea(viewport) { x, y ->
+ drawForeMap(spriteBatch, viewport, x, y)
+ }
+ }
+
+ companion object {
+ private const val RENDER_LAYER = 100000
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/BlocksRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/BlocksRenderer.kt
--- /dev/null
@@ -0,0 +1,108 @@
+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
+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.utils.px
+
+abstract class BlocksRenderer(
+ protected val gameWorld: GameWorld,
+ protected val mobsController: MobsController,
+) : IGameRenderer {
+
+ protected abstract val background: Boolean
+
+ private val Block.canSeeThrough
+ get() = isNone() || params.isTransparent
+
+ private fun blockDamageSprite(index: Int): Sprite? {
+ if (index !in 0..< MAX_BLOCK_DAMAGE_INDEX) {
+ return null
+ }
+ return Assets.blockDamageSprites[index]
+ }
+
+ protected fun drawBlockDamage(spriteBatch: SpriteBatch, viewport: Rectangle) {
+ val player = mobsController.player
+ val blockDamage = player.blockDamage.takeIf { it > 0f } ?: return
+ val cursorX = player.cursorX
+ val cursorY = player.cursorY
+
+ val block = if (background) {
+ gameWorld.getBackMap(cursorX, cursorY)
+ } else {
+ gameWorld.getForeMap(cursorX, cursorY)
+ }
+
+ val index = (MAX_BLOCK_DAMAGE_INDEX.toFloat() * (blockDamage.toFloat() / block.params.hitPoints.toFloat()))
+ .let(MathUtils::floor)
+ val sprite = blockDamageSprite(index) ?: return
+
+ if (gameWorld.hasForeAt(cursorX, cursorY) != background) {
+ sprite.setBounds(
+ /* x = */ cursorX.px - viewport.x + block.params.spriteMargins.left,
+ /* y = */ cursorY.px - viewport.y + block.params.spriteMargins.top,
+ /* width = */ block.spriteWidth,
+ /* height = */ block.spriteHeight
+ )
+ sprite.draw(spriteBatch)
+ }
+ }
+
+ protected fun shadeBackMap(
+ shapeRenderer: ShapeRenderer,
+ viewport: Rectangle,
+ x: Int,
+ y: Int
+ ) {
+ val foregroundBlock = gameWorld.getForeMap(x, y)
+ val backgroundBlock = gameWorld.getBackMap(x, y)
+
+ if (foregroundBlock.canSeeThrough && !backgroundBlock.isNone()) {
+ val drawX = x.px - viewport.x
+ val drawY = y.px - viewport.y
+ val marginLeft = backgroundBlock.params.spriteMargins.left
+ val marginTop = backgroundBlock.params.spriteMargins.top
+
+ shapeRenderer.rect(
+ /* x = */ drawX + marginLeft,
+ /* y = */ drawY + marginTop,
+ /* width = */ backgroundBlock.width,
+ /* height = */ backgroundBlock.height
+ )
+ }
+ }
+
+ protected fun drawBackMap(spriteBatch: SpriteBatch, viewport: Rectangle, x: Int, y: Int) {
+ val foregroundBlock = gameWorld.getForeMap(x, y)
+ val backgroundBlock = gameWorld.getBackMap(x, y)
+
+ if (foregroundBlock.canSeeThrough && !backgroundBlock.isNone()) {
+ val drawX = x.px - viewport.x
+ val drawY = y.px - viewport.y
+ backgroundBlock.draw(spriteBatch, drawX, drawY)
+ }
+ }
+
+ protected fun drawForeMap(spriteBatch: SpriteBatch, viewport: Rectangle, x: Int, y: Int) {
+ val foregroundBlock = gameWorld.getForeMap(x, y)
+
+ if (!foregroundBlock.isNone() && foregroundBlock.params.isBackground == background) {
+ val drawX = x.px - viewport.x
+ val drawY = y.px - viewport.y
+ foregroundBlock.draw(spriteBatch, drawX, drawY)
+ }
+ }
+
+ companion object {
+ private const val MAX_BLOCK_DAMAGE_INDEX = 10
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/DebugRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/DebugRenderer.kt
--- /dev/null
@@ -0,0 +1,114 @@
+package ru.deadsoftware.cavedroid.game.render
+
+import com.badlogic.gdx.graphics.Color
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import com.badlogic.gdx.graphics.glutils.ShapeRenderer
+import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.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.utils.bl
+import ru.deadsoftware.cavedroid.misc.utils.drawString
+import ru.deadsoftware.cavedroid.misc.utils.forEachBlockInArea
+import ru.deadsoftware.cavedroid.misc.utils.px
+import javax.inject.Inject
+
+@GameScope
+class DebugRenderer @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val gameWorld: GameWorld,
+ private val mobsController: MobsController,
+ private val debugInfoStringsProvider: DebugInfoStringsProvider,
+) : IGameRenderer {
+
+ override val renderLayer get() = RENDER_LAYER
+
+ private fun getMinimapColor(x: Int, y: Int): Color? {
+ val foregroundBlock = gameWorld.getForeMap(x, y)
+
+ return if (!foregroundBlock.isNone()) {
+ when (foregroundBlock) {
+ is Block.Water -> Color.BLUE
+ is Block.Lava -> Color.RED
+ else -> Color.BLACK
+ }
+ } else if (gameWorld.hasBackAt(x, y)) {
+ Color.DARK_GRAY
+ } else {
+ null
+ }
+ }
+
+ private fun drawMinimap(
+ spriteBatch: SpriteBatch,
+ shapeRenderer: ShapeRenderer,
+ minimapX: Float,
+ minimapY: Float,
+ minimapSize: Float
+ ) {
+ val mapArea = Rectangle(
+ /* x = */ mobsController.player.x - (minimapSize.px / 2),
+ /* y = */ mobsController.player.y - (minimapSize.px / 2),
+ /* width = */ minimapSize.px,
+ /* height = */ minimapSize.px
+ )
+
+ spriteBatch.end()
+ shapeRenderer.begin(ShapeRenderer.ShapeType.Filled)
+ shapeRenderer.color = Color.LIGHT_GRAY
+ shapeRenderer.rect(minimapX, minimapY, minimapSize, minimapSize)
+
+ forEachBlockInArea(mapArea) { x, y ->
+ getMinimapColor(x, y)?.let { color ->
+ shapeRenderer.setColor(color)
+ shapeRenderer.rect(
+ /* x = */ minimapX + (x - mapArea.x.bl),
+ /* y = */ minimapY + (y - mapArea.y.bl),
+ /* width = */ 1f,
+ /* height = */ 1f
+ )
+ }
+ }
+
+ shapeRenderer.color = Color.OLIVE
+ shapeRenderer.rect(minimapX + minimapSize / 2, minimapY + minimapSize / 2, 1f, 2f)
+ shapeRenderer.end()
+ spriteBatch.begin()
+ }
+
+ private fun drawDebugInfo(spriteBatch: SpriteBatch) {
+ debugInfoStringsProvider.getDebugStrings().forEachIndexed { index, str ->
+ spriteBatch.drawString(str, 0f, index * 10f)
+ }
+ }
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ if (mainConfig.isShowInfo) {
+ drawDebugInfo(spriteBatch)
+ }
+
+ if (mainConfig.isShowMap) {
+ drawMinimap(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ minimapX = viewport.width - MinimapConfig.margin - MinimapConfig.size,
+ minimapY = MinimapConfig.margin,
+ minimapSize = MinimapConfig.size
+ )
+ }
+
+ }
+
+ companion object {
+ private const val RENDER_LAYER = Int.MAX_VALUE
+
+ private data object MinimapConfig {
+ const val margin = 24f
+ const val size = 64f
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/DropsRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/DropsRenderer.kt
--- /dev/null
@@ -0,0 +1,40 @@
+package ru.deadsoftware.cavedroid.game.render
+
+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.world.GameWorld
+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
+class DropsRenderer @Inject constructor(
+ private val dropController: DropController,
+ private val gameWorld: GameWorld,
+) : IGameRenderer {
+
+ override val renderLayer = RENDER_LAYER
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ dropController.forEach { drop ->
+ drop.cycledInsideWorld(viewport, gameWorld.width.px)?.let { dropRect ->
+ spriteBatch.drawSprite(
+ sprite = drop.item.sprite,
+ x = dropRect.x - viewport.x,
+ y = dropRect.y - viewport.y,
+ width = dropRect.width,
+ height = dropRect.height,
+ )
+ }
+ }
+ }
+
+ companion object {
+ private const val RENDER_LAYER = 100200
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/ForegroundBlocksRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/ForegroundBlocksRenderer.kt
--- /dev/null
@@ -0,0 +1,32 @@
+package ru.deadsoftware.cavedroid.game.render
+
+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.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.utils.forEachBlockInArea
+import javax.inject.Inject
+
+@GameScope
+class ForegroundBlocksRenderer @Inject constructor(
+ gameWorld: GameWorld,
+ mobsController: MobsController
+) : BlocksRenderer(gameWorld, mobsController) {
+
+ override val renderLayer get() = RENDER_LAYER
+
+ override val background = false
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ forEachBlockInArea(viewport) { x, y ->
+ drawForeMap(spriteBatch, viewport, x, y)
+ }
+ drawBlockDamage(spriteBatch, viewport)
+ }
+
+ companion object {
+ private const val RENDER_LAYER = 100400
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/HudRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/HudRenderer.kt
--- /dev/null
@@ -0,0 +1,122 @@
+package ru.deadsoftware.cavedroid.game.render
+
+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.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player.ControlMode
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.utils.px
+import javax.inject.Inject
+
+@GameScope
+class HudRenderer @Inject constructor(
+ private val gameWorld: GameWorld,
+ private val mobsController: MobsController,
+) : IGameRenderer {
+
+ override val renderLayer = RENDER_LAYER
+
+ private val cursorTexture get() = requireNotNull(Assets.textureRegions[CURSOR_KEY])
+ 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 halfHeartTexture get() = requireNotNull(Assets.textureRegions[HALF_HEART_KEY])
+
+ private fun drawCursor(spriteBatch: SpriteBatch, viewport: Rectangle) {
+ val cursorX = mobsController.player.cursorX
+ val cursorY = mobsController.player.cursorY
+
+ if (gameWorld.hasForeAt(cursorX, cursorY) ||
+ gameWorld.hasBackAt(cursorX, cursorY) ||
+ mobsController.player.controlMode == ControlMode.CURSOR
+ ) {
+ spriteBatch.draw(cursorTexture, cursorX.px - viewport.x, cursorY.px - viewport.y)
+ }
+ }
+
+ private fun drawHealth(spriteBatch: SpriteBatch, x: Float, y: Float) {
+ val player = mobsController.player
+
+ if (player.gameMode == 1) {
+ return
+ }
+
+ val wholeHeart = wholeHeartTexture
+ val wholeHearts = player.health / 2
+
+ for (i in 0..<wholeHearts) {
+ spriteBatch.draw(wholeHeart, 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)
+ .forEachIndexed { index, item ->
+ if (item.item.isNone()) {
+ return@forEachIndexed
+ }
+
+ item.draw(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ x = hotbarX + HotbarConfig.horizontalMargin +
+ index * (HotbarConfig.itemSeparatorWidth + HotbarConfig.itemSlotSpace),
+ y = HotbarConfig.verticalMargin,
+ )
+ }
+ }
+
+ private fun drawHotbarSelector(spriteBatch: SpriteBatch, hotbarX: Float) {
+ spriteBatch.draw(
+ /* region = */ hotbarSelectorTexture,
+ /* x = */ hotbarX - HotbarSelectorConfig.horizontalPadding
+ + mobsController.player.slot * (HotbarConfig.itemSeparatorWidth + HotbarConfig.itemSlotSpace),
+ /* y = */ -HotbarSelectorConfig.verticalPadding
+ )
+ }
+
+ private fun drawHotbar(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle) {
+ val hotbar = hotbarTexture
+ val hotbarX = viewport.width / 2 - hotbar.regionWidth / 2
+
+ spriteBatch.draw(hotbar, hotbarX, 0f)
+ drawHealth(spriteBatch, hotbarX, hotbarTexture.regionHeight.toFloat())
+ drawHotbarSelector(spriteBatch, hotbarX)
+ drawHotbarItems(spriteBatch, shapeRenderer, hotbarX)
+ }
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ drawCursor(spriteBatch, viewport)
+ drawHotbar(spriteBatch, shapeRenderer, viewport)
+ }
+
+ companion object {
+ private const val RENDER_LAYER = 100500
+
+ private const val CURSOR_KEY = "cursor"
+ private const val HOTBAR_KEY = "hotbar"
+ 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 data object HotbarConfig {
+ const val horizontalMargin = 3f
+ const val verticalMargin = 3f
+ const val itemSeparatorWidth = 4f
+ const val itemSlotSpace = 16f
+ const val hotbarCells = 9
+ }
+
+ private data object HotbarSelectorConfig {
+ const val horizontalPadding = 1f
+ const val verticalPadding = 1f
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/IGameRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/IGameRenderer.kt
--- /dev/null
@@ -0,0 +1,20 @@
+package ru.deadsoftware.cavedroid.game.render
+
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import com.badlogic.gdx.graphics.glutils.ShapeRenderer
+import com.badlogic.gdx.math.Rectangle
+
+interface IGameRenderer {
+
+ val renderLayer: Int
+
+ /**
+ * When called, [spriteBatch] is beginned!
+ */
+ fun draw(
+ spriteBatch: SpriteBatch,
+ shapeRenderer: ShapeRenderer,
+ viewport: Rectangle,
+ delta: Float
+ )
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/MobsRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/MobsRenderer.kt
--- /dev/null
@@ -0,0 +1,45 @@
+package ru.deadsoftware.cavedroid.game.render
+
+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.mobs.Mob
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import ru.deadsoftware.cavedroid.misc.utils.cycledInsideWorld
+import ru.deadsoftware.cavedroid.misc.utils.px
+import javax.inject.Inject
+
+@GameScope
+class MobsRenderer @Inject constructor(
+ private val mobsController: MobsController,
+ private val gameWorld: GameWorld,
+) : IGameRenderer {
+
+ override val renderLayer get() = RENDER_LAYER
+
+ private fun drawMob(spriteBatch: SpriteBatch, viewport: Rectangle, mob: Mob, delta: Float) {
+ mob.cycledInsideWorld(viewport, gameWorld.width.px)?.let { mobRect ->
+ mob.draw(spriteBatch, mobRect.x - viewport.x, mobRect.y - viewport.y, delta)
+ }
+ }
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ val player = mobsController.player
+ player.draw(
+ /* spriteBatch = */ spriteBatch,
+ /* x = */ player.x - viewport.x - player.width / 2,
+ /* y = */ player.y - viewport.y,
+ /* delta = */ delta
+ )
+
+ mobsController.mobs.forEach { mob ->
+ drawMob(spriteBatch, viewport, mob, delta)
+ }
+ }
+
+ companion object {
+ private const val RENDER_LAYER = 100100
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/RenderModule.kt b/core/src/ru/deadsoftware/cavedroid/game/render/RenderModule.kt
--- /dev/null
@@ -0,0 +1,59 @@
+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()
+// }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/TouchControlsRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/TouchControlsRenderer.kt
--- /dev/null
@@ -0,0 +1,59 @@
+package ru.deadsoftware.cavedroid.game.render
+
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import com.badlogic.gdx.graphics.glutils.ShapeRenderer
+import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.mobs.Player.ControlMode
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.misc.Assets
+import ru.deadsoftware.cavedroid.misc.utils.ArrayMapExtensions.component1
+import ru.deadsoftware.cavedroid.misc.utils.ArrayMapExtensions.component2
+import javax.inject.Inject
+
+@GameScope
+class TouchControlsRenderer @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+ private val gameWindowsManager: GameWindowsManager,
+) : IGameRenderer {
+
+ override val renderLayer get() = RENDER_LAYER
+
+ private val shadeTexture get() = Assets.textureRegions[SHADE_KEY]
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ if (!mainConfig.isTouch || gameWindowsManager.getCurrentWindow() != GameUiWindow.NONE) {
+ return
+ }
+
+ val touchControlsMap = Assets.guiMap
+
+ touchControlsMap.forEach { (key, value) ->
+ val touchKey = value.rect
+ spriteBatch.draw(
+ /* region = */ Assets.textureRegions[key],
+ /* x = */ touchKey.x,
+ /* y = */ touchKey.y,
+ /* width = */ touchKey.width,
+ /* height = */ touchKey.height
+ )
+ }
+
+ // FIXME: Add pressed state for buttons
+ if (mobsController.player.controlMode == ControlMode.CURSOR) {
+ val altKeyRect = touchControlsMap.get("alt").rect
+ spriteBatch.draw(shadeTexture, altKeyRect.x, altKeyRect.y, altKeyRect.width, altKeyRect.height)
+ }
+ }
+
+ companion object {
+ private const val RENDER_LAYER = 100700
+
+ private const val SHADE_KEY = "shade"
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/WindowsRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/WindowsRenderer.kt
--- /dev/null
@@ -0,0 +1,40 @@
+package ru.deadsoftware.cavedroid.game.render
+
+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.game.GameScope
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.render.windows.CraftingWindowRenderer
+import ru.deadsoftware.cavedroid.game.render.windows.CreativeWindowRenderer
+import ru.deadsoftware.cavedroid.game.render.windows.SurvivalWindowRenderer
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import javax.inject.Inject
+
+@GameScope
+class WindowsRenderer @Inject constructor(
+ private val creativeWindowRenderer: CreativeWindowRenderer,
+ private val survivalWindowRenderer: SurvivalWindowRenderer,
+ private val craftingWindowRenderer: CraftingWindowRenderer,
+ private val gameWindowsManager: GameWindowsManager,
+) : IGameRenderer {
+
+ override val renderLayer get() = RENDER_LAYER
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ when (val windowType = gameWindowsManager.getCurrentWindow()) {
+ GameUiWindow.CREATIVE_INVENTORY -> creativeWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta)
+ GameUiWindow.SURVIVAL_INVENTORY -> survivalWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta)
+ GameUiWindow.CRAFTING_TABLE -> craftingWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta)
+ GameUiWindow.NONE -> return
+ else -> Gdx.app.error(TAG, "Cannot draw window: ${windowType.name}")
+ }
+ }
+
+ companion object {
+ private const val TAG = "WindowsRenderer"
+
+ const val RENDER_LAYER = 100600
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/windows/AbstractWindowRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/windows/AbstractWindowRenderer.kt
--- /dev/null
@@ -0,0 +1,51 @@
+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 ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.misc.utils.drawSprite
+
+abstract class AbstractWindowRenderer {
+
+ protected inline fun <reified T> drawItemsGrid(
+ spriteBatch: SpriteBatch,
+ shapeRenderer: ShapeRenderer,
+ gridX: Float,
+ gridY: Float,
+ items: Iterable<T>,
+ itemsInRow: Int,
+ cellWidth: Float,
+ cellHeight: Float
+ ) {
+ if (T::class != Item::class && T::class != InventoryItem::class) {
+ Gdx.app.log(_TAG, "Trying to draw items grid of not items")
+ return
+ }
+
+ items.forEachIndexed { index, element ->
+ val item = element as? Item
+ val inventoryItem = element as? InventoryItem
+
+ if (item == null && inventoryItem == null) {
+ throw IllegalStateException("This should be unreachable")
+ }
+
+ if (item?.isNone() == true || inventoryItem?.item?.isNone() == true) {
+ return@forEachIndexed
+ }
+
+ val itemX = gridX + (index % itemsInRow) * cellWidth
+ val itemY = gridY + (index / itemsInRow) * cellHeight
+
+ inventoryItem?.draw(spriteBatch, shapeRenderer, itemX, itemY)
+ ?: item?.let { spriteBatch.drawSprite(it.sprite, itemX, itemY) }
+ }
+ }
+
+ companion object {
+ protected const val _TAG = "AbstractWindowRenderer"
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/windows/CraftingWindowRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/windows/CraftingWindowRenderer.kt
--- /dev/null
@@ -0,0 +1,95 @@
+package ru.deadsoftware.cavedroid.game.render.windows
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import com.badlogic.gdx.graphics.glutils.ShapeRenderer
+import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.render.IGameRenderer
+import ru.deadsoftware.cavedroid.game.render.WindowsRenderer
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.windows.inventory.CraftingInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+
+@GameScope
+class CraftingWindowRenderer @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+ private val gameWindowsManager: GameWindowsManager,
+ private val gameItemsHolder: GameItemsHolder,
+) : AbstractWindowRenderer(), IGameRenderer {
+
+ override val renderLayer get() = WindowsRenderer.RENDER_LAYER
+
+ private val craftingWindowTexture get() = requireNotNull(Assets.textureRegions[CRAFTING_WINDOW_KEY])
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ val windowTexture = craftingWindowTexture
+ val window = gameWindowsManager.currentWindow as CraftingInventoryWindow
+
+ val windowX = viewport.width / 2 - windowTexture.regionWidth / 2
+ val windowY = viewport.height / 2 - windowTexture.regionHeight / 2
+
+ spriteBatch.draw(windowTexture, windowX, windowY)
+
+ drawItemsGrid(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ gridX = windowX + GameWindowsConfigs.Crafting.itemsGridMarginLeft,
+ gridY = windowY + GameWindowsConfigs.Crafting.itemsGridMarginTop,
+ items = mobsController.player.inventory.asSequence()
+ .drop(GameWindowsConfigs.Crafting.hotbarCells)
+ .take(GameWindowsConfigs.Crafting.itemsInCol * GameWindowsConfigs.Crafting.itemsInRow)
+ .asIterable(),
+ itemsInRow = GameWindowsConfigs.Crafting.itemsInRow,
+ cellWidth = GameWindowsConfigs.Crafting.itemsGridColWidth,
+ cellHeight = GameWindowsConfigs.Crafting.itemsGridRowHeight,
+ )
+
+ drawItemsGrid(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ gridX = windowX + GameWindowsConfigs.Crafting.itemsGridMarginLeft,
+ gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Crafting.hotbarOffsetFromBottom,
+ items = mobsController.player.inventory.asSequence()
+ .take(GameWindowsConfigs.Crafting.hotbarCells)
+ .asIterable(),
+ itemsInRow = GameWindowsConfigs.Crafting.hotbarCells,
+ cellWidth = GameWindowsConfigs.Crafting.itemsGridColWidth,
+ cellHeight = GameWindowsConfigs.Crafting.itemsGridRowHeight,
+ )
+
+ drawItemsGrid(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ gridX = windowX + GameWindowsConfigs.Crafting.craftOffsetX,
+ gridY = windowY + GameWindowsConfigs.Crafting.craftOffsetY,
+ items = window.craftingItems.asSequence().map { it ?: gameItemsHolder.fallbackItem.toInventoryItem()}.asIterable(),
+ itemsInRow = GameWindowsConfigs.Crafting.craftGridSize,
+ cellWidth = GameWindowsConfigs.Crafting.itemsGridColWidth,
+ cellHeight = GameWindowsConfigs.Crafting.itemsGridRowHeight,
+ )
+
+ window.craftResult?.draw(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ x = windowX + GameWindowsConfigs.Crafting.craftResultOffsetX,
+ y = windowY + GameWindowsConfigs.Crafting.craftResultOffsetY
+ )
+
+ window.selectedItem?.drawSelected(
+ spriteBatch = spriteBatch,
+ x = Gdx.input.x * (viewport.width / Gdx.graphics.width),
+ y = Gdx.input.y * (viewport.height / Gdx.graphics.height)
+ )
+ }
+
+ companion object {
+ private const val CRAFTING_WINDOW_KEY = "crafting_table"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/windows/CreativeWindowRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/windows/CreativeWindowRenderer.kt
--- /dev/null
@@ -0,0 +1,83 @@
+package ru.deadsoftware.cavedroid.game.render.windows
+
+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.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.misc.Assets
+import javax.inject.Inject
+import kotlin.math.min
+
+@GameScope
+class CreativeWindowRenderer @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val gameWindowsManager: GameWindowsManager,
+ private val gameItemsHolder: GameItemsHolder,
+ private val mobsController: MobsController,
+) : AbstractWindowRenderer(), IGameRenderer {
+
+ override val renderLayer get() = WindowsRenderer.RENDER_LAYER
+
+ private val creativeWindowTexture get() = requireNotNull(Assets.textureRegions[CREATIVE_WINDOW_KEY])
+ private val scrollIndicatorTexture get() = requireNotNull(Assets.textureRegions[SCROLL_INDICATOR_KEY])
+
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ val creativeWindow = creativeWindowTexture
+
+ val windowX = viewport.width / 2 - creativeWindow.regionWidth / 2
+ val windowY = viewport.height / 2 - creativeWindow.regionHeight / 2
+ val oneScrollAmount = GameWindowsConfigs.Creative.scrollIndicatorFullHeight / gameItemsHolder.getMaxCreativeScrollAmount()
+
+ spriteBatch.draw(creativeWindow, windowX, windowY)
+ spriteBatch.draw(
+ /* region = */ scrollIndicatorTexture,
+ /* x = */ windowX + GameWindowsConfigs.Creative.scrollIndicatorMarginLeft,
+ /* y = */ windowY + GameWindowsConfigs.Creative.scrollIndicatorMarginTop
+ + (gameWindowsManager.creativeScrollAmount * oneScrollAmount)
+ )
+
+ val allItems = gameItemsHolder.getAllItems()
+ val startIndex = gameWindowsManager.creativeScrollAmount * GameWindowsConfigs.Creative.itemsInRow
+ val endIndex = min(startIndex + GameWindowsConfigs.Creative.itemsOnPage, allItems.size)
+ val items = sequence {
+ for (i in startIndex..<endIndex) {
+ yield(allItems.elementAt(i))
+ }
+ }
+
+ drawItemsGrid(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ gridX = windowX + GameWindowsConfigs.Creative.itemsGridMarginLeft,
+ gridY = windowY + GameWindowsConfigs.Creative.itemsGridMarginTop,
+ items = items.asIterable(),
+ itemsInRow = GameWindowsConfigs.Creative.itemsInRow,
+ cellWidth = GameWindowsConfigs.Creative.itemsGridColWidth,
+ cellHeight = GameWindowsConfigs.Creative.itemsGridRowHeight,
+ )
+
+ drawItemsGrid(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ gridX = windowX + GameWindowsConfigs.Creative.itemsGridMarginLeft,
+ gridY = windowY + creativeWindow.regionHeight - GameWindowsConfigs.Creative.playerInventoryOffsetFromBottom,
+ items = mobsController.player.inventory.asSequence().take(GameWindowsConfigs.Creative.invItems).asIterable(),
+ itemsInRow = GameWindowsConfigs.Creative.invItems,
+ cellWidth = GameWindowsConfigs.Creative.itemsGridColWidth,
+ cellHeight = GameWindowsConfigs.Creative.itemsGridRowHeight,
+ )
+ }
+
+ companion object {
+ private const val CREATIVE_WINDOW_KEY = "creative"
+ private const val SCROLL_INDICATOR_KEY = "handle"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/render/windows/SurvivalWindowRenderer.kt b/core/src/ru/deadsoftware/cavedroid/game/render/windows/SurvivalWindowRenderer.kt
--- /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.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.windows.GameWindowsConfigs
+import ru.deadsoftware.cavedroid.game.windows.GameWindowsManager
+import ru.deadsoftware.cavedroid.game.windows.inventory.SurvivalInventoryWindow
+import ru.deadsoftware.cavedroid.misc.Assets
+import javax.inject.Inject
+import kotlin.math.atan
+
+@GameScope
+class SurvivalWindowRenderer @Inject constructor(
+ private val mainConfig: MainConfig,
+ private val mobsController: MobsController,
+ private val gameWindowsManager: GameWindowsManager,
+ private val gameItemsHolder: GameItemsHolder,
+) : AbstractWindowRenderer(), IGameRenderer {
+
+ override val renderLayer get() = WindowsRenderer.RENDER_LAYER
+
+ private val survivalWindowTexture get() = requireNotNull(Assets.textureRegions[SURVIVAL_WINDOW_KEY])
+
+ private fun setPortraitHeadRotation(portraitX: Float, portraitY: Float) {
+ if (mainConfig.isTouch) {
+ return
+ }
+
+ val mouseX = Gdx.input.x * (mainConfig.width / Gdx.graphics.width)
+ val mouseY = Gdx.input.y * (mainConfig.height / Gdx.graphics.height)
+
+ val h = mouseX.toDouble() - portraitX.toDouble()
+ val v = mouseY.toDouble() - portraitY.toDouble()
+
+ mobsController.player.setDir(
+ if (mouseX < portraitX + mobsController.player.width / 2)
+ Mob.Direction.LEFT
+ else
+ Mob.Direction.RIGHT
+ )
+
+ mobsController.player.headRotation = atan(v / h).toFloat() * MathUtils.radDeg
+ }
+
+ private fun drawPlayerPortrait(spriteBatch: SpriteBatch, windowX: Float, windowY: Float, delta: Float) {
+ val portraitX = windowX + GameWindowsConfigs.Survival.portraitMarginLeft +
+ (GameWindowsConfigs.Survival.portraitWidth / 2 - mobsController.player.width / 2)
+ val portraitY = windowY + GameWindowsConfigs.Survival.portraitMarginTop +
+ (GameWindowsConfigs.Survival.portraitHeight / 2 - mobsController.player.height / 2)
+
+ setPortraitHeadRotation(portraitX, portraitY)
+ mobsController.player.draw(spriteBatch, portraitX, portraitY, delta)
+ }
+
+ override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) {
+ val windowTexture = survivalWindowTexture
+ val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow
+
+ val windowX = viewport.width / 2 - windowTexture.regionWidth / 2
+ val windowY = viewport.height / 2 - windowTexture.regionHeight / 2
+
+ spriteBatch.draw(windowTexture, windowX, windowY)
+
+ drawPlayerPortrait(spriteBatch, windowX, windowY, delta)
+
+ drawItemsGrid(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ gridX = windowX + GameWindowsConfigs.Survival.itemsGridMarginLeft,
+ gridY = windowY + GameWindowsConfigs.Survival.itemsGridMarginTop,
+ items = mobsController.player.inventory.asSequence()
+ .drop(GameWindowsConfigs.Survival.hotbarCells)
+ .take(GameWindowsConfigs.Survival.itemsInCol * GameWindowsConfigs.Survival.itemsInRow)
+ .asIterable(),
+ itemsInRow = GameWindowsConfigs.Survival.itemsInRow,
+ cellWidth = GameWindowsConfigs.Survival.itemsGridColWidth,
+ cellHeight = GameWindowsConfigs.Survival.itemsGridRowHeight,
+ )
+
+ drawItemsGrid(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ gridX = windowX + GameWindowsConfigs.Survival.itemsGridMarginLeft,
+ gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Survival.hotbarOffsetFromBottom,
+ items = mobsController.player.inventory.asSequence()
+ .take(GameWindowsConfigs.Survival.hotbarCells)
+ .asIterable(),
+ itemsInRow = GameWindowsConfigs.Survival.hotbarCells,
+ cellWidth = GameWindowsConfigs.Survival.itemsGridColWidth,
+ cellHeight = GameWindowsConfigs.Survival.itemsGridRowHeight,
+ )
+
+ drawItemsGrid(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ gridX = windowX + GameWindowsConfigs.Survival.craftOffsetX,
+ gridY = windowY + GameWindowsConfigs.Survival.craftOffsetY,
+ items = window.craftingItems.asSequence().mapIndexedNotNull { index, it ->
+ if (index % 3 > 1 || index / 3 > 1) {
+ null
+ } else {
+ it ?: gameItemsHolder.fallbackItem.toInventoryItem()
+ }
+ }.asIterable(),
+ itemsInRow = GameWindowsConfigs.Survival.craftGridSize,
+ cellWidth = GameWindowsConfigs.Survival.itemsGridColWidth,
+ cellHeight = GameWindowsConfigs.Survival.itemsGridRowHeight,
+ )
+
+ window.craftResult?.draw(
+ spriteBatch = spriteBatch,
+ shapeRenderer = shapeRenderer,
+ x = windowX + GameWindowsConfigs.Survival.craftResultOffsetX,
+ y = windowY + GameWindowsConfigs.Survival.craftResultOffsetY
+ )
+
+ window.selectedItem?.drawSelected(
+ spriteBatch = spriteBatch,
+ x = Gdx.input.x * (viewport.width / Gdx.graphics.width),
+ y = Gdx.input.y * (viewport.height / Gdx.graphics.height)
+ )
+ }
+
+ companion object {
+ private const val SURVIVAL_WINDOW_KEY = "survival"
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsConfigs.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsConfigs.kt
--- /dev/null
@@ -0,0 +1,73 @@
+package ru.deadsoftware.cavedroid.game.windows
+
+object GameWindowsConfigs {
+ data object Creative {
+ const val scrollIndicatorMarginLeft = 156f
+ const val scrollIndicatorMarginTop = 18f
+ const val scrollIndicatorFullHeight = 72f
+
+ const val itemsGridMarginLeft = 8f
+ const val itemsGridMarginTop = 18f
+
+ const val itemsGridRowHeight = 18f
+ const val itemsGridColWidth = 18f
+
+ const val itemsInRow = 8
+ const val itemsInCol = 5
+
+ const val invItems = 9
+
+ const val playerInventoryOffsetFromBottom = 24f
+
+ val itemsOnPage get() = itemsInCol * itemsInRow
+ }
+
+ data object Survival {
+ 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 portraitMarginLeft = 24f
+ const val portraitMarginTop = 8f
+ const val portraitWidth = 48f
+ const val portraitHeight = 68f
+
+ const val craftGridSize = 2
+
+ const val craftOffsetX = 98f
+ const val craftOffsetY = 18f
+
+ const val craftResultOffsetX = 154f
+ const val craftResultOffsetY = 28f
+ }
+
+ data object Crafting {
+ const val itemsGridMarginLeft = 8f
+ const val itemsGridMarginTop = 84f
+
+ const val itemsGridRowHeight = 18f
+ const val itemsGridColWidth = 18f
+
+ const val itemsInRow = 9
+ const val itemsInCol = 5
+
+ const val hotbarOffsetFromBottom = 24f
+ const val hotbarCells = 9
+
+ const val craftGridSize = 3
+
+ const val craftOffsetX = 30f
+ const val craftOffsetY = 18f
+
+ const val craftResultOffsetX = 128f
+ const val craftResultOffsetY = 36f
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsManager.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/GameWindowsManager.kt
--- /dev/null
@@ -0,0 +1,46 @@
+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
--- /dev/null
@@ -0,0 +1,12 @@
+package ru.deadsoftware.cavedroid.game.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+abstract class AbstractInventoryWindow {
+
+ abstract val type: GameUiWindow
+
+ abstract var selectedItem: InventoryItem?
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CraftingInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CraftingInventoryWindow.kt
--- /dev/null
@@ -0,0 +1,15 @@
+package ru.deadsoftware.cavedroid.game.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class CraftingInventoryWindow(
+ override val type: GameUiWindow,
+) : AbstractInventoryWindow() {
+
+ override var selectedItem: InventoryItem? = null
+
+ val craftingItems = MutableList<InventoryItem?>(9) { null }
+
+ var craftResult: InventoryItem? = null
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CreativeInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/CreativeInventoryWindow.kt
--- /dev/null
@@ -0,0 +1,10 @@
+package ru.deadsoftware.cavedroid.game.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class CreativeInventoryWindow(
+ override val type: GameUiWindow,
+) : AbstractInventoryWindow() {
+ override var selectedItem: InventoryItem? = null
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/SurvivalInventoryWindow.kt b/core/src/ru/deadsoftware/cavedroid/game/windows/inventory/SurvivalInventoryWindow.kt
--- /dev/null
@@ -0,0 +1,15 @@
+package ru.deadsoftware.cavedroid.game.windows.inventory
+
+import ru.deadsoftware.cavedroid.game.GameUiWindow
+import ru.deadsoftware.cavedroid.game.model.item.InventoryItem
+
+class SurvivalInventoryWindow(
+ override val type: GameUiWindow,
+) : AbstractInventoryWindow() {
+
+ override var selectedItem: InventoryItem? = null
+
+ val craftingItems = MutableList<InventoryItem?>(9) { null }
+
+ var craftResult: InventoryItem? = null
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java
--- /dev/null
@@ -0,0 +1,218 @@
+package ru.deadsoftware.cavedroid.game.world;
+
+import kotlin.Pair;
+import ru.deadsoftware.cavedroid.game.GameItemsHolder;
+import ru.deadsoftware.cavedroid.game.GameScope;
+import ru.deadsoftware.cavedroid.game.mobs.MobsController;
+import ru.deadsoftware.cavedroid.game.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.misc.utils.MeasureUnitsUtilsKt;
+
+import javax.annotation.CheckForNull;
+import javax.inject.Inject;
+
+@GameScope
+public class GameWorld {
+
+ private final DropController mDropController;
+ private final MobsController mMobsController;
+ private final GameItemsHolder mGameItemsHolder;
+
+ private final int mWidth;
+ private final int mHeight;
+ private final Block[][] mForeMap;
+ private final Block[][] mBackMap;
+
+ private final WorldGeneratorConfig mWorldConfig = WorldGeneratorConfig.Companion.getDefault();
+
+ @Inject
+ public GameWorld(DropController dropController,
+ MobsController mobsController,
+ GameItemsHolder gameItemsHolder,
+ @CheckForNull Block[][] foreMap,
+ @CheckForNull Block[][] backMap) {
+ mDropController = dropController;
+ mMobsController = mobsController;
+ mGameItemsHolder = gameItemsHolder;
+
+ boolean isNewGame = foreMap == null || backMap == null;
+
+ if (isNewGame) {
+ mWidth = mWorldConfig.getWidth();
+ mHeight = mWorldConfig.getHeight();
+ Pair<Block[][], Block[][]> maps = new GameWorldGenerator(mWorldConfig, mGameItemsHolder).generate();
+ mForeMap = maps.getFirst();
+ mBackMap = maps.getSecond();
+ mMobsController.getPlayer().respawn(this, mGameItemsHolder);
+ } else {
+ mForeMap = foreMap;
+ mBackMap = backMap;
+ mWidth = mForeMap.length;
+ mHeight = mForeMap[0].length;
+ }
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * @deprecated for kotlin use {@link MeasureUnitsUtilsKt#getPx } extension val
+ */
+ @Deprecated
+ public float getWidthPx() {
+ return MeasureUnitsUtilsKt.getPx(mWidth);
+ }
+
+ /**
+ * @deprecated for kotlin use {@link MeasureUnitsUtilsKt#getPx } extension val
+ */
+ @Deprecated
+ public float getHeightPx() {
+ return MeasureUnitsUtilsKt.getPx(mHeight);
+ }
+
+ public Block[][] getFullForeMap() {
+ return mForeMap;
+ }
+
+ public Block[][] getFullBackMap() {
+ return mBackMap;
+ }
+
+ private int transformX(int x) {
+ x = x % getWidth();
+ if (x < 0) {
+ x = getWidth() - Math.abs(x);
+ }
+ return x;
+ }
+
+ 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) {
+ }
+ 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) {
+ }
+ }
+
+ private boolean isSameSlab(Block slab1, Block slab2) {
+ if (!(slab1 instanceof Block.Slab) || !(slab2 instanceof Block.Slab)) {
+ return false;
+ }
+
+ return slab1.getParams().getKey().equals(((Block.Slab) slab2).getOtherPartBlockKey())
+ || slab1.getParams().getKey().equals(slab2.getParams().getKey());
+ }
+
+ public boolean hasForeAt(int x, int y) {
+ return getMap(x, y, 0) != mGameItemsHolder.getFallbackBlock();
+ }
+
+ public boolean hasBackAt(int x, int y) {
+ return getMap(x, y, 1) != mGameItemsHolder.getFallbackBlock();
+ }
+
+ public Block getForeMap(int x, int y) {
+ return getMap(x, y, 0);
+ }
+
+ public void setForeMap(int x, int y, Block block) {
+ setMap(x, y, 0, block);
+ }
+
+ public void resetForeMap(int x, int y) {
+ setForeMap(x, y, mGameItemsHolder.getFallbackBlock());
+ }
+
+ public Block getBackMap(int x, int y) {
+ return getMap(x, y, 1);
+ }
+
+ public void setBackMap(int x, int y, Block block) {
+ setMap(x, y, 1, block);
+ }
+
+ public boolean placeToForeground(int x, int y, Block value) {
+ if (!hasForeAt(x, y) || value == mGameItemsHolder.getFallbackBlock() || !getForeMap(x, y).hasCollision()) {
+ setForeMap(x, y, value);
+ return true;
+ } else if (value instanceof Block.Slab && isSameSlab(value, getForeMap(x, y))) {
+ setForeMap(x, y, mGameItemsHolder.getBlock(((Block.Slab) value).getFullBlockKey()));
+ return true;
+ }
+ return false;
+ }
+
+ 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"))) {
+ setBackMap(x, y, value);
+ return true;
+ }
+ return false;
+ }
+
+ private void playerDurateTool() {
+ final InventoryItem playerCurrentItem = mMobsController.getPlayer().getCurrentItem();
+ if (mMobsController.getPlayer().getCurrentItem().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;
+ if (item.isTool() && block.getParams().getToolType() != item.getClass()) {
+ toolLevel = 0;
+ }
+ return toolLevel >= block.getParams().getToolLevel();
+ }
+
+ public void destroyForeMap(int x, int y) {
+ Block block = getForeMap(x, y);
+ 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()));
+ }
+ }
+ playerDurateTool();
+ placeToForeground(x, y, mGameItemsHolder.getFallbackBlock());
+ }
+
+ public WorldGeneratorConfig getWorldConfig() {
+ return mWorldConfig;
+ }
+
+ public void destroyBackMap(int x, int y) {
+ Block block = getBackMap(x, y);
+ 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()));
+ }
+ }
+ playerDurateTool();
+ placeToBackground(x, y, mGameItemsHolder.getFallbackBlock());
+ }
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldBlocksLogicControllerTask.kt b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldBlocksLogicControllerTask.kt
--- /dev/null
@@ -0,0 +1,62 @@
+package ru.deadsoftware.cavedroid.game.world
+
+import com.badlogic.gdx.utils.Timer.Task
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.actions.getRequiresBlockAction
+import ru.deadsoftware.cavedroid.game.actions.updateblock.IUpdateBlockAction
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import javax.inject.Inject
+
+@GameScope
+class GameWorldBlocksLogicControllerTask @Inject constructor(
+ private val gameWorld: GameWorld,
+ private val updateBlockActions: Map<String, @JvmSuppressWildcards IUpdateBlockAction>,
+ private val mobsController: MobsController,
+) : Task() {
+
+ private var currentRelativeChunk = 0
+
+ private fun getChunkStart(): Int {
+ val playerX = mobsController.player.mapX
+ val playerChunk = playerX / CHUNK_WIDTH
+ val currentChunk = playerChunk - CHUNKS / 2 + currentRelativeChunk
+
+ return currentChunk * 16
+ }
+
+ private fun updateBlock(x: Int, y: Int) {
+ val block = gameWorld.getForeMap(x, y)
+
+ if (block.isNone()) {
+ return
+ }
+
+ val blockKey = block.params.key
+ val action = updateBlockActions[blockKey]
+ ?: updateBlockActions.getRequiresBlockAction().takeIf { block.params.requiresBlock }
+
+ action?.update(x, y)
+ }
+
+ override fun run() {
+ val startX = getChunkStart()
+
+ for (y in gameWorld.height downTo 0) {
+ for (x in startX ..< startX + CHUNK_WIDTH) {
+ updateBlock(x, y)
+ }
+ }
+
+ currentRelativeChunk = (currentRelativeChunk + 1) % CHUNKS
+ }
+
+ companion object {
+ private const val TAG = "GameWorldBlocksLogicControllerTask"
+
+ private const val CHUNK_WIDTH = 16
+ private const val CHUNKS = 3
+
+ const val WORLD_BLOCKS_LOGIC_UPDATE_INTERVAL_SEC = .1f
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java
--- /dev/null
@@ -0,0 +1,223 @@
+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/GameWorldGenerator.kt b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldGenerator.kt
--- /dev/null
@@ -0,0 +1,291 @@
+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.random.Random
+
+class GameWorldGenerator(
+ private val config: WorldGeneratorConfig,
+ private val gameItemsHolder: GameItemsHolder,
+) {
+
+ private val random = Random(config.seed)
+
+ private val foreMap by lazy { Array(config.width) { Array(config.height) { gameItemsHolder.fallbackBlock } } }
+ private val backMap by lazy { Array(config.width) { Array(config.height) { gameItemsHolder.fallbackBlock } } }
+
+ private val heights by lazy { generateHeights() }
+ private val biomesMap by lazy { generateBiomes() }
+
+ private val plainsPlants = listOf("dandelion", "rose", "tallgrass")
+ private val mushrooms = listOf("mushroom_brown", "mushroom_red",)
+
+ private fun generateHeights(): IntArray {
+ val surfaceHeightRange = config.minSurfaceHeight .. config.maxSurfaceHeight
+ val result = IntArray(config.width)
+
+ result[0] = surfaceHeightRange.random(random)
+
+ for (x in 1 ..< config.width) {
+ val previous = result[x - 1]
+ var d = random.nextInt(-5, 6).let { if (it !in -4..4) it / abs(it) else 0 }
+
+ if (previous + d !in surfaceHeightRange) { d = -d }
+
+ if (result.lastIndex - x < abs(result[0] - previous) * 3) {
+ d = result[0].compareTo(previous).let { if (it != 0) it / abs(it) else 0 }
+ }
+
+ result[x] = result[x - 1] + d
+ }
+
+ return result
+ }
+
+ private fun generateBiomes(): Map<Int, Biome> {
+ val xSequence = sequence {
+ var lastX = 0
+ var count = 0
+
+ while (lastX < config.width - config.minBiomeSize - 1) {
+ yield(lastX)
+
+ lastX = random.nextInt(lastX + config.minBiomeSize, config.width)
+ count++
+ }
+ }
+
+ return xSequence.associateWith { config.biomes.random(random) }
+ }
+
+ private fun winterBiome(x: Int) {
+ assert(x in 0 ..< config.width) { "x not in range of world width" }
+
+ val surfaceHeight = heights[x]
+
+ val grass = gameItemsHolder.getBlock("grass_snowed")
+ val bedrock = gameItemsHolder.getBlock("bedrock")
+ val dirt = gameItemsHolder.getBlock("dirt")
+ val stone = gameItemsHolder.getBlock("stone")
+
+ foreMap[x][surfaceHeight] = grass
+ foreMap[x][config.height - 1] = bedrock
+ backMap[x][surfaceHeight] = grass
+ backMap[x][config.height - 1] = bedrock
+
+ for (y in min(surfaceHeight + 1, config.seaLevel) ..< config.height - 1) {
+ if (y <= surfaceHeight) {
+ backMap[x][y] = dirt
+ continue
+ }
+
+ foreMap[x][y] = when {
+ y < surfaceHeight + random.nextInt(5, 8) -> dirt
+ else -> stone
+ }
+ backMap[x][y] = foreMap[x][y]
+ }
+ }
+
+ private fun plainsBiome(x: Int) {
+ assert(x in 0 ..< config.width) { "x not in range of world width" }
+
+ val surfaceHeight = heights[x]
+
+ val grass = gameItemsHolder.getBlock("grass")
+ val bedrock = gameItemsHolder.getBlock("bedrock")
+ val dirt = gameItemsHolder.getBlock("dirt")
+ val stone = gameItemsHolder.getBlock("stone")
+
+ foreMap[x][surfaceHeight] = grass
+ foreMap[x][config.height - 1] = bedrock
+ backMap[x][surfaceHeight] = grass
+ backMap[x][config.height - 1] = bedrock
+
+ for (y in min(surfaceHeight + 1, config.seaLevel) ..< config.height - 1) {
+ if (y <= surfaceHeight) {
+ backMap[x][y] = dirt
+ continue
+ }
+
+ foreMap[x][y] = when {
+ y < surfaceHeight + random.nextInt(5, 8) -> dirt
+ else -> stone
+ }
+ backMap[x][y] = foreMap[x][y]
+ }
+
+ val plant = random.nextInt(100)
+ if (surfaceHeight < config.seaLevel) {
+ if (plant < 10) {
+ generateOak(x)
+ } else if (plant < 40) {
+ generateTallGrass(x)
+ }
+ }
+ }
+
+ private fun desertBiome(x: Int) {
+ assert(x in 0 ..< config.width) { "x not in range of world width" }
+
+ val surfaceHeight = heights[x]
+
+ val sand = gameItemsHolder.getBlock("sand")
+ val bedrock = gameItemsHolder.getBlock("bedrock")
+ val sandstone = gameItemsHolder.getBlock("sandstone")
+ val stone = gameItemsHolder.getBlock("stone")
+
+
+ foreMap[x][surfaceHeight] = sand
+ foreMap[x][config.height - 1] = bedrock
+ backMap[x][surfaceHeight] = sand
+ backMap[x][config.height - 1] = bedrock
+
+ for (y in min(surfaceHeight + 1, config.seaLevel) ..< config.height - 1) {
+ if (y <= surfaceHeight) {
+ backMap[x][y] = sand
+ continue
+ }
+
+ foreMap[x][y] = when {
+ y < surfaceHeight + random.nextInt(5, 8) -> sand
+ y < surfaceHeight + random.nextInt(0, 2) -> sandstone
+ else -> stone
+ }
+ backMap[x][y] = foreMap[x][y]
+ }
+
+ val plant = random.nextInt(100)
+ if (surfaceHeight < config.seaLevel) {
+ if (plant < 5) {
+ generateCactus(x)
+ } else if (plant < 10) {
+ generateDeadBush(x)
+ }
+ }
+ }
+
+ private fun fillWater() {
+ val water = gameItemsHolder.getBlock("water")
+
+ for (x in 0 ..< config.width) {
+ for (y in config.seaLevel ..< config.height) {
+ if (foreMap[x][y] != gameItemsHolder.fallbackBlock) {
+ break
+ }
+
+ foreMap[x][y] = water
+ }
+ }
+ }
+
+ private fun generateCactus(x: Int) {
+ val cactus = gameItemsHolder.getBlock("cactus")
+ val cactusHeight = random.nextInt(3)
+ val h = heights[x] - 1
+
+ for (y in h downTo max(0, h - cactusHeight)) {
+ foreMap[x][y] = cactus
+ }
+ }
+
+ private fun generateOak(x: Int) {
+ val log = gameItemsHolder.getBlock("log_oak")
+ val leaves = gameItemsHolder.getBlock("leaves_oak")
+ val h = heights[x] - 1
+ val treeH = random.nextInt(5, 7)
+ 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)) {
+ for (y in height .. height + treeH - 4) {
+ foreMap[x1][y] = leaves
+ backMap[x1][y] = leaves
+ }
+ if (random.nextInt(15) < 3) {
+ foreMap[x1][heights[x1] - 1] = gameItemsHolder.getBlock(mushrooms.random(random))
+ }
+ }
+
+ 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
+ if (h > 0) {
+ foreMap[x][h] = tallGrass
+ }
+ }
+
+ private fun generateDeadBush(x: Int) {
+ val bush = gameItemsHolder.getBlock("deadbush")
+ val h = heights[x] - 1
+ if (h > 0) {
+ foreMap[x][h] = bush
+ }
+ }
+
+ private fun generateOres(x : Int) {
+ val stone = gameItemsHolder.getBlock("stone")
+ val coal = gameItemsHolder.getBlock("coal_ore")
+ val iron = gameItemsHolder.getBlock("iron_ore")
+ val gold = gameItemsHolder.getBlock("gold_ore")
+ val diamond = gameItemsHolder.getBlock("diamond_ore")
+ val lapis = gameItemsHolder.getBlock("lapis_ore")
+
+ for (y in heights[x] ..< config.height) {
+ val res = random.nextInt(10000)
+
+ val h = config.height - y
+ val block = when {
+ res in 0..<25 && h < 16 -> diamond
+ res in 25 ..< 50 && h < 32 -> gold
+ res in 50 ..< 250 && h < 64 -> iron
+ res in 250 ..< 450 && h < 128 -> coal
+ res in 450 ..< (450 + (25 - (abs(h - 16) * (25 / 16)))) -> lapis
+ else -> null
+ }
+
+ if (block != null && foreMap[x][y] == stone) {
+ foreMap[x][y] = block
+ }
+ }
+ }
+
+ /**
+ * Generate world
+ */
+ fun generate(): Pair<Array<Array<Block>>, Array<Array<Block>>> {
+ var biome = Biome.PLAINS
+
+ for (x in 0 until config.width) {
+ biome = biomesMap[x] ?: biome
+
+ when (biome) {
+ Biome.PLAINS -> plainsBiome(x)
+ Biome.DESERT -> desertBiome(x)
+ Biome.WINTER -> winterBiome(x)
+ }
+
+ generateOres(x)
+ }
+
+ fillWater()
+
+ return Pair(foreMap, backMap)
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldMobDamageControllerTask.kt b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldMobDamageControllerTask.kt
--- /dev/null
@@ -0,0 +1,38 @@
+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.misc.utils.forEachBlockInArea
+import javax.inject.Inject
+import kotlin.math.max
+
+@GameScope
+class GameWorldMobDamageControllerTask @Inject constructor(
+ private val mobsController: MobsController,
+ private val gameWorld: GameWorld,
+ private val gameItemsHolder: GameItemsHolder,
+) : Timer.Task() {
+
+ override fun run() {
+ sequence {
+ yield(mobsController.player)
+ yieldAll(mobsController.mobs)
+ }.forEach { mob ->
+ forEachBlockInArea(mob) { x, y ->
+ val foregroundBlock = gameWorld.getForeMap(x, y)
+ val backgroundBlock = gameWorld.getBackMap(x, y)
+
+ mob.damage(max(foregroundBlock.params.damage, backgroundBlock.params.damage))
+ }
+ }
+
+
+ }
+
+ companion object {
+ const val ENVIRONMENTAL_MOB_DAMAGE_INTERVAL_SEC = 0.5f
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/MenuProc.java b/core/src/ru/deadsoftware/cavedroid/menu/MenuProc.java
index f96d4d42756545c2d13cfc20de9a74655f986c15..6accb5726147700119cb351f1d327d7760df4937 100644 (file)
public class Input {
private void startNewGame(int gameMode) {
- mMainConfig.getCaveGame().newGame();
+ mMainConfig.getCaveGame().newGame(gameMode);
}
public void newGameClicked() {
private Menu mCurrentMenu;
@Inject
- public MenuProc(MainConfig mainConfig) {
+ public MenuProc(
+ MainConfig mainConfig,
+ MenuMain.Factory menuMainFactory,
+ MenuNewGame.Factory menuNewGameFactory
+ ) {
super(mainConfig.getWidth(), mainConfig.getHeight());
mMainConfig = mainConfig;
Input menuInput = new Input();
- mMenuMain = new MenuMain(getWidth(), getHeight(), this::drawButton, mainConfig, menuInput);
- mMenuNewGame = new MenuNewGame(getWidth(), getHeight(), this::drawButton, mainConfig, menuInput);
+ mMenuMain = menuMainFactory.get(getWidth(), getHeight(), this::drawButton, menuInput);
+ mMenuNewGame = menuNewGameFactory.get(getWidth(), getHeight(), this::drawButton, menuInput);
mCurrentMenu = mMenuMain;
}
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/Menu.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/Menu.java
index 9f8df4c25d96d498c6019c1e123ef99a61e05c6e..587d0f622ebbc5df347d482734e75996bc006c3a 100644 (file)
import ru.deadsoftware.cavedroid.menu.objects.ButtonEventListener;
import ru.deadsoftware.cavedroid.menu.objects.ButtonRenderer;
import ru.deadsoftware.cavedroid.misc.Assets;
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader;
import java.util.HashMap;
protected final MainConfig mMainConfig;
protected final MenuProc.Input mMenuInput;
+ protected final AssetLoader mAssetLoader;
private final ButtonRenderer mButtonRenderer;
* @param height Viewport height
* @param buttonRenderer {@link ButtonRenderer} that will draw the buttons of this menu
*/
- Menu(float width, float height, ButtonRenderer buttonRenderer, MainConfig mainConfig, MenuProc.Input menuInput) {
+ Menu(float width,
+ float height,
+ ButtonRenderer buttonRenderer,
+ MainConfig mainConfig,
+ MenuProc.Input menuInput,
+ AssetLoader assetLoader) {
mWidth = width;
mHeight = height;
mButtonRenderer = buttonRenderer;
mMainConfig = mainConfig;
mMenuInput = menuInput;
+ mAssetLoader = assetLoader;
initButtons();
}
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuMain.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuMain.java
index d2dedd23c351f3fc8e91626375e55a0f3b3fd90d..f71183a0583ce95b4c78fa760cb081e5323188e0 100644 (file)
import ru.deadsoftware.cavedroid.menu.objects.Button;
import ru.deadsoftware.cavedroid.menu.objects.ButtonEventListener;
import ru.deadsoftware.cavedroid.menu.objects.ButtonRenderer;
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader;
+import javax.inject.Inject;
import java.util.HashMap;
public class MenuMain extends Menu {
- public MenuMain(float width, float height, ButtonRenderer buttonRenderer, MainConfig mainConfig, MenuProc.Input menuInput) {
- super(width, height, buttonRenderer, mainConfig, menuInput);
+ public MenuMain(float width,
+ float height,
+ ButtonRenderer buttonRenderer,
+ MainConfig mainConfig,
+ MenuProc.Input menuInput,
+ AssetLoader assetLoader) {
+ super(width, height, buttonRenderer, mainConfig, menuInput, assetLoader);
}
@Override
@Override
protected void initButtons() {
- loadButtonsFromJson(Gdx.files.internal("json/menu_main_buttons.json"));
+ loadButtonsFromJson(mAssetLoader.getAssetHandle("json/menu_main_buttons.json"));
if (GameSaver.exists(mMainConfig)) {
getButtons().get("load_game").setType(Button.NORMAL);
}
}
+
+ public static class Factory {
+
+ private final MainConfig mMainConfig;
+ private final AssetLoader mAssetLoader;
+
+ @Inject
+ public Factory(MainConfig mainConfig, AssetLoader assetLoader) {
+ mMainConfig = mainConfig;
+ mAssetLoader = assetLoader;
+ }
+
+ public MenuMain get(float width, float height, ButtonRenderer buttonRenderer, MenuProc.Input menuInput) {
+ return new MenuMain(width, height, buttonRenderer, mMainConfig, menuInput, mAssetLoader);
+ }
+
+ }
+
}
diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuNewGame.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuNewGame.java
index 2c3b38bee9b041a5ae16fd4e2e5a4d1d707aacad..33985ff8206d395939c21c54d474e0017e26c5a3 100644 (file)
package ru.deadsoftware.cavedroid.menu.submenus;
-import com.badlogic.gdx.Gdx;
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;
+import javax.inject.Inject;
import java.util.HashMap;
public class MenuNewGame extends Menu {
- public MenuNewGame(float width, float height, ButtonRenderer buttonRenderer, MainConfig mainConfig, MenuProc.Input menuInput) {
- super(width, height, buttonRenderer, mainConfig, menuInput);
+ public MenuNewGame(float width,
+ float height,
+ ButtonRenderer buttonRenderer,
+ MainConfig mainConfig,
+ MenuProc.Input menuInput,
+ AssetLoader assetLoader) {
+ super(width, height, buttonRenderer, mainConfig, menuInput, assetLoader);
}
@Override
@Override
protected void initButtons() {
- loadButtonsFromJson(Gdx.files.internal("json/menu_new_game_buttons.json"));
+ loadButtonsFromJson(mAssetLoader.getAssetHandle("json/menu_new_game_buttons.json"));
+ }
+
+ public static class Factory {
+
+ private final MainConfig mMainConfig;
+ private final AssetLoader mAssetLoader;
+
+ @Inject
+ public Factory(MainConfig mainConfig, AssetLoader assetLoader) {
+ mMainConfig = mainConfig;
+ mAssetLoader = assetLoader;
+ }
+
+ public MenuNewGame get(float width, float height, ButtonRenderer buttonRenderer, MenuProc.Input menuInput) {
+ return new MenuNewGame(width, height, buttonRenderer, mMainConfig, menuInput, mAssetLoader);
+ }
+
}
}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/Assets.java b/core/src/ru/deadsoftware/cavedroid/misc/Assets.java
index 335e3c0d2239a450908f325c3b94dbaa38982143..b7fd03e132063167fe37cc0ee2d97ac5f2b6090e 100644 (file)
package ru.deadsoftware.cavedroid.misc;
-import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Input;
+import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.ArrayMap;
import com.badlogic.gdx.utils.JsonReader;
import com.badlogic.gdx.utils.JsonValue;
import ru.deadsoftware.cavedroid.game.objects.TouchButton;
+import ru.deadsoftware.cavedroid.misc.utils.AssetLoader;
+import java.io.File;
import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
public class Assets {
+ private static final int BLOCK_DAMAGE_STAGES = 10;
+
public static final JsonReader jsonReader = new JsonReader();
+
+ private static final List<Texture> loadedTextures = new LinkedList<>();
+
public static final Sprite[][] playerSprite = new Sprite[2][4];
public static final Sprite[][] pigSprite = new Sprite[2][2];
+
+ public static final Sprite[] blockDamageSprites = new Sprite[10];
+
public static final HashMap<String, TextureRegion> textureRegions = new HashMap<>();
public static final ArrayMap<String, TouchButton> guiMap = new ArrayMap<>();
private static final GlyphLayout glyphLayout = new GlyphLayout();
- static BitmapFont minecraftFont;
+ public static BitmapFont minecraftFont;
+
+ public static Map<String, Texture> blockTextures = new HashMap<>();
+ public static Map<String, Texture> itemTextures = new HashMap<>();
+
+ public static void dispose() {
+ minecraftFont.dispose();
+ loadedTextures.forEach(Texture::dispose);
+ loadedTextures.clear();
+ }
+
+ private static Texture loadTexture(FileHandle fileHandle) {
+ Texture texture = new Texture(fileHandle);
+ loadedTextures.add(texture);
+ return texture;
+ }
private static TextureRegion flippedRegion(Texture texture, int x, int y, int width, int height) {
return new TextureRegion(texture, x, y + height, width, -height);
return sprite;
}
- private static void loadMob(Sprite[][] sprite, String mob) {
+ private static void loadMob(AssetLoader assetLoader, Sprite[][] sprite, String mob) {
for (int i = 0; i < sprite.length; i++) {
for (int j = 0; j < sprite[i].length; j++) {
- sprite[i][j] = flippedSprite(new Texture(
- Gdx.files.internal("mobs/" + mob + "/" + i + "_" + j + ".png")));
+ sprite[i][j] = flippedSprite(loadTexture(
+ assetLoader.getAssetHandle("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"));
+ for (int i = 0; i < BLOCK_DAMAGE_STAGES; i++) {
+ blockDamageSprites[i] = new Sprite(flippedRegion(blockDamageTexture, i * 16, 0, 16, 16));
+ }
+ }
+
+ private static void setPlayerHeadOrigin() {
+ for (Sprite[] sprites : playerSprite) {
+ sprites[0].setOrigin(sprites[0].getWidth() / 2, sprites[0].getHeight());
+ }
+ }
+
/**
* Loads texture names and sizes from <b>json/texture_regions.json</b>, cuts them to TextureRegions
* and puts to {@link #textureRegions} HashMap
*/
- private static void loadJSON() {
- JsonValue json = jsonReader.parse(Gdx.files.internal("json/texture_regions.json"));
+ private static void loadJSON(AssetLoader assetLoader) {
+ JsonValue json = jsonReader.parse(assetLoader.getAssetHandle("json/texture_regions.json"));
for (JsonValue file = json.child(); file != null; file = file.next()) {
- Texture texture = new Texture(Gdx.files.internal(file.name() + ".png"));
+ Texture texture = loadTexture(assetLoader.getAssetHandle(file.name() + ".png"));
if (file.size == 0) {
textureRegions.put(file.name(),
flippedRegion(texture, 0, 0, texture.getWidth(), texture.getHeight()));
}
}
- public static void load() {
- loadMob(playerSprite, "char");
- loadMob(pigSprite, "pig");
- loadJSON();
- minecraftFont = new BitmapFont(Gdx.files.internal("font.fnt"), true);
+ private static int getMouseKey(String name) {
+ switch (name) {
+ case "Left":
+ return Input.Buttons.LEFT;
+ case "Right":
+ return Input.Buttons.RIGHT;
+ case "Middle":
+ return Input.Buttons.MIDDLE;
+ case "Back":
+ return Input.Buttons.BACK;
+ case "Forward":
+ return Input.Buttons.FORWARD;
+ default:
+ return -1;
+ }
+ }
+
+ private static void loadTouchButtonsFromJSON(AssetLoader assetLoader) {
+ JsonValue json = Assets.jsonReader.parse(assetLoader.getAssetHandle("json/touch_buttons.json"));
+ for (JsonValue key = json.child(); key != null; key = key.next()) {
+ float x = key.getFloat("x");
+ float y = key.getFloat("y");
+ float w = key.getFloat("w");
+ float h = key.getFloat("h");
+ boolean mouse = Assets.getBooleanFromJson(key, "mouse", false);
+ String name = key.getString("key");
+ int code = mouse ? getMouseKey(name) : Input.Keys.valueOf(name);
+ if (x < 0) {
+ x = assetLoader.getGameRendererWidth() + x;
+ }
+ if (y < 0) {
+ y = assetLoader.getGameRendererHeight() + y;
+ }
+ Assets.guiMap.put(key.name(), new TouchButton(new Rectangle(x, y, w, h), code, mouse));
+ }
+
+ }
+
+ private static Texture resolveTexture(AssetLoader assetLoader, String textureName, String lookUpPath, Map<String, Texture> cache) {
+ if (cache.containsKey(textureName)) {
+ return cache.get(textureName);
+ }
+
+ final Texture texture = loadTexture(assetLoader.getAssetHandle(lookUpPath + File.separator + textureName + ".png"));
+ cache.put(textureName, texture);
+
+ return texture;
+ }
+
+ public static Texture resolveItemTexture(AssetLoader assetLoader, String textureName) {
+ return resolveTexture(assetLoader, textureName, "textures/items", itemTextures);
+ }
+
+ public static Texture resolveBlockTexture(AssetLoader assetLoader, String textureName) {
+ return resolveTexture(assetLoader, textureName, "textures/blocks", blockTextures);
+ }
+
+ private static void loadAllPngsFromDirInto(FileHandle dir, Map<String, Texture> loadInto) {
+ for (FileHandle handle : dir.list((d, name) -> name.endsWith(".png"))) {
+ loadInto.put(handle.nameWithoutExtension(), loadTexture(handle));
+ }
+ }
+
+ private static void loadItems(AssetLoader assetLoader) {
+ final FileHandle itemsDir = assetLoader.getAssetHandle("textures/items");
+ loadAllPngsFromDirInto(itemsDir, itemTextures);
+ }
+
+ private static void loadBlocks(AssetLoader assetLoader) {
+ final FileHandle blocksDir = assetLoader.getAssetHandle("textures/blocks");
+ loadAllPngsFromDirInto(blocksDir, blockTextures);
+ }
+
+ public static void load(final AssetLoader assetLoader) {
+ loadMob(assetLoader, playerSprite, "char");
+ loadMob(assetLoader, pigSprite, "pig");
+ loadBlockDamage(assetLoader);
+ loadJSON(assetLoader);
+ loadBlocks(assetLoader);
+ loadItems(assetLoader);
+ loadTouchButtonsFromJSON(assetLoader);
+ setPlayerHeadOrigin();
+ minecraftFont = new BitmapFont(assetLoader.getAssetHandle("font.fnt"), true);
minecraftFont.getData().setScale(.375f);
}
return json.has(name) ? json.getInt(name) : defaultValue;
}
+ public static float getFloatFromJson(JsonValue json, String name, float defaultValue) {
+ return json.has(name) ? json.getFloat(name) : defaultValue;
+ }
+
public static String getStringFromJson(JsonValue json, String name, String defaultValue) {
return json.has(name) ? json.getString(name) : defaultValue;
}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/ControlMode.java b/core/src/ru/deadsoftware/cavedroid/misc/ControlMode.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package ru.deadsoftware.cavedroid.misc;
-
-public enum ControlMode {
- WALK,
- CURSOR
-}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/Renderer.java b/core/src/ru/deadsoftware/cavedroid/misc/Renderer.java
index b59e977c5045c05195f72e870c2c3c0b82d69335..c2577d36e4940a996468b3dbb382369b19eb000d 100644 (file)
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
+import com.badlogic.gdx.math.Rectangle;
public abstract class Renderer implements InputProcessor {
protected final ShapeRenderer shaper;
protected final SpriteBatch spriter;
private final OrthographicCamera camera;
+ private final Rectangle mCameraViewport;
protected Renderer() {
this(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
shaper.setProjectionMatrix(camera.combined);
spriter = new SpriteBatch();
spriter.setProjectionMatrix(camera.combined);
+
+ mCameraViewport =
+ new Rectangle(camera.position.x, camera.position.y, camera.viewportWidth, camera.viewportHeight);
}
public float getWidth() {
public void setCamPos(float x, float y) {
camera.position.set(x, y, 0);
+ mCameraViewport.x = x;
+ mCameraViewport.y = y;
+ }
+
+ public Rectangle getCameraViewport() {
+ return mCameraViewport;
}
public void setFontScale(float scale) {
}
@Override
- public boolean scrolled(int amount) {
+ public boolean scrolled(float amountX, float amountY) {
return false;
}
+ @Override
+ public boolean touchCancelled(int i, int i1, int i2, int i3) {
+ return false;
+ }
}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/ArrayMapExtensions.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/ArrayMapExtensions.kt
--- /dev/null
@@ -0,0 +1,9 @@
+package ru.deadsoftware.cavedroid.misc.utils
+
+import com.badlogic.gdx.utils.ObjectMap
+
+object ArrayMapExtensions {
+ operator fun <K, V> ObjectMap.Entry<K, V>.component1(): K = this.key
+
+ operator fun <K, V> ObjectMap.Entry<K, V>.component2(): V = this.value
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/AssetLoader.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/AssetLoader.kt
--- /dev/null
@@ -0,0 +1,35 @@
+package ru.deadsoftware.cavedroid.misc.utils
+
+import com.badlogic.gdx.Gdx
+import com.badlogic.gdx.files.FileHandle
+import ru.deadsoftware.cavedroid.MainConfig
+import ru.deadsoftware.cavedroid.game.GameScope
+import java.io.File
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class AssetLoader @Inject constructor(
+ private val mainConfig: MainConfig,
+) {
+
+ fun getAssetHandle(path: String): FileHandle {
+ val texturePackPath =
+ mainConfig.assetsPackPath?.let { if (!it.endsWith(File.separator)) "$it${File.separator}" else it }
+
+ return if (texturePackPath == null) {
+ Gdx.files.internal(path)
+ } else {
+ Gdx.files.absolute("$texturePackPath$path")
+ }
+ }
+
+ fun getGameRendererWidth(): Float {
+ return mainConfig.width
+ }
+
+ fun getGameRendererHeight(): Float {
+ return mainConfig.height
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/ItemUtils.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/ItemUtils.kt
--- /dev/null
@@ -0,0 +1,8 @@
+package ru.deadsoftware.cavedroid.misc.utils
+
+import ru.deadsoftware.cavedroid.game.GameItemsHolder.Companion.FALLBACK_ITEM_KEY
+import ru.deadsoftware.cavedroid.game.model.item.Item
+
+fun Item.isFallback(): Boolean {
+ return this.params.key == FALLBACK_ITEM_KEY
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/MeasureUnitsUtils.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/MeasureUnitsUtils.kt
--- /dev/null
@@ -0,0 +1,18 @@
+package ru.deadsoftware.cavedroid.misc.utils
+
+import com.badlogic.gdx.math.MathUtils
+
+/**
+ * Converts this value in BLOCKS into pixels
+ */
+val Float.px get() = this * 16f
+
+/**
+ * Converts this value in BLOCKS into pixels
+ */
+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
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/RenderingUtils.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/RenderingUtils.kt
--- /dev/null
@@ -0,0 +1,71 @@
+package ru.deadsoftware.cavedroid.misc.utils
+
+import com.badlogic.gdx.graphics.Color
+import com.badlogic.gdx.graphics.g2d.GlyphLayout
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+import com.badlogic.gdx.math.Rectangle
+import ru.deadsoftware.cavedroid.misc.Assets
+
+private fun Rectangle.shifted(shift: Float) = Rectangle(x + shift, y, width, height)
+
+private fun Rectangle.getLazyShifts(worldWidthPx: Float)
+ = Triple(
+ first = lazy { shifted(0f) },
+ second = lazy { shifted(-worldWidthPx) },
+ third = lazy { shifted(worldWidthPx) }
+ )
+
+fun Rectangle.cycledInsideWorld(
+ viewport: Rectangle,
+ worldWidthPx: Float,
+): Rectangle? {
+ val (notShifted, shiftedLeft, shiftedRight) = getLazyShifts(worldWidthPx)
+
+ return when {
+ viewport.overlaps(notShifted.value) -> notShifted.value
+ viewport.overlaps(shiftedLeft.value) -> shiftedLeft.value
+ viewport.overlaps(shiftedRight.value) -> shiftedRight.value
+ else -> null
+ }
+}
+
+fun forEachBlockInArea(
+ area: Rectangle,
+ func: (x: Int, y: Int) -> Unit,
+) {
+ val startMapX = area.x.bl
+ val endMapX = (area.x + area.width - 1f).bl
+ val startMapY = area.y.bl
+ val endMapY = (area.y + area.height - 1f).bl
+
+ for (x in startMapX..endMapX) {
+ for (y in startMapY..endMapY) {
+ func(x, y)
+ }
+ }
+}
+
+@JvmOverloads
+fun SpriteBatch.drawString(str: String, x: Float, y: Float, color: Color = Color.WHITE): GlyphLayout {
+ Assets.minecraftFont.color = color
+ return Assets.minecraftFont.draw(this, str, x, y)
+}
+
+/**
+ * Parses hex color string into [Color]
+ * Format is strictly #FFFFFF
+ */
+fun colorFromHexString(hex: String): Color {
+ if (hex[0] != '#' || hex.length != 7) {
+ return Color.WHITE
+ }
+
+ var rgba = try {
+ hex.substring(1).toInt(16)
+ } catch (e: NumberFormatException) {
+ 0xffffff
+ }
+
+ rgba = (rgba shl 8) or 0xFF
+ return Color(rgba)
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/SpriteOrigin.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/SpriteOrigin.kt
--- /dev/null
@@ -0,0 +1,31 @@
+package ru.deadsoftware.cavedroid.misc.utils
+
+import com.badlogic.gdx.graphics.g2d.Sprite
+
+/**
+ * An origin of a [com.badlogic.gdx.graphics.g2d.Sprite]
+ *
+ * x and y must be between 0 and 1 in percents from sprite size
+ */
+data class SpriteOrigin(
+ val x: Float,
+ val y: Float,
+) {
+
+ init {
+ assert(x in 0f..1f)
+ assert(y in 0f..1f)
+ }
+
+ fun getFlipped(flipX: Boolean, flipY: Boolean): SpriteOrigin {
+ return SpriteOrigin(
+ x = if (flipX) 1 - x else x,
+ y = if (flipY) 1 - y else y,
+ )
+ }
+
+ fun applyToSprite(sprite: Sprite) {
+ sprite.setOrigin(sprite.width * x, sprite.height * y)
+ }
+
+}
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/SpriteUtils.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/SpriteUtils.kt
--- /dev/null
@@ -0,0 +1,29 @@
+package ru.deadsoftware.cavedroid.misc.utils
+
+import com.badlogic.gdx.graphics.g2d.Sprite
+import com.badlogic.gdx.graphics.g2d.SpriteBatch
+
+/**
+ * Draw sprite at given position rotated by [rotation] degrees
+ */
+@JvmOverloads
+fun SpriteBatch.drawSprite(
+ sprite: Sprite,
+ x: Float,
+ y: Float,
+ rotation: Float = 0f,
+ width: Float = sprite.regionWidth.toFloat(),
+ height: Float = sprite.regionHeight.toFloat(),
+) {
+ sprite.setPosition(x, y)
+ sprite.setSize(width, height)
+ sprite.rotation = rotation
+ sprite.draw(this)
+
+ sprite.setSize(sprite.regionWidth.toFloat(), sprite.regionHeight.toFloat())
+ sprite.rotation = 0f
+}
+
+fun Sprite.applyOrigin(origin: SpriteOrigin) {
+ origin.applyToSprite(this)
+}
\ No newline at end of file
diff --git a/core/src/ru/deadsoftware/cavedroid/misc/utils/mobs/MobSprites.kt b/core/src/ru/deadsoftware/cavedroid/misc/utils/mobs/MobSprites.kt
--- /dev/null
@@ -0,0 +1,47 @@
+package ru.deadsoftware.cavedroid.misc.utils.mobs
+
+import ru.deadsoftware.cavedroid.game.mobs.Mob
+import ru.deadsoftware.cavedroid.game.mobs.Mob.Direction
+import ru.deadsoftware.cavedroid.misc.Assets
+
+object MobSprites {
+
+ object Player {
+
+ fun getBackgroundHand() = Assets.playerSprite[1][2]
+
+ fun getForegroundHand() = Assets.playerSprite[0][2]
+
+ fun getBackgroundLeg() = Assets.playerSprite[1][3]
+
+ fun getForegroundLeg() = Assets.playerSprite[0][3]
+
+ fun getHead(direction: Mob.Direction) = Assets.playerSprite[direction.index][0]
+
+ fun getBody(direction: Direction) = Assets.playerSprite[direction.index][1]
+
+ fun getBodyRelativeX() = 2
+
+ fun getBodyRelativeY() = 8
+
+ fun getLegsRelativeY() = 20
+
+ }
+
+ object Pig {
+
+ fun getForegroundLeg() = Assets.pigSprite[0][1]
+
+ fun getBackgroundLeg() = Assets.pigSprite[1][1]
+
+ fun getBody(direction: Direction) = Assets.pigSprite[direction.index][0]
+
+ fun getLeftLegRelativeX(direction: Direction) = 9 - direction.index * 9
+
+ fun getRightLegRelativeX(direction: Direction) = 21 - (9 * direction.index)
+
+ fun getLegsRelativeY() = 12
+
+ }
+
+}
\ No newline at end of file
diff --git a/desktop/build.gradle b/desktop/build.gradle
index b3a6693f285b98a686c56c8db34af36336b6be2c..70a63ba56bc3d5f3207941b4c50ac64eab09ee3e 100644 (file)
--- a/desktop/build.gradle
+++ b/desktop/build.gradle
-apply plugin: "java"
+plugins {
+ id 'kotlin'
+ id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlinVersion"
+}
-sourceCompatibility = 1.8
+sourceCompatibility = 17
sourceSets.main.java.srcDirs = [ "src/" ]
sourceSets.main.resources.srcDirs = ["../android/assets"]
standardInput = System.in
workingDir = project.assetsDir
ignoreExitValue = true as JavaExecSpec
+ args "--debug"
}
task runTouch(dependsOn: classes, type: JavaExec) {
standardInput = System.in
workingDir = project.assetsDir
ignoreExitValue = true as JavaExecSpec
- args "--touch"
+ args "--touch", "--debug"
}
task debug(dependsOn: classes, type: JavaExec) {
}
task dist(type: Jar) {
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
attributes 'Main-Class': project.mainClassName
}
diff --git a/desktop/gradle/wrapper/gradle-wrapper.properties b/desktop/gradle/wrapper/gradle-wrapper.properties
--- /dev/null
@@ -0,0 +1,6 @@
+#Sat Apr 13 17:22:59 NOVT 2024
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.java b/desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.java
index 2e9ae424749693629aa5adfa9acd0226b50b754f..fe78235dd8fe5bc0ec937561b82ae1a9b4f6e87e 100644 (file)
package ru.deadsoftware.cavedroid.desktop;
import com.badlogic.gdx.Files;
-import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
-import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
+import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
+import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import ru.deadsoftware.cavedroid.CaveGame;
class DesktopLauncher {
- public static void main (String[] arg) {
- LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
- config.addIcon("icons/icon256.png", Files.FileType.Internal);
- config.addIcon("icons/icon128.png", Files.FileType.Internal);
- config.foregroundFPS = 144;
- config.title = "CaveDroid";
- config.width = 960;
- config.height = 540;
- config.forceExit = false;
-
- boolean touch = false;
- for (String anArg : arg) {
- if (anArg.equals("--touch")) touch = true;
- }
- new LwjglApplication(new CaveGame(System.getProperty("user.home") + "/.cavedroid", touch), config);
- }
+ public static void main(String[] arg) {
+ Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
+ config.setWindowIcon(Files.FileType.Internal, "icons/icon256.png", "icons/icon128.png");
+ config.setTitle("CaveDroid");
+ config.setWindowedMode(960, 540);
+ config.useVsync(true);
+
+ boolean touch = false;
+ boolean debug = false;
+ String assetsPath = null;
+
+ for (String anArg : arg) {
+ if (anArg.equals("--touch")) {
+ touch = true;
+ }
+
+ if (anArg.equals("--debug")) {
+ debug = true;
+ }
+
+ if (anArg.startsWith("--assets")) {
+ String[] splitArg = anArg.split("=");
+ if (splitArg.length >= 2) {
+ assetsPath = splitArg[1];
+ }
+ }
+ }
+
+ CaveGame caveGame = new CaveGame(System.getProperty("user.home") + "/.cavedroid", touch, assetsPath);
+ caveGame.setDebug(debug);
+
+ new Lwjgl3Application(caveGame, config);
+ }
}
diff --git a/gen-changelog.sh b/gen-changelog.sh
--- /dev/null
+++ b/gen-changelog.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+# Waku-2 CC BY-SA 3.0
+# https://stackoverflow.com/a/46033999
+# https://creativecommons.org/licenses/by-sa/3.0/
+
+previous_tag=0
+i=0
+
+echo '<!--#include virtual="/includes/pre_header.shtml" -->'
+
+for current_tag in $(git tag --sort=-creatordate)
+do
+
+if [ "$previous_tag" != 0 ] && [ $i -lt 1 ]; then
+ i=$(echo "$i + 1" | bc -q )
+ tag_date=$(git log -1 --pretty=format:'%ad' --date=short ${previous_tag})
+ printf "## ${previous_tag} (${tag_date})\n\n"
+ git log ${current_tag}...${previous_tag} --pretty=format:'* %s ' --reverse | grep -v Merge
+ printf "\n\n"
+fi
+previous_tag=${current_tag}
+done
+
+echo '<!--#include virtual="/includes/pre_footer.shtml" -->'
diff --git a/gradle.properties b/gradle.properties
index 339fa1508b04d641faacd40a6958049724bbdf76..b7f66d430f6f4d25e3db7900a59a42c0962076ff 100644 (file)
--- a/gradle.properties
+++ b/gradle.properties
org.gradle.daemon=true
org.gradle.jvmargs=-Xms128m -Xmx1500m
org.gradle.configureondemand=true
+android.useAndroidX=true
index 374585553662a55219dc2f375458e4cb4db35db0..b3497272b316d215d0731d4299d41673c6d1621f 100644 (file)
#Thu Sep 26 22:30:23 NOVT 2019
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
index 4453ccea33d960069d9137ee65f6b21fc65e7e92..54f115a3e6a1a49ab69cc9f25cf875b24ea352da 100755 (executable)
--- a/gradlew
+++ b/gradlew
#!/usr/bin/env sh
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
##############################################################################
##
## Gradle start up script for UN*X
diff --git a/gradlew.bat b/gradlew.bat
index f9553162f122c71b34635112e717c3e733b5b212..c42c57956b0c0329894230ee9aee43a6153439af 100644 (file)
--- a/gradlew.bat
+++ b/gradlew.bat
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
diff --git a/make-release.sh b/make-release.sh
--- /dev/null
+++ b/make-release.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+if [[ ! $1 ]]; then
+ echo "usage: $0 versionName"
+ exit
+fi
+
+./require_clean_work_tree "$0"
+
+release_dir="release-$1"
+
+mkdir "$release_dir"
+
+./up-version.sh "$1"
+./gen-changelog.sh > "$release_dir/CHANGELOG"
+
+./gradlew clean android:assembleRelease desktop:dist
+
+cp android/build/outputs/apk/release/*.apk "$release_dir/"
+cp desktop/build/libs/*.jar "$release_dir/"
+
+echo "$release_dir/"
diff --git a/require-celan-work-tree.sh b/require-celan-work-tree.sh
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+# VonC CC BY-SA 4.0
+# https://stackoverflow.com/a/3879077
+# https://creativecommons.org/licenses/by-sa/4.0/
+
+ # Update the index
+ git update-index -q --ignore-submodules --refresh
+ err=0
+
+ # Disallow unstaged changes in the working tree
+ if ! git diff-files --quiet --ignore-submodules --
+ then
+ echo >&2 "cannot $1: you have unstaged changes."
+ git diff-files --name-status -r --ignore-submodules -- >&2
+ err=1
+ fi
+
+ # Disallow uncommitted changes in the index
+ if ! git diff-index --cached --quiet HEAD --ignore-submodules --
+ then
+ echo >&2 "cannot $1: your index contains uncommitted changes."
+ git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
+ err=1
+ fi
+
+ if [ $err = 1 ]
+ then
+ echo >&2 "Please commit or stash them."
+ exit 1
+ fi
diff --git a/up-version.sh b/up-version.sh
--- /dev/null
+++ b/up-version.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+new_version=$1
+
+new_version_string=$(echo $new_version | sed 's/\(alpha\|beta\)\(.*\)/\1 \2/')
+
+sed -i 's/\(version\s=\s\)'"'"'.*'"'"'/\1'"'"''"$new_version"''"'"'/g' build.gradle
+sed -i 's/\(versionName\s\)\".*\"/\1\"'"$new_version"'\"/g' android/build.gradle
+sed -i 's/\(^\s*versionCode\s\)\([0-9]*\)/echo "\1$((\2+1))"/ge' android/build.gradle
+sed -i 's/\(public static final String VERSION = \)\".*\"/\1\"'"$new_version_string"'\"/' core/src/ru/deadsoftware/cavedroid/CaveGame.java
+
+git add build.gradle android/build.gradle core/src/ru/deadsoftware/cavedroid/CaveGame.java
+
+git commit -m "Update version"
+git tag "$new_version"