From: fredboy Date: Wed, 2 Jul 2025 05:29:46 +0000 (+0700) Subject: Change scale values X-Git-Url: https://deadsoftware.ru/gitweb?p=cavedroid.git;a=commitdiff_plain;h=refs%2Fheads%2Fmaster;hp=16b10981ea5bc34da1a8d63c70acf03faef8fc92 Change scale values --- diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a05e399 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{kt,kts}] +ktlint_code_style=intellij_idea +ktlint_standard_multiline-expression-wrapping = disabled +ktlint_standard_string-template-indent = disabled +ktlint_standard_comment-wrapping = disabled +ktlint_standard_no-empty-first-line-in-class-body = disabled +ktlint_standard_property-naming = disabled +ktlint_standard_backing-property-naming = disabled +ktlint_standard_function-expression-body = disabled + +[**/generated/**] +generated_code = true +ij_formatter_enabled = false +ktlint = disabled diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..6621b01 --- /dev/null +++ b/.github/workflows/android.yml @@ -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/.github/workflows/ktlint.yml b/.github/workflows/ktlint.yml new file mode 100644 index 0000000..a33d14c --- /dev/null +++ b/.github/workflows/ktlint.yml @@ -0,0 +1,32 @@ +name: Ktlint + +on: + push: + branches: [ "master" ] + paths: + - .github/workflows/ktlint.yml + - 'src/**/*.kt' + - '**.kts' + pull_request: + branches: [ "master", "develop" ] + paths: + - .github/workflows/ktlint.yml + - 'src/**/*.kt' + - '**.kts' + +jobs: + lint: + 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: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Check Ktlint with Gradle + run: ./gradlew ktlintCheck buildSrc:ktlintCheck diff --git a/.gitignore b/.gitignore index ac624d2..c6de61e 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,9 @@ Thumbs.db !/ios-moe/xcode/*.xcodeproj/xcshareddata !/ios-moe/xcode/*.xcodeproj/project.pbxproj /ios-moe/xcode/native/ + +release-*/ +keystore.properties + +**/build/ +**/_build/ diff --git a/COPYING b/COPYING index 37400b6..90ecac3 100644 --- a/COPYING +++ b/COPYING @@ -1,5 +1,13 @@ This game is distributed under MIT License (see LICENSE). -Textures used in this game: +Textures used in this game (in android/assets/pp directory): Pixel Perfection by XSSheep is licensed under a Creative Commons Attribution-Share Alike 4.0 International License. https://creativecommons.org/licenses/by-sa/4.0/ + +On screen joystick is CC-0 from opengameart.org: +https://opengameart.org/content/mmorpg-virtual-joysticks + +Font is Minecraft Font by JDGraphics licensed as Public Domain: +https://www.fontspace.com/minecraft-font-f28180 + +Some scripts from stack overflow are distributed under applicable licenses diff --git a/README.md b/README.md index b0f7525..b5f7866 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,21 @@ # 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)
+[![Android CI](https://github.com/fredboy/cavedroid/actions/workflows/android.yml/badge.svg)](https://github.com/fredboy/cavedroid/actions/workflows/android.yml) +[![Ktlint](https://github.com/fredboy/cavedroid/actions/workflows/ktlint.yml/badge.svg)](https://github.com/fredboy/cavedroid/actions/workflows/ktlint.yml) +[![GitHub Tag](https://img.shields.io/github/v/tag/fredboy/cavedroid)](https://github.com/fredboy/cavedroid/tags)
2D Minecraft clone for Android and Desktop.
Written in Java using libGDX framework.
+
+ Screenshot + +![Screenshot](https://fredboy.ru/pub/cavedroid/screenshot.png) + +
+ ## Binary releases You can download apk and jar from here:
## Build instructions +You need to publish [my ksp processor](https://github.com/fredboy/automultibind) to mavenLocal repository first. ### Android To build for Android use
`./gradlew android:assemble`
diff --git a/android/assets/icons/icon128.png b/android/assets/icons/icon128.png deleted file mode 100644 index 19e4532..0000000 Binary files a/android/assets/icons/icon128.png and /dev/null differ diff --git a/android/assets/icons/icon256.png b/android/assets/icons/icon256.png deleted file mode 100644 index 3b8b92b..0000000 Binary files a/android/assets/icons/icon256.png and /dev/null differ diff --git a/android/assets/json/game_items.json b/android/assets/json/game_items.json deleted file mode 100644 index 042f0c9..0000000 --- a/android/assets/json/game_items.json +++ /dev/null @@ -1,654 +0,0 @@ -{ - "blocks": { - "none": { - "collision": false, - "transparent": true - }, - "stone": { - "hp": 450, - "drop": "cobblestone" - }, - "grass": { - "hp": 54, - "drop": "dirt" - }, - "dirt": { - "hp": 45 - }, - "cobblestone": { - "hp": 600 - }, - "planks_oak": { - "hp": 180 - }, - "sapling_oak": { - "hp": 0, - "collision": false, - "background": false, - "transparent": true, - "block_required": true - }, - "bedrock": { - "hp": -1 - }, - "water": { - "hp": -1, - "collision": false, - "background": false, - "transparent": true, - "fluid": true, - "texture": "water_still", - "meta": "water", - "animated": true, - "frames": 16 - }, - "lava": { - "hp": -1, - "collision": false, - "background": false, - "fluid": true, - "texture": "lava_still", - "meta": "lava", - "animated": true, - "frames": 16 - }, - "sand": { - "hp": 45 - }, - "gravel": { - "hp": 54 - }, - "gold_ore": { - "hp": 900 - }, - "iron_ore": { - "hp": 900 - }, - "coal_ore": { - "hp": 900, - "drop": "coal" - }, - "log_oak": { - "hp": 180 - }, - "leaves_oak": { - "hp": 21, - "transparent": true - }, - "sponge": { - "hp": 54 - }, - "glass": { - "hp": 27, - "drop": "none", - "background": false, - "transparent": true - }, - "lapis_ore": { - "hp": 900, - "drop": "lapis" - }, - "lapis_block": { - "hp": 900 - }, - "sandstone": { - "hp": 240 - }, - "noteblock": { - "hp": 75 - }, - "bed_l": { - "hp": 21, - "drop": "none", - "collision": false, - "background": true, - "transparent": true - }, - "bed_r": { - "hp": 21, - "drop": "none", - "collision": false, - "background": true, - "transparent": true - }, - "web": { - "hp": 1200, - "collision": false, - "background": false, - "transparent": true - }, - "tallgrass": { - "hp": 0, - "collision": false, - "background": false, - "transparent": true, - "block_required": true - }, - "deadbush": { - "hp": 0, - "collision": false, - "background": false, - "transparent": true, - "block_required": true - }, - "bricks": { - "hp": 600 - }, - "dandelion": { - "hp": 0, - "collision": false, - "background": false, - "transparent": true, - "block_required": true - }, - "rose": { - "hp": 0, - "collision": false, - "background": false, - "transparent": true, - "block_required": true - }, - "mushroom_brown": { - "hp": 0, - "collision": false, - "background": false, - "transparent": true, - "block_required": true - }, - "mushroom_red": { - "hp": 0, - "collision": false, - "background": false, - "transparent": true, - "block_required": true - }, - "wool_colored_white": { - "hp": 75 - }, - "wool_colored_orange": { - "hp": 75 - }, - "wool_colored_magenta": { - "hp": 75 - }, - "wool_colored_light_blue": { - "hp": 75 - }, - "wool_colored_yellow": { - "hp": 75 - }, - "wool_colored_lime": { - "hp": 75 - }, - "wool_colored_pink": { - "hp": 75 - }, - "wool_colored_gray": { - "hp": 75 - }, - "wool_colored_silver": { - "hp": 75 - }, - "wool_colored_cyan": { - "hp": 75 - }, - "wool_colored_purple": { - "hp": 75 - }, - "wool_colored_blue": { - "hp": 75 - }, - "wool_colored_brown": { - "hp": 75 - }, - "wool_colored_green": { - "hp": 75 - }, - "wool_colored_red": { - "hp": 75 - }, - "wool_colored_black": { - "hp": 75 - }, - "gold_block": { - "hp": 900 - }, - "iron_block": { - "hp": 1500 - }, - "stone_slab": { - "top": 8, - "hp": 600, - "transparent": true, - "sprite_top": 8 - }, - "double_stone_slab": { - "hp": 600, - "drop": "stone_slab", - "texture": "stone_slab" - }, - "sandstone_slab": { - "top": 8, - "hp": 600, - "transparent": true, - "texture": "sandstone" - }, - "oak_slab": { - "top": 8, - "hp": 180, - "transparent": true, - "texture": "planks_oak" - }, - "cobblestone_slab": { - "top": 8, - "hp": 600, - "transparent": true, - "texture": "cobblestone" - }, - "brick_slab": { - "top": 8, - "hp": 600, - "transparent": true, - "texture": "bricks" - }, - "stonebrick": { - "hp": 450 - }, - "stonebrick_slab": { - "top": 8, - "hp": 450, - "transparent": true, - "texture": "stonebrick" - }, - "cactus": { - "left": 1, - "right": 1, - "hp": 39, - "transparent": true, - "block_required": true - }, - "water_16": { - "hp": -1, - "collision": false, - "background": false, - "transparent": true, - "fluid": true, - "texture": "water_flow", - "meta": "water", - "animated": true, - "frames": 16 - }, - "water_12": { - "top": 4, - "hp": -1, - "collision": false, - "background": false, - "transparent": true, - "fluid": true, - "texture": "water_flow", - "meta": "water", - "animated": true, - "frames": 16, - "sprite_top": 4 - }, - "water_8": { - "top": 8, - "hp": -1, - "collision": false, - "background": false, - "transparent": true, - "fluid": true, - "texture": "water_flow", - "meta": "water", - "animated": true, - "frames": 16, - "sprite_top": 8 - }, - "water_4": { - "top": 12, - "hp": -1, - "collision": false, - "background": false, - "transparent": true, - "fluid": true, - "texture": "water_flow", - "meta": "water", - "animated": true, - "frames": 16, - "sprite_top": 12 - }, - "lava_16": { - "hp": -1, - "collision": false, - "background": false, - "fluid": true, - "texture": "lava_flow", - "meta": "lava", - "animated": true, - "frames": 16 - }, - "lava_12": { - "top": 4, - "hp": -1, - "collision": false, - "background": false, - "fluid": true, - "texture": "lava_flow", - "meta": "lava", - "animated": true, - "frames": 16, - "sprite_top": 4 - }, - "lava_8": { - "top": 8, - "hp": -1, - "collision": false, - "background": false, - "fluid": true, - "texture": "lava_flow", - "meta": "lava", - "animated": true, - "frames": 16, - "sprite_top": 8 - }, - "lava_4": { - "top": 12, - "hp": -1, - "collision": false, - "background": false, - "fluid": true, - "texture": "lava_flow", - "meta": "lava", - "animated": true, - "frames": 16, - "sprite_top": 12 - }, - "obsidian": { - "hp": 1500 - } - }, - "items": { - "none": { - "name": "", - "type": "block" - }, - "stone": { - "name": "Stone", - "type": "block" - }, - "grass": { - "name": "Grass", - "type": "block" - }, - "dirt": { - "name": "Dirt", - "type": "block" - }, - "cobblestone": { - "name": "Cobblestone", - "type": "block" - }, - "planks_oak": { - "name": "Oak Planks", - "type": "block" - }, - "sapling_oak": { - "name": "Oak Sapling", - "type": "block" - }, - "bedrock": { - "name": "Bedrock", - "type": "block" - }, - "water": { - "name": "Water", - "type": "block" - }, - "lava": { - "name": "Lava", - "type": "block" - }, - "sand": { - "name": "Sand", - "type": "block" - }, - "gravel": { - "name": "Gravel", - "type": "block" - }, - "gold_ore": { - "name": "Golden Ore", - "type": "block" - }, - "iron_ore": { - "name": "Iron Ore", - "type": "block" - }, - "coal_ore": { - "name": "Coal Ore", - "type": "block" - }, - "log_oak": { - "name": "Wood", - "type": "block" - }, - "leaves_oak": { - "name": "Leaves", - "type": "block" - }, - "glass": { - "name": "Glass", - "type": "block" - }, - "lapis_ore": { - "name": "Lapis Ore", - "type": "block" - }, - "lapis_block": { - "name": "Lapis Block", - "type": "block" - }, - "sandstone": { - "name": "Sandstone", - "type": "block" - }, - "web": { - "name": "Cobweb", - "type": "block" - }, - "tallgrass": { - "name": "Tall Grass", - "type": "block" - }, - "deadbush": { - "name": "Dead Bush", - "type": "block" - }, - "bricks": { - "name": "Bricks", - "type": "block" - }, - "dandelion": { - "name": "Dandelion", - "type": "block" - }, - "rose": { - "name": "Rose", - "type": "block" - }, - "mushroom_brown": { - "name": "Mushroom", - "type": "block" - }, - "mushroom_red": { - "name": "Mushroom", - "type": "block" - }, - "wool_colored_white": { - "name": "Wool", - "type": "block" - }, - "wool_colored_orange": { - "name": "Wool", - "type": "block" - }, - "wool_colored_magenta": { - "name": "Wool", - "type": "block" - }, - "wool_colored_light_blue": { - "name": "Wool", - "type": "block" - }, - "wool_colored_yellow": { - "name": "Wool", - "type": "block" - }, - "wool_colored_lime": { - "name": "Wool", - "type": "block" - }, - "wool_colored_pink": { - "name": "Wool", - "type": "block" - }, - "wool_colored_gray": { - "name": "Wool", - "type": "block" - }, - "wool_colored_silver": { - "name": "Wool", - "type": "block" - }, - "wool_colored_cyan": { - "name": "Wool", - "type": "block" - }, - "wool_colored_purple": { - "name": "Wool", - "type": "block" - }, - "wool_colored_blue": { - "name": "Wool", - "type": "block" - }, - "wool_colored_brown": { - "name": "Wool", - "type": "block" - }, - "wool_colored_green": { - "name": "Wool", - "type": "block" - }, - "wool_colored_red": { - "name": "Wool", - "type": "block" - }, - "wool_colored_black": { - "name": "Wool", - "type": "block" - }, - "gold_block": { - "name": "Gold Block", - "type": "block" - }, - "iron_block": { - "name": "Iron Block", - "type": "block" - }, - "stone_slab": { - "name": "Stone Slab", - "type": "block", - "meta": "slab" - }, - "sandstone_slab": { - "name": "Sandstone Slab", - "type": "block", - "meta": "slab" - }, - "oak_slab": { - "name": "Oak Slab", - "type": "block", - "meta": "slab" - }, - "cobblestone_slab": { - "name": "Cobblestone Slab", - "type": "block", - "meta": "slab" - }, - "brick_slab": { - "name": "Brick Slab", - "type": "block", - "meta": "slab" - }, - "stonebrick": { - "name": "Stone Brick", - "type": "block" - }, - "stonebrick_slab": { - "name": "Stone Brick Slab", - "type": "block", - "meta": "slab" - }, - "cactus": { - "name": "Cactus", - "type": "block" - }, - "obsidian": { - "name": "Obsidian", - "type": "block" - }, - "wood_sword": { - "name": "Wooden Sword", - "type": "tool" - }, - "stone_sword": { - "name": "Stone Sword", - "type": "tool" - }, - "iron_sword": { - "name": "Iron Sword", - "type": "tool" - }, - "diamond_sword": { - "name": "Diamond Sword", - "type": "tool" - }, - "gold_sword": { - "name": "Golden Sword", - "type": "tool" - }, - "wood_shovel": { - "name": "Wooden Shovel", - "type": "tool" - }, - "stone_shovel": { - "name": "Stone Shovel", - "type": "tool" - }, - "iron_shovel": { - "name": "Iron Shovel", - "type": "tool" - }, - "diamond_shovel": { - "name": "Diamond Shovel", - "type": "tool" - }, - "gold_shovel": { - "name": "Golden Shovel", - "type": "tool" - }, - "bucket_empty": { - "name": "Empty Bucket", - "type": "tool" - }, - "bucket_water": { - "name": "Water Bucket", - "type": "tool" - }, - "bucket_lava": { - "name": "Lava Bucket", - "type": "tool" - } - } -} \ No newline at end of file diff --git a/android/assets/json/menu_main_buttons.json b/android/assets/json/menu_main_buttons.json deleted file mode 100644 index c52f47c..0000000 --- a/android/assets/json/menu_main_buttons.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "new_game": { - "label": "New Game" - }, - "load_game": { - "label": "Load Game", - "type": 0 - }, - "quit": { - "label": "Quit" - } -} \ 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 deleted file mode 100644 index c61adc3..0000000 --- a/android/assets/json/menu_new_game_buttons.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "creative": { - "label": "Creative" - }, - "survival": { - "label": "Survival", - "type": 0 - }, - "back": { - "label": "Back" - } -} \ No newline at end of file diff --git a/android/assets/json/touch_buttons.json b/android/assets/json/touch_buttons.json deleted file mode 100644 index cddaf53..0000000 --- a/android/assets/json/touch_buttons.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "up": { - "x": 26, - "y": -52, - "w": 26, - "h": 26, - "key": "W" - }, - "down": { - "x": 26, - "y": -26, - "w": 26, - "h": 26, - "key": "S" - }, - "left": { - "x": 0, - "y": -26, - "w": 26, - "h": 26, - "key": "A" - }, - "right": { - "x": 52, - "y": -26, - "w": 26, - "h": 26, - "key": "D" - }, - "alt": { - "x": 78, - "y": -26, - "w": 26, - "h": 26, - "key": "L-Alt" - }, - "lmb": { - "x": -52, - "y": -26, - "w": 26, - "h": 26, - "mouse": true, - "key": "Left" - }, - "rmb": { - "x": -26, - "y": -26, - "w": 26, - "h": 26, - "mouse": true, - "key": "Right" - } -} \ No newline at end of file diff --git a/android/assets/mobs/char/1_0.png b/android/assets/mobs/char/1_0.png deleted file mode 100644 index 2c1a2b2..0000000 Binary files a/android/assets/mobs/char/1_0.png and /dev/null differ diff --git a/android/assets/mobs/char/1_1.png b/android/assets/mobs/char/1_1.png deleted file mode 100644 index eff8cc6..0000000 Binary files a/android/assets/mobs/char/1_1.png and /dev/null differ diff --git a/android/assets/mobs/char/1_2.png b/android/assets/mobs/char/1_2.png deleted file mode 100644 index c173562..0000000 Binary files a/android/assets/mobs/char/1_2.png and /dev/null differ diff --git a/android/assets/mobs/char/1_3.png b/android/assets/mobs/char/1_3.png deleted file mode 100644 index 3ee37e4..0000000 Binary files a/android/assets/mobs/char/1_3.png and /dev/null differ diff --git a/android/assets/mobs/pig/1_0.png b/android/assets/mobs/pig/1_0.png deleted file mode 100644 index ed31acc..0000000 Binary files a/android/assets/mobs/pig/1_0.png and /dev/null differ diff --git a/android/assets/mobs/pig/1_1.png b/android/assets/mobs/pig/1_1.png deleted file mode 100644 index 5a7acc5..0000000 Binary files a/android/assets/mobs/pig/1_1.png and /dev/null differ diff --git a/android/assets/textures/blocks/furnace_off.png b/android/assets/textures/blocks/furnace_off.png deleted file mode 100644 index 6d9b405..0000000 Binary files a/android/assets/textures/blocks/furnace_off.png and /dev/null differ diff --git a/android/assets/textures/blocks/furnace_on.png b/android/assets/textures/blocks/furnace_on.png deleted file mode 100644 index ecbc607..0000000 Binary files a/android/assets/textures/blocks/furnace_on.png and /dev/null differ diff --git a/android/assets/textures/blocks/lapis_ore.png b/android/assets/textures/blocks/lapis_ore.png deleted file mode 100644 index 04a4630..0000000 Binary files a/android/assets/textures/blocks/lapis_ore.png and /dev/null differ diff --git a/android/assets/textures/blocks/leaves_birch.png b/android/assets/textures/blocks/leaves_birch.png deleted file mode 100644 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 deleted file mode 100644 index c2a54ee..0000000 Binary files a/android/assets/textures/blocks/leaves_oak.png and /dev/null differ diff --git a/android/assets/textures/blocks/leaves_spruce.png b/android/assets/textures/blocks/leaves_spruce.png deleted file mode 100644 index cdcd782..0000000 Binary files a/android/assets/textures/blocks/leaves_spruce.png and /dev/null differ diff --git a/android/assets/touch_gui.png b/android/assets/touch_gui.png deleted file mode 100644 index b194413..0000000 Binary files a/android/assets/touch_gui.png and /dev/null differ diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index a5ed087..0000000 --- a/android/build.gradle +++ /dev/null @@ -1,104 +0,0 @@ -android { - buildToolsVersion "28.0.3" - compileSdkVersion 29 - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src'] - aidl.srcDirs = ['src'] - renderscript.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - jniLibs.srcDirs = ['libs'] - } - debug { - res.srcDirs = ['debug/res'] - } - } - compileOptions { - sourceCompatibility 1.8 - targetCompatibility 1.8 - } - packagingOptions { - exclude 'META-INF/robovm/ios/robovm.xml' - } - defaultConfig { - applicationId "ru.deadsoftware.cavedroid" - minSdkVersion 24 - targetSdkVersion 29 - versionCode 10 - versionName "alpha0.4" - } - applicationVariants.all { variant -> - variant.outputs.all { - outputFileName = "android-${versionName}.apk" - } - } - buildTypes { - release { - minifyEnabled false - } - debug { - applicationIdSuffix ".debug" - } - } - -} - - -// called every time gradle gets executed, takes the native dependencies of -// the natives configuration, and extracts them to the proper libs/ folders -// so they get packed with the APK. -task copyAndroidNatives { - doFirst { - file("libs/armeabi/").mkdirs() - file("libs/armeabi-v7a/").mkdirs() - file("libs/arm64-v8a/").mkdirs() - file("libs/x86_64/").mkdirs() - file("libs/x86/").mkdirs() - - configurations.natives.files.each { jar -> - def outputDir = null - if (jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a") - if (jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a") - if(jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi") - if(jar.name.endsWith("natives-x86_64.jar")) outputDir = file("libs/x86_64") - if(jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86") - if(outputDir != null) { - copy { - from zipTree(jar) - into outputDir - include "*.so" - } - } - } - } -} - -tasks.whenTaskAdded { packageTask -> - if (packageTask.name.contains("package")) { - packageTask.dependsOn 'copyAndroidNatives' - } -} - -task run(type: Exec) { - def path - def localProperties = project.file("../local.properties") - if (localProperties.exists()) { - Properties properties = new Properties() - localProperties.withInputStream { instr -> - properties.load(instr) - } - def sdkDir = properties.getProperty('sdk.dir') - if (sdkDir) { - path = sdkDir - } else { - path = "$System.env.ANDROID_HOME" - } - } else { - path = "$System.env.ANDROID_HOME" - } - - def adb = path + "/platform-tools/adb" - commandLine "$adb", 'shell', 'am', 'start', '-n', 'ru.deadsoftware.cavedroid/ru.deadsoftware.cavedroid.AndroidLauncher' -} \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..827e0c2 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,131 @@ +import java.io.FileInputStream +import java.util.Properties + +private val natives by configurations.creating + +plugins { + id("com.android.application") + id("kotlin-android") +} + +private val keystorePropertiesFile = rootProject.file("keystore.properties") +private val keystoreProperties = if (keystorePropertiesFile.exists()) { + Properties().apply { + load(FileInputStream(keystorePropertiesFile)) + } +} else { + null +} + +android { + namespace = ApplicationInfo.packageName + compileSdk = 34 + + sourceSets { + + named("main") { + jniLibs.srcDir("libs") + } + + named("debug") { + res.srcDir("src/debug/res") + } + } + + compileOptions { + sourceCompatibility = ApplicationInfo.sourceCompatibility + targetCompatibility = ApplicationInfo.sourceCompatibility + } + + defaultConfig { + applicationId = ApplicationInfo.packageName + minSdk = 19 + targetSdk = 34 + + versionCode = ApplicationInfo.versionCode + versionName = ApplicationInfo.versionName + + multiDexEnabled = true + } + + applicationVariants.asSequence() + .flatMap { variant -> variant.outputs.asSequence() } + .mapNotNull { output -> output as? com.android.build.gradle.internal.api.BaseVariantOutputImpl } + .forEach { output -> output.outputFileName = "android-${ApplicationInfo.versionName}.apk" } + + val releaseConfig = signingConfigs.create("release_config") + with(releaseConfig) { + storeFile = keystoreProperties?.get("releaseKeystorePath")?.let(::file) + storePassword = keystoreProperties?.get("releaseKeystorePassword")?.toString() + keyAlias = keystoreProperties?.get("releaseKeyAlias")?.toString() + keyPassword = keystoreProperties?.get("releaseKeyPassword")?.toString() + } + + buildTypes { + release { + isMinifyEnabled = false + signingConfig = releaseConfig + } + + debug { + applicationIdSuffix = ".debug" + } + } + + buildFeatures { + buildConfig = true + } +} + +// called every time gradle gets executed, takes the native dependencies of +// the natives configuration, and extracts them to the proper libs/ folders +// so they get packed with the APK. +task("copyAndroidNatives") { + doFirst { + val armeabiV7Dir = file("libs/armeabi-v7a/").apply { mkdirs() } + val arm64Dir = file("libs/arm64-v8a/").apply { mkdirs() } + val x86Dir = file("libs/x86/").apply { mkdirs() } + val amd64Dir = file("libs/x86_64/").apply { mkdirs() } + + natives.files.forEach { jar -> + val outputDir = when { + jar.name.endsWith("natives-armeabi-v7a.jar") -> armeabiV7Dir + jar.name.endsWith("natives-arm64-v8a.jar") -> arm64Dir + jar.name.endsWith("natives-x86.jar") -> x86Dir + jar.name.endsWith("natives-x86_64.jar") -> amd64Dir + else -> null + } + + if (outputDir != null) { + copy { + from(zipTree(jar)) + into(outputDir) + include("*.so") + } + } + } + } +} + +tasks.whenTaskAdded { + if (name.contains("package")) { + dependsOn("copyAndroidNatives") + } +} + +dependencies { + useCommonModule() + useZygoteModule() + + implementation(platform(Dependencies.Kotlin.bom)) + + implementation(Dependencies.LibGDX.gdx) + implementation(Dependencies.LibGDX.Android.backend) + + natives(Dependencies.LibGDX.Android.Natives.armeabi) + natives(Dependencies.LibGDX.Android.Natives.arm64) + natives(Dependencies.LibGDX.Android.Natives.x86) + natives(Dependencies.LibGDX.Android.Natives.x86_64) + + configurations["implementation"].exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8") +} diff --git a/android/debug/res/drawable-anydpi-v26/ic_launcher_foreground.xml b/android/debug/res/drawable-anydpi-v26/ic_launcher_foreground.xml deleted file mode 100644 index 4535da3..0000000 --- a/android/debug/res/drawable-anydpi-v26/ic_launcher_foreground.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/debug/res/drawable-hdpi/ic_launcher.png b/android/debug/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 9c5bde7..0000000 Binary files a/android/debug/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/debug/res/drawable-mdpi/ic_launcher.png b/android/debug/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 7e8a490..0000000 Binary files a/android/debug/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/debug/res/drawable-xhdpi/ic_launcher.png b/android/debug/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index 88f3a21..0000000 Binary files a/android/debug/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/debug/res/drawable-xxhdpi/ic_launcher.png b/android/debug/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index ca5ae82..0000000 Binary files a/android/debug/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/debug/res/drawable-xxxhdpi/ic_launcher.png b/android/debug/res/drawable-xxxhdpi/ic_launcher.png deleted file mode 100644 index 89ce105..0000000 Binary files a/android/debug/res/drawable-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/ic_launcher-web.png b/android/ic_launcher-web.png index f6a837e..9d5fc15 100644 Binary files a/android/ic_launcher-web.png and b/android/ic_launcher-web.png differ diff --git a/android/project.properties b/android/project.properties deleted file mode 100644 index 3fefa92..0000000 --- a/android/project.properties +++ /dev/null @@ -1,9 +0,0 @@ -# This file is used by the Eclipse ADT plugin. It is unnecessary for IDEA and Android Studio projects, which -# configure Proguard and the Android target via the build.gradle file. - -# To enable ProGuard to work with Eclipse ADT, uncomment this (available properties: sdk.dir, user.home) -# and ensure proguard.jar in the Android SDK is up to date (or alternately reduce the android target to 23 or lower): -# proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-rules.pro - -# Project target. -target=android-19 diff --git a/android/res/drawable-anydpi-v26/ic_launcher_background.xml b/android/res/drawable-anydpi-v26/ic_launcher_background.xml deleted file mode 100644 index b398624..0000000 --- a/android/res/drawable-anydpi-v26/ic_launcher_background.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/res/drawable-anydpi-v26/ic_launcher_foreground.xml b/android/res/drawable-anydpi-v26/ic_launcher_foreground.xml deleted file mode 100644 index ce92ea1..0000000 --- a/android/res/drawable-anydpi-v26/ic_launcher_foreground.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/res/drawable-hdpi/ic_launcher.png b/android/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index ace457e..0000000 Binary files a/android/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-mdpi/ic_launcher.png b/android/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index ed09826..0000000 Binary files a/android/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-xhdpi/ic_launcher.png b/android/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index 190d16d..0000000 Binary files a/android/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-xxhdpi/ic_launcher.png b/android/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index b58006b..0000000 Binary files a/android/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-xxxhdpi/ic_launcher.png b/android/res/drawable-xxxhdpi/ic_launcher.png deleted file mode 100644 index a4b6775..0000000 Binary files a/android/res/drawable-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/src/debug/res/drawable-hdpi/ic_launcher.png b/android/src/debug/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..595b5c6 Binary files /dev/null and b/android/src/debug/res/drawable-hdpi/ic_launcher.png differ diff --git a/android/src/debug/res/drawable-mdpi/ic_launcher.png b/android/src/debug/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..76e15c3 Binary files /dev/null and b/android/src/debug/res/drawable-mdpi/ic_launcher.png differ diff --git a/android/src/debug/res/drawable-xhdpi/ic_launcher.png b/android/src/debug/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..1c55208 Binary files /dev/null and b/android/src/debug/res/drawable-xhdpi/ic_launcher.png differ diff --git a/android/src/debug/res/drawable-xxhdpi/ic_launcher.png b/android/src/debug/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..94f1374 Binary files /dev/null and b/android/src/debug/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/android/src/debug/res/drawable-xxxhdpi/ic_launcher.png b/android/src/debug/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..88ddb67 Binary files /dev/null and b/android/src/debug/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/android/src/debug/res/drawable/ic_launcher_foreground.xml b/android/src/debug/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..c963cce --- /dev/null +++ b/android/src/debug/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/debug/res/values/strings.xml b/android/src/debug/res/values/strings.xml similarity index 100% rename from android/debug/res/values/strings.xml rename to android/src/debug/res/values/strings.xml diff --git a/android/AndroidManifest.xml b/android/src/main/AndroidManifest.xml similarity index 79% rename from android/AndroidManifest.xml rename to android/src/main/AndroidManifest.xml index 47eaf7a..4428532 100644 --- a/android/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,16 +1,15 @@ + xmlns:tools="http://schemas.android.com/tools"> + android:theme="@style/GdxTheme" tools:ignore="GoogleAppIndexingWarning"> diff --git a/android/src/main/assets b/android/src/main/assets new file mode 120000 index 0000000..2978ef3 --- /dev/null +++ b/android/src/main/assets @@ -0,0 +1 @@ +../../../assets \ No newline at end of file diff --git a/android/src/main/kotlin/ru/deadsoftware/cavedroid/AndroidLauncher.kt b/android/src/main/kotlin/ru/deadsoftware/cavedroid/AndroidLauncher.kt new file mode 100644 index 0000000..7bf5d85 --- /dev/null +++ b/android/src/main/kotlin/ru/deadsoftware/cavedroid/AndroidLauncher.kt @@ -0,0 +1,34 @@ +package ru.deadsoftware.cavedroid + +import android.os.Bundle +import com.badlogic.gdx.backends.android.AndroidApplication +import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration +import ru.fredboy.cavedroid.zygote.CaveDroidApplication + +class AndroidLauncher : AndroidApplication() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val gameDataDirectoryPath = packageManager.getPackageInfo(packageName, 0) + .applicationInfo.dataDir + + val config = AndroidApplicationConfiguration() + config.useImmersiveMode = true + + initialize( + /* listener = */ CaveDroidApplication( + gameDataDirectoryPath = gameDataDirectoryPath, + isTouchScreen = true, + isDebug = BuildConfig.DEBUG, + preferencesStore = AndroidPreferencesStore(applicationContext), + ), + /* config = */ config, + ) + } + + @Suppress("OVERRIDE_DEPRECATION") + override fun onBackPressed() { + // ignore + } +} diff --git a/android/src/main/kotlin/ru/deadsoftware/cavedroid/AndroidPreferencesStore.kt b/android/src/main/kotlin/ru/deadsoftware/cavedroid/AndroidPreferencesStore.kt new file mode 100644 index 0000000..11c661d --- /dev/null +++ b/android/src/main/kotlin/ru/deadsoftware/cavedroid/AndroidPreferencesStore.kt @@ -0,0 +1,24 @@ +package ru.deadsoftware.cavedroid + +import android.content.Context +import ru.fredboy.cavedroid.common.api.PreferencesStore + +class AndroidPreferencesStore( + private val context: Context, +) : PreferencesStore { + + private val sharedPreferences by lazy { context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) } + + override fun getPreference(key: String): String? = sharedPreferences.getString(key, null) + + override fun setPreference(key: String, value: String?) { + with(sharedPreferences.edit()) { + putString(key, value) + apply() + } + } + + private companion object { + private const val SHARED_PREFS_NAME = "cavedroid_prefs" + } +} diff --git a/android/res/drawable-anydpi-v26/ic_launcher.xml b/android/src/main/res/drawable-anydpi-v26/ic_launcher.xml similarity index 100% rename from android/res/drawable-anydpi-v26/ic_launcher.xml rename to android/src/main/res/drawable-anydpi-v26/ic_launcher.xml diff --git a/android/src/main/res/drawable-hdpi/ic_launcher.png b/android/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..2b9989e Binary files /dev/null and b/android/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/android/src/main/res/drawable-mdpi/ic_launcher.png b/android/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..4657268 Binary files /dev/null and b/android/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/android/src/main/res/drawable-xhdpi/ic_launcher.png b/android/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..739b6ce Binary files /dev/null and b/android/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/android/src/main/res/drawable-xxhdpi/ic_launcher.png b/android/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..1c1aa90 Binary files /dev/null and b/android/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/android/src/main/res/drawable-xxxhdpi/ic_launcher.png b/android/src/main/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..0f64585 Binary files /dev/null and b/android/src/main/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/android/src/main/res/drawable/ic_launcher_background.xml b/android/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..bae566f --- /dev/null +++ b/android/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + diff --git a/android/src/main/res/drawable/ic_launcher_foreground.xml b/android/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..a30acde --- /dev/null +++ b/android/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/res/values/strings.xml b/android/src/main/res/values/strings.xml similarity index 100% rename from android/res/values/strings.xml rename to android/src/main/res/values/strings.xml diff --git a/android/res/values/styles.xml b/android/src/main/res/values/styles.xml similarity index 100% rename from android/res/values/styles.xml rename to android/src/main/res/values/styles.xml diff --git a/android/res/xml/backup_descriptor.xml b/android/src/main/res/xml/backup_descriptor.xml similarity index 100% rename from android/res/xml/backup_descriptor.xml rename to android/src/main/res/xml/backup_descriptor.xml diff --git a/android/src/ru/deadsoftware/cavedroid/AndroidLauncher.java b/android/src/ru/deadsoftware/cavedroid/AndroidLauncher.java deleted file mode 100644 index fcf0831..0000000 --- a/android/src/ru/deadsoftware/cavedroid/AndroidLauncher.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.deadsoftware.cavedroid; - -import android.content.pm.PackageManager; -import android.os.Bundle; -import com.badlogic.gdx.backends.android.AndroidApplication; -import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; - -public class AndroidLauncher extends AndroidApplication { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - AndroidApplicationConfiguration config = new AndroidApplicationConfiguration(); - config.hideStatusBar = true; - config.useImmersiveMode = true; - String gameFolder = ""; - try { - gameFolder = getPackageManager().getPackageInfo(getPackageName(), 0).applicationInfo.dataDir; - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - exit(); - } - CaveGame caveGame = new CaveGame(gameFolder, true); - caveGame.setDebug(BuildConfig.DEBUG); - initialize(caveGame, config); - } - - @Override - public void onBackPressed() { - } -} diff --git a/android/assets/font.fnt b/assets/font.fnt similarity index 100% rename from android/assets/font.fnt rename to assets/font.fnt diff --git a/android/assets/font.png b/assets/font.png similarity index 100% rename from android/assets/font.png rename to assets/font.png diff --git a/android/assets/gamelogo.png b/assets/gamelogo.png similarity index 100% rename from android/assets/gamelogo.png rename to assets/gamelogo.png diff --git a/assets/icons/icon128.png b/assets/icons/icon128.png new file mode 100644 index 0000000..08d0b74 Binary files /dev/null and b/assets/icons/icon128.png differ diff --git a/assets/icons/icon256.png b/assets/icons/icon256.png new file mode 100644 index 0000000..4e36964 Binary files /dev/null and b/assets/icons/icon256.png differ diff --git a/assets/icons/icon512.png b/assets/icons/icon512.png new file mode 100644 index 0000000..35aa45b Binary files /dev/null and b/assets/icons/icon512.png differ diff --git a/assets/joy_background.png b/assets/joy_background.png new file mode 100644 index 0000000..b1ea80b Binary files /dev/null and b/assets/joy_background.png differ diff --git a/assets/joy_stick.png b/assets/joy_stick.png new file mode 100644 index 0000000..525dd7f Binary files /dev/null and b/assets/joy_stick.png differ diff --git a/assets/json/crafting.json b/assets/json/crafting.json new file mode 100644 index 0000000..9a67e01 --- /dev/null +++ b/assets/json/crafting.json @@ -0,0 +1,110 @@ +{ + "planks_oak": { + "input": ["log_oak", "none", "none", "none", "none", "none", "none", "none", "none"], + "count": 4 + }, + "planks_spruce": { + "input": ["log_spruce", "none", "none", "none", "none", "none", "none", "none", "none"], + "count": 4 + }, + "stick": { + "input": ["planks.*", "none", "none", "planks.*", "none", "none", "none", "none", "none"], + "count": 4 + }, + "wood_pickaxe": { + "input": ["planks.*", "planks.*", "planks.*", "none", "stick", "none", "none", "stick", "none"], + "count": 59 + }, + "wood_axe": { + "input": ["planks.*", "planks.*", "none", "planks.*", "stick", "none", "none", "stick", "none"], + "count": 60 + }, + "wood_sword": { + "input": ["planks.*", "none", "none", "planks.*", "none", "none", "stick", "none", "none"], + "count": 60 + }, + "wood_shovel": { + "input": ["planks.*", "none", "none", "stick", "none", "none", "stick", "none", "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": ["cobblestone", "none", "none", "cobblestone", "none", "none", "stick", "none", "none"], + "count": 132 + }, + "stone_shovel": { + "input": ["cobblestone", "none", "none", "stick", "none", "none", "stick", "none", "none"], + "count": 131 + }, + "iron_pickaxe": { + "input": ["iron_ingot", "iron_ingot", "iron_ingot", "none", "stick", "none", "none", "stick", "none"], + "count": 251 + }, + "iron_axe": { + "input": ["iron_ingot", "iron_ingot", "none", "iron_ingot", "stick", "none", "none", "stick", "none"], + "count": 250 + }, + "iron_sword": { + "input": ["iron_ingot", "none", "none", "iron_ingot", "none", "none", "stick", "none", "none"], + "count": 251 + }, + "iron_shovel": { + "input": ["iron_ingot", "none", "none", "stick", "none", "none", "stick", "none", "none"], + "count": 250 + }, + "gold_pickaxe": { + "input": ["gold_ingot", "gold_ingot", "gold_ingot", "none", "stick", "none", "none", "stick", "none"], + "count": 33 + }, + "gold_axe": { + "input": ["gold_ingot", "gold_ingot", "none", "gold_ingot", "stick", "none", "none", "stick", "none"], + "count": 32 + }, + "gold_sword": { + "input": ["gold_ingot", "none", "none", "gold_ingot", "none", "none", "stick", "none", "none"], + "count": 33 + }, + "gold_shovel": { + "input": ["gold_ingot", "none", "none", "stick", "none", "none", "stick", "none", "none"], + "count": 32 + }, + "diamond_pickaxe": { + "input": ["diamond", "diamond", "diamond", "none", "stick", "none", "none", "stick", "none"], + "count": 1562 + }, + "diamond_axe": { + "input": ["diamond", "diamond", "none", "diamond", "stick", "none", "none", "stick", "none"], + "count": 1561 + }, + "diamond_sword": { + "input": ["diamond", "none", "none", "diamond", "none", "none", "stick", "none", "none"], + "count": 1562 + }, + "diamond_shovel": { + "input": ["diamond", "none", "none", "stick", "none", "none", "stick", "none", "none"], + "count": 1561 + }, + "snow_block": { + "input": ["snowball", "snowball", "none", "snowball", "snowball", "none", "none", "none", "none"], + "count": 1 + }, + "crafting_table": { + "input": ["planks.*", "planks.*", "none", "planks.*", "planks.*", "none", "none", "none", "none"] + }, + "furnace": { + "input": ["cobblestone", "cobblestone", "cobblestone", "cobblestone", "none", "cobblestone", "cobblestone", "cobblestone", "cobblestone"] + }, + "chest": { + "input": ["planks_.*", "planks_.*", "planks_.*", "planks_.*", "none", "planks_.*", "planks_.*", "planks_.*", "planks_.*"] + }, + "bed": { + "input": ["wool_.*", "wool_.*", "wool_.*", "planks_.*", "planks_.*", "planks_.*", "none", "none", "none"] + } +} \ No newline at end of file diff --git a/assets/json/game_items.json b/assets/json/game_items.json new file mode 100644 index 0000000..349daf3 --- /dev/null +++ b/assets/json/game_items.json @@ -0,0 +1,1439 @@ +{ + "blocks": { + "none": { + "collision": false, + "transparent": true, + "drop": "none", + "texture": "none", + "meta": "none" + }, + "stone": { + "hp": 450, + "drop": "cobblestone", + "texture": "stone", + "tool_level": 1, + "tool_type": "pickaxe" + }, + "grass": { + "hp": 54, + "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, + "drop": "dirt", + "texture": "dirt", + "tool_level": 0, + "tool_type": "shovel" + }, + "cobblestone": { + "hp": 600, + "drop": "cobblestone", + "texture": "cobblestone", + "tool_level": 1, + "tool_type": "pickaxe" + }, + "planks_oak": { + "hp": 180, + "drop": "planks_oak", + "texture": "planks_oak", + "tool_level": 0, + "tool_type": "axe" + }, + "planks_spruce": { + "hp": 180, + "drop": "planks_spruce", + "texture": "planks_spruce", + "tool_level": 0, + "tool_type": "axe" + }, + "crafting_table": { + "hp": 180, + "drop": "crafting_table", + "texture": "crafting_table", + "tool_level": 0, + "tool_type": "axe" + }, + "sapling_oak": { + "collision": false, + "transparent": true, + "block_required": true, + "drop": "sapling_oak", + "texture": "sapling_oak", + "hp": 0 + }, + "sapling_spruce": { + "collision": false, + "transparent": true, + "block_required": true, + "drop": "sapling_spruce", + "texture": "sapling_spruce", + "hp": 0 + }, + "bedrock": { + "drop": "bedrock", + "texture": "bedrock" + }, + "water": { + "collision": false, + "transparent": true, + "drop": "water", + "meta": "water", + "texture": "water_still", + "animated": true, + "frames": 16, + "state": 0 + }, + "lava": { + "collision": false, + "drop": "lava", + "meta": "lava", + "texture": "lava_still", + "animated": true, + "frames": 16, + "state": 0, + "damage": 4 + }, + "sand": { + "hp": 45, + "drop": "sand", + "texture": "sand", + "tool_level": 0, + "tool_type": "shovel", + "fallable": true + }, + "gravel": { + "hp": 54, + "drop": "gravel", + "texture": "gravel", + "tool_level": 0, + "tool_type": "shovel", + "fallable": true + }, + "gold_ore": { + "hp": 900, + "drop": "gold_ore", + "texture": "gold_ore", + "tool_level": 3, + "tool_type": "pickaxe" + }, + "iron_ore": { + "hp": 900, + "drop": "iron_ore", + "texture": "iron_ore", + "tool_level": 2, + "tool_type": "pickaxe" + }, + "coal_ore": { + "hp": 900, + "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, + "drop": "log_oak", + "texture": "log_oak", + "tool_level": 0, + "tool_type": "axe" + }, + "leaves_oak": { + "hp": 21, + "drop": "leaves_oak", + "texture": "leaves_oak", + "tool_level": 1, + "tool_type": "shears", + "tint": "#5AC557" + }, + "log_spruce": { + "hp": 180, + "drop": "log_spruce", + "texture": "log_spruce", + "tool_level": 0, + "tool_type": "axe" + }, + "leaves_spruce": { + "hp": 21, + "drop": "leaves_spruce", + "texture": "leaves_spruce", + "tool_level": 1, + "tool_type": "shears", + "tint": "#486D4E" + }, + "sponge": { + "hp": 54, + "drop": "sponge", + "texture": "sponge", + "tool_level": 0, + "tool_type": "hoe" + }, + "glass": { + "hp": 27, + "transparent": true, + "drop": "none", + "texture": "glass" + }, + "lapis_ore": { + "hp": 900, + "drop": "lapis", + "texture": "lapis_ore", + "tool_level": 2, + "tool_type": "pickaxe" + }, + "lapis_block": { + "hp": 900, + "drop": "lapis_block", + "texture": "lapis_block", + "tool_level": 2, + "tool_type": "pickaxe" + }, + "sandstone": { + "hp": 240, + "drop": "sandstone", + "texture": "sandstone", + "tool_level": 1, + "tool_type": "pickaxe" + }, + "noteblock": { + "hp": 75, + "drop": "noteblock", + "texture": "noteblock", + "tool_level": 0, + "tool_type": "axe" + }, + "bed_l": { + "hp": 21, + "collision": false, + "background": true, + "transparent": true, + "drop": "bed", + "texture": "bed_l", + "tool_level": 0, + "tool_type": "axe" + }, + "bed_r": { + "hp": 21, + "collision": false, + "background": true, + "transparent": true, + "drop": "bed", + "texture": "bed_r", + "tool_level": 0, + "tool_type": "axe" + }, + "web": { + "hp": 1200, + "collision": false, + "transparent": true, + "drop": "web", + "texture": "web", + "tool_level": 1, + "tool_type": "shears" + }, + "tallgrass": { + "collision": false, + "transparent": true, + "block_required": true, + "drop": "tallgrass", + "texture": "tallgrass", + "tool_level": 1, + "tool_type": "shears", + "tint": "#5AC557", + "hp": 0 + }, + "deadbush": { + "collision": false, + "transparent": true, + "block_required": true, + "drop": "deadbush", + "texture": "deadbush", + "tool_level": 1, + "tool_type": "shears", + "hp": 0 + }, + "bricks": { + "hp": 600, + "drop": "bricks", + "texture": "bricks", + "tool_level": 1, + "tool_type": "pickaxe" + }, + "dandelion": { + "collision": false, + "transparent": true, + "block_required": true, + "drop": "dandelion", + "texture": "dandelion", + "hp": 0 + }, + "rose": { + "collision": false, + "transparent": true, + "block_required": true, + "drop": "rose", + "texture": "rose", + "hp": 0 + }, + "mushroom_brown": { + "collision": false, + "transparent": true, + "block_required": true, + "drop": "mushroom_brown", + "texture": "mushroom_brown", + "hp": 0 + }, + "mushroom_red": { + "collision": false, + "transparent": true, + "block_required": true, + "drop": "mushroom_red", + "texture": "mushroom_red", + "hp": 0 + }, + "wool_colored_white": { + "hp": 75, + "drop": "wool_colored_white", + "texture": "wool_colored_white", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_orange": { + "hp": 75, + "drop": "wool_colored_orange", + "texture": "wool_colored_orange", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_magenta": { + "hp": 75, + "drop": "wool_colored_magenta", + "texture": "wool_colored_magenta", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_light_blue": { + "hp": 75, + "drop": "wool_colored_light_blue", + "texture": "wool_colored_light_blue", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_yellow": { + "hp": 75, + "drop": "wool_colored_yellow", + "texture": "wool_colored_yellow", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_lime": { + "hp": 75, + "drop": "wool_colored_lime", + "texture": "wool_colored_lime", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_pink": { + "hp": 75, + "drop": "wool_colored_pink", + "texture": "wool_colored_pink", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_gray": { + "hp": 75, + "drop": "wool_colored_gray", + "texture": "wool_colored_gray", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_silver": { + "hp": 75, + "drop": "wool_colored_silver", + "texture": "wool_colored_silver", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_cyan": { + "hp": 75, + "drop": "wool_colored_cyan", + "texture": "wool_colored_cyan", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_purple": { + "hp": 75, + "drop": "wool_colored_purple", + "texture": "wool_colored_purple", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_blue": { + "hp": 75, + "drop": "wool_colored_blue", + "texture": "wool_colored_blue", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_brown": { + "hp": 75, + "drop": "wool_colored_brown", + "texture": "wool_colored_brown", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_green": { + "hp": 75, + "drop": "wool_colored_green", + "texture": "wool_colored_green", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_red": { + "hp": 75, + "drop": "wool_colored_red", + "texture": "wool_colored_red", + "tool_level": 0, + "tool_type": "shears" + }, + "wool_colored_black": { + "hp": 75, + "drop": "wool_colored_black", + "texture": "wool_colored_black", + "tool_level": 0, + "tool_type": "shears" + }, + "gold_block": { + "hp": 900, + "drop": "gold_block", + "texture": "gold_block", + "tool_level": 3, + "tool_type": "pickaxe" + }, + "iron_block": { + "hp": 1500, + "drop": "iron_block", + "texture": "iron_block", + "tool_level": 2, + "tool_type": "pickaxe" + }, + "stone_slab_bottom": { + "top": 8, + "sprite_top": 8, + "hp": 600, + "transparent": true, + "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", + "drop_count": 2, + "texture": "stone_slab", + "tool_level": 1, + "tool_type": "pickaxe" + }, + "sandstone_slab_bottom": { + "top": 8, + "sprite_top": 8, + "hp": 600, + "transparent": true, + "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, + "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" + }, + "spruce_slab_bottom": { + "top": 8, + "sprite_top": 8, + "hp": 180, + "transparent": true, + "drop": "spruce_slab", + "meta": "slab", + "texture": "planks_spruce", + "full_block": "planks_spruce", + "other_part": "spruce_slab_top", + "tool_level": 0, + "tool_type": "axe" + }, + "spruce_slab_top": { + "bottom": 8, + "sprite_bottom": 8, + "hp": 180, + "transparent": true, + "drop": "spruce_slab", + "meta": "slab", + "texture": "planks_spruce", + "full_block": "planks_spruce", + "other_part": "spruce_slab_bottom", + "tool_level": 0, + "tool_type": "axe" + }, + "cobblestone_slab_bottom": { + "top": 8, + "sprite_top": 8, + "hp": 600, + "transparent": true, + "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, + "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, + "drop": "stonebrick", + "texture": "stonebrick", + "tool_level": 1, + "tool_type": "pickaxe" + }, + "stonebrick_slab_bottom": { + "top": 8, + "sprite_top": 8, + "hp": 450, + "transparent": true, + "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, + "drop": "cactus", + "texture": "cactus", + "damage": 1 + }, + "water_16": { + "collision": false, + "transparent": true, + "drop": "water_16", + "meta": "water", + "texture": "water_flow", + "animated": true, + "frames": 16, + "state": 1 + }, + "water_12": { + "top": 4, + "sprite_top": 4, + "collision": false, + "transparent": true, + "drop": "water_12", + "meta": "water", + "texture": "water_flow", + "animated": true, + "frames": 16, + "state": 2 + }, + "water_8": { + "top": 8, + "sprite_top": 8, + "collision": false, + "transparent": true, + "drop": "water_8", + "meta": "water", + "texture": "water_flow", + "animated": true, + "frames": 16, + "state": 3 + }, + "water_4": { + "top": 12, + "sprite_top": 12, + "collision": false, + "transparent": true, + "drop": "water_4", + "meta": "water", + "texture": "water_flow", + "animated": true, + "frames": 16, + "state": 4 + }, + "lava_16": { + "collision": false, + "drop": "lava_16", + "meta": "lava", + "texture": "lava_flow", + "animated": true, + "frames": 16, + "state": 1, + "damage": 4 + }, + "lava_12": { + "top": 4, + "sprite_top": 4, + "collision": false, + "transparent": true, + "drop": "lava_12", + "meta": "lava", + "texture": "lava_flow", + "animated": true, + "frames": 16, + "state": 2, + "damage": 4 + }, + "lava_8": { + "top": 8, + "sprite_top": 8, + "collision": false, + "transparent": true, + "drop": "lava_8", + "meta": "lava", + "texture": "lava_flow", + "animated": true, + "frames": 16, + "state": 3, + "damage": 4 + }, + "lava_4": { + "top": 12, + "sprite_top": 12, + "collision": false, + "transparent": true, + "drop": "lava_4", + "meta": "lava", + "texture": "lava_flow", + "animated": true, + "frames": 16, + "state": 4, + "damage": 4 + }, + "obsidian": { + "hp": 15000, + "drop": "obsidian", + "texture": "obsidian", + "tool_level": 4, + "tool_type": "pickaxe" + }, + "snow": { + "top": 14, + "sprite_top": 14, + "collision": false, + "transparent": true, + "drop": "snowball", + "texture": "snow", + "hp": 6, + "tool_level": 1, + "tool_type": "shovel", + "block_required": true + }, + "snow_block": { + "collision": true, + "transparent": true, + "drop": "snowball", + "drop_count": 4, + "texture": "snow", + "hp": 60, + "tool_level": 1, + "tool_type": "shovel" + }, + "furnace": { + "hp": 1050, + "collision": true, + "transparent": false, + "drop": "furnace", + "texture": "furnace", + "animated": true, + "frames": 2, + "tool_level": 1, + "tool_type": "pickaxe", + "meta": "furnace" + }, + "chest": { + "hp": 180, + "collision": true, + "transparent": true, + "drop": "chest", + "texture": "chest", + "tool_level": 0, + "tool_type": "axe", + "meta": "chest", + "top": 2, + "left": 1, + "right": 1, + "sprite_top": 2, + "sprite_left": 1, + "sprite_right": 1 + } + }, + "items": { + "none": { + "name": "", + "type": "none", + "texture": "none" + }, + "stone": { + "name": "Stone", + "type": "block", + "texture": "stone" + }, + "grass": { + "name": "Grass", + "type": "block", + "texture": "grass" + }, + "dirt": { + "name": "Dirt", + "type": "block", + "texture": "dirt" + }, + "cobblestone": { + "name": "Cobblestone", + "type": "block", + "texture": "cobblestone", + "smelt_product": "stone" + }, + "planks_oak": { + "name": "Oak Planks", + "type": "block", + "texture": "planks_oak", + "burning_time": 15000 + }, + "sapling_oak": { + "name": "Oak Sapling", + "type": "block", + "texture": "sapling_oak", + "burning_time": 5000 + }, + "planks_spruce": { + "name": "Spruce Planks", + "type": "block", + "texture": "planks_spruce", + "burning_time": 15000 + }, + "crafting_table": { + "name": "Crafting Table", + "type": "block", + "texture": "crafting_table", + "burning_time": 15000 + }, + "furnace": { + "name": "Furnace", + "type": "block", + "texture": "furnace_off" + }, + "chest": { + "name": "Chest", + "type": "block", + "texture": "chest" + }, + "sapling_spruce": { + "name": "Spruce Sapling", + "type": "block", + "texture": "sapling_spruce", + "burning_time": 5000 + }, + "bedrock": { + "name": "Bedrock", + "type": "block", + "texture": "bedrock" + }, + "sand": { + "name": "Sand", + "type": "block", + "texture": "sand", + "smelt_product": "glass" + }, + "gravel": { + "name": "Gravel", + "type": "block", + "texture": "gravel" + }, + "gold_ore": { + "name": "Golden Ore", + "type": "block", + "texture": "gold_ore", + "smelt_product": "gold_ingot" + }, + "iron_ore": { + "name": "Iron Ore", + "type": "block", + "texture": "iron_ore", + "smelt_product": "iron_ingot" + }, + "coal_ore": { + "name": "Coal Ore", + "type": "block", + "texture": "coal_ore", + "smelt_product": "coal" + }, + "diamond_ore": { + "name": "Diamond Ore", + "type": "block", + "texture": "diamond_ore", + "smelt_product": "diamond" + }, + "log_oak": { + "name": "Wood", + "type": "block", + "texture": "log_oak", + "burning_time": 15000, + "smelt_product": "charcoal" + }, + "leaves_oak": { + "name": "Leaves", + "type": "block", + "texture": "leaves_oak" + }, + "log_spruce": { + "name": "Spruce", + "type": "block", + "texture": "log_spruce", + "burning_time": 15000, + "smelt_product": "charcoal" + }, + "leaves_spruce": { + "name": "Spruce Leaves", + "type": "block", + "texture": "leaves_spruce" + }, + "glass": { + "name": "Glass", + "type": "block", + "texture": "glass" + }, + "lapis_ore": { + "name": "Lapis Ore", + "type": "block", + "texture": "lapis_ore", + "smelt_product": "lapis_lazuli" + }, + "lapis_block": { + "name": "Lapis Block", + "type": "block", + "texture": "lapis_block" + }, + "sandstone": { + "name": "Sandstone", + "type": "block", + "texture": "sandstone" + }, + "web": { + "name": "Cobweb", + "type": "block", + "texture": "web" + }, + "tallgrass": { + "name": "Tall Grass", + "type": "block", + "texture": "tallgrass", + "origin_x": 0.5 + }, + "deadbush": { + "name": "Dead Bush", + "type": "block", + "texture": "deadbush", + "origin_x": 0.5, + "burning_time": 5000 + }, + "bricks": { + "name": "Bricks", + "type": "block", + "texture": "bricks" + }, + "dandelion": { + "name": "Dandelion", + "type": "block", + "texture": "dandelion", + "origin_x": 0.5 + }, + "rose": { + "name": "Rose", + "type": "block", + "texture": "rose", + "origin_x": 0.5 + }, + "mushroom_brown": { + "name": "Mushroom", + "type": "block", + "texture": "mushroom_brown", + "origin_x": 0.5 + }, + "mushroom_red": { + "name": "Mushroom", + "type": "block", + "texture": "mushroom_red", + "origin_x": 0.5 + }, + "wool_colored_white": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_white" + }, + "wool_colored_orange": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_orange" + }, + "wool_colored_magenta": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_magenta" + }, + "wool_colored_light_blue": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_light_blue" + }, + "wool_colored_yellow": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_yellow" + }, + "wool_colored_lime": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_lime" + }, + "wool_colored_pink": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_pink" + }, + "wool_colored_gray": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_gray" + }, + "wool_colored_silver": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_silver" + }, + "wool_colored_cyan": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_cyan" + }, + "wool_colored_purple": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_purple" + }, + "wool_colored_blue": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_blue" + }, + "wool_colored_brown": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_brown" + }, + "wool_colored_green": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_green" + }, + "wool_colored_red": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_red" + }, + "wool_colored_black": { + "name": "Wool", + "type": "block", + "texture": "wool_colored_black" + }, + "gold_block": { + "name": "Gold Block", + "type": "block", + "texture": "gold_block" + }, + "iron_block": { + "name": "Iron Block", + "type": "block", + "texture": "iron_block" + }, + "diamond_block": { + "name": "Diamond Block", + "type": "block", + "texture": "diamond_block" + }, + "stone_slab": { + "name": "Stone Slab", + "type": "slab", + "texture": "stone_slab", + "top_slab_block": "stone_slab_top", + "bottom_slab_block": "stone_slab_bottom" + }, + "sandstone_slab": { + "name": "Sandstone Slab", + "type": "slab", + "texture": "sandstone_slab", + "top_slab_block": "sandstone_slab_top", + "bottom_slab_block": "sandstone_slab_bottom" + }, + "oak_slab": { + "name": "Oak Slab", + "type": "slab", + "texture": "oak_slab", + "top_slab_block": "oak_slab_top", + "bottom_slab_block": "oak_slab_bottom", + "burn_time": 7500 + }, + "spruce_slab": { + "name": "Spruce Slab", + "type": "slab", + "texture": "spruce_slab", + "top_slab_block": "spruce_slab_top", + "bottom_slab_block": "spruce_slab_bottom", + "burn_time": 7500 + }, + "cobblestone_slab": { + "name": "Cobblestone Slab", + "type": "slab", + "texture": "cobblestone_slab", + "top_slab_block": "cobblestone_slab_top", + "bottom_slab_block": "cobblestone_slab_bottom" + }, + "brick_slab": { + "name": "Brick Slab", + "type": "slab", + "texture": "brick_slab", + "top_slab_block": "brick_slab_top", + "bottom_slab_block": "brick_slab_bottom" + }, + "stonebrick": { + "name": "Stone Brick", + "type": "block", + "texture": "stonebrick" + }, + "stonebrick_slab": { + "name": "Stone Brick Slab", + "type": "slab", + "texture": "stonebrick_slab", + "top_slab_block": "stonebrick_slab_top", + "bottom_slab_block": "stonebrick_slab_bottom" + }, + "cactus": { + "name": "Cactus", + "type": "block", + "texture": "cactus" + }, + "obsidian": { + "name": "Obsidian", + "type": "block", + "texture": "obsidian" + }, + "snow_block": { + "name": "Snow Block", + "type": "block", + "texture": "snow" + }, + "stick": { + "name": "Stick", + "texture": "stick", + "burning_time": 5000 + }, + "wood_sword": { + "name": "Wooden Sword", + "type": "sword", + "texture": "wood_sword", + "origin_x": 0.125, + "tool_level": 1, + "max_stack": 60, + "burning_time": 10000 + }, + "stone_sword": { + "name": "Stone Sword", + "type": "sword", + "texture": "stone_sword", + "origin_x": 0.125, + "tool_level": 2, + "max_stack": 132 + }, + "iron_sword": { + "name": "Iron Sword", + "type": "sword", + "texture": "iron_sword", + "origin_x": 0.125, + "tool_level": 3, + "max_stack": 251 + }, + "diamond_sword": { + "name": "Diamond Sword", + "type": "sword", + "texture": "diamond_sword", + "origin_x": 0.125, + "tool_level": 4, + "max_stack": 1562 + }, + "gold_sword": { + "name": "Golden Sword", + "type": "sword", + "texture": "gold_sword", + "origin_x": 0.125, + "tool_level": 1, + "max_stack": 33 + }, + "wood_shovel": { + "name": "Wooden Shovel", + "type": "shovel", + "texture": "wood_shovel", + "origin_x": 0.125, + "tool_level" : 1, + "max_stack": 59 + }, + "stone_shovel": { + "name": "Stone Shovel", + "type": "shovel", + "texture": "stone_shovel", + "origin_x": 0.125, + "tool_level" : 2, + "max_stack": 131 + }, + "iron_shovel": { + "name": "Iron Shovel", + "type": "shovel", + "texture": "iron_shovel", + "origin_x": 0.125, + "tool_level" : 3, + "max_stack": 250 + }, + "diamond_shovel": { + "name": "Diamond Shovel", + "type": "shovel", + "texture": "diamond_shovel", + "origin_x": 0.125, + "tool_level" : 4, + "max_stack": 1561 + }, + "gold_shovel": { + "name": "Golden Shovel", + "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, + "burning_time": 10000 + }, + "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, + "burning_time": 10000 + }, + "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": "usable", + "texture": "bucket_empty", + "origin_x": 0.25, + "action_key": "use_empty_bucket", + "max_stack": 1 + }, + "bucket_water": { + "name": "Water Bucket", + "type": "usable", + "texture": "bucket_water", + "origin_x": 0.25, + "action_key": "use_water_bucket", + "max_stack": 1 + }, + "bucket_lava": { + "name": "Lava Bucket", + "type": "usable", + "texture": "bucket_lava", + "origin_x": 0.25, + "action_key": "use_lava_bucket", + "max_stack": 1, + "burning_time": 1000000 + }, + "snowball": { + "name": "Snowball", + "type": "usable", + "texture": "snowball", + "action_key": "use_snow_ball_action", + "max_stack": 16 + }, + "coal": { + "name": "Coal", + "texture": "coal", + "burning_time": 80000, + "origin_x": 0.25 + }, + "charcoal": { + "name": "Charcoal", + "texture": "charcoal", + "burning_time": 80000, + "origin_x": 0.25 + }, + "iron_ingot": { + "name": "Iron Ingot", + "texture": "iron_ingot", + "origin_x": 0.5 + }, + "gold_ingot": { + "name": "Gold Ingot", + "texture": "gold_ingot", + "origin_x": 0.5 + }, + "diamond": { + "name": "Diamond", + "texture": "diamond", + "origin_x": 0.5 + }, + "lapis_lazuli": { + "name": "Lapis Lazuli", + "texture": "lapis_lazuli", + "origin_x": 0.5 + }, + "bed": { + "name": "Bed", + "type": "usable", + "texture": "bed", + "action_key": "use_bed_action" + }, + "spawn_egg_pig": { + "name": "Pig Spawn Egg", + "type": "usable", + "texture": "spawn_egg", + "tint": "#EDBFB4", + "action_key": "use_spawn_egg_pig", + "origin_x": 0.5 + }, + "porkchop_raw": { + "name": "Raw Porkchop", + "type": "food", + "texture": "porkchop_raw", + "heal": 3, + "smelt_product": "porkchop_cooked" + }, + "porkchop_cooked": { + "name": "Raw Porkchop", + "type": "food", + "texture": "porkchop_cooked", + "heal": 8 + } + } +} diff --git a/assets/json/menu.json b/assets/json/menu.json new file mode 100644 index 0000000..409fd82 --- /dev/null +++ b/assets/json/menu.json @@ -0,0 +1,62 @@ +{ + "main": { + "new_game": { + "label": "New Game", + "actionKey": "new_game_action" + }, + "load_game": { + "label": "Load Game", + "actionKey": "load_game_action" + }, + "options": { + "label": "Settings", + "actionKey": "options_action" + }, + "quit": { + "label": "Quit", + "actionKey": "exit_game_action" + } + }, + "new_game": { + "creative": { + "label": "Creative", + "actionKey": "new_game_creative_action" + }, + "survival": { + "label": "Survival", + "actionKey": "new_game_survival_action" + }, + "back": { + "label": "Back", + "actionKey": "menu_main_action" + } + }, + "options": { + "screen_scale": { + "type": "numerical_option", + "label": "Screen Scale: x%d", + "actionKey": "screen_scale_action", + "options": ["screen_scale"] + }, + "dyncam": { + "type": "boolean_option", + "label": "Dynamic Camera: %s", + "actionKey": "toggle_dyncam_action", + "options": ["dyncam"] + }, + "fullscreen": { + "type": "boolean_option", + "label": "Fullscreen: %s", + "actionKey": "toggle_fullscreen_action", + "options": ["fullscreen"], + "visibility": { + "android": false, + "ios": false + } + }, + "back": { + "label": "Back", + "actionKey": "menu_main_action" + } + } +} diff --git a/android/assets/json/texture_regions.json b/assets/json/texture_regions.json similarity index 54% rename from android/assets/json/texture_regions.json rename to assets/json/texture_regions.json index e1f3e48..240d1de 100644 --- a/android/assets/json/texture_regions.json +++ b/assets/json/texture_regions.json @@ -35,60 +35,20 @@ "y": 26, "w": 26, "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 + "inv": { + "x": 78, + "y": 26, + "w": 26, + "h": 26 }, - "break_9": { - "x": 144, - "w": 16, - "h": 16 + "pause": { + "x": 104, + "w": 26, + "h": 26 } }, - "allitems": { + "textures/allitems": { "creative": { "w": 176, "h": 136 @@ -100,7 +60,42 @@ "h": 15 } }, - "buttons": { + "textures/inventory": { + "survival": { + "w": 176, + "h": 166 + } + }, + "textures/crafting_table": { + "crafting_table": { + "w": 176, + "h": 166 + } + }, + "textures/furnace": { + "furnace": { + "w": 176, + "h": 166 + }, + "furnace_burn": { + "x": 176, + "w": 14, + "h": 14 + }, + "furnace_progress": { + "x": 176, + "y": 14, + "w": 24, + "h": 14 + } + }, + "textures/chest": { + "chest": { + "w": 176, + "h": 168 + } + }, + "textures/buttons": { "button_0": { "w": 200, "h": 20 @@ -116,7 +111,7 @@ "h": 20 } }, - "gui": { + "textures/gui": { "hotbar": { "y": 16, "w": 182, @@ -134,5 +129,20 @@ }, "shade": {}, "gamelogo": {}, - "background": {} + "textures/background": {}, + "textures/health":{ + "heart_whole": { + "w": 9 + }, + "heart_half": { + "x": 9, + "w": 9 + }, + "heart_empty": { + "x": 18, + "w": 9 + } + }, + "joy_stick": {}, + "joy_background": {} } \ No newline at end of file diff --git a/assets/json/touch_buttons.json b/assets/json/touch_buttons.json new file mode 100644 index 0000000..cbb9a15 --- /dev/null +++ b/assets/json/touch_buttons.json @@ -0,0 +1,23 @@ +{ + "alt": { + "x": -48, + "y": -48, + "w": 48, + "h": 48, + "key": "L-Alt" + }, + "inv": { + "x": -96, + "y": -48, + "w": 48, + "h": 48, + "key": "E" + }, + "pause": { + "x": -48, + "y": 0, + "w": 48, + "h": 48, + "key": "Escape" + } +} \ No newline at end of file diff --git a/android/assets/shade.png b/assets/shade.png similarity index 100% rename from android/assets/shade.png rename to assets/shade.png diff --git a/android/assets/allitems.png b/assets/textures/allitems.png similarity index 100% rename from android/assets/allitems.png rename to assets/textures/allitems.png diff --git a/android/assets/background.png b/assets/textures/background.png similarity index 100% rename from android/assets/background.png rename to assets/textures/background.png diff --git a/android/assets/background_top.png b/assets/textures/background_top.png similarity index 100% rename from android/assets/background_top.png rename to assets/textures/background_top.png diff --git a/android/assets/break.png b/assets/textures/break.png similarity index 100% rename from android/assets/break.png rename to assets/textures/break.png diff --git a/android/assets/buttons.png b/assets/textures/buttons.png similarity index 100% rename from android/assets/buttons.png rename to assets/textures/buttons.png diff --git a/assets/textures/chest.png b/assets/textures/chest.png new file mode 100644 index 0000000..2c6d0c6 Binary files /dev/null and b/assets/textures/chest.png differ diff --git a/assets/textures/chest_large.png b/assets/textures/chest_large.png new file mode 100644 index 0000000..961891f Binary files /dev/null and b/assets/textures/chest_large.png differ diff --git a/assets/textures/crafting_table.png b/assets/textures/crafting_table.png new file mode 100644 index 0000000..e019e39 Binary files /dev/null and b/assets/textures/crafting_table.png differ diff --git a/assets/textures/furnace.png b/assets/textures/furnace.png new file mode 100644 index 0000000..fc04793 Binary files /dev/null and b/assets/textures/furnace.png differ diff --git a/android/assets/gui.png b/assets/textures/gui.png similarity index 100% rename from android/assets/gui.png rename to assets/textures/gui.png diff --git a/assets/textures/health.png b/assets/textures/health.png new file mode 100644 index 0000000..99011d5 Binary files /dev/null and b/assets/textures/health.png differ diff --git a/assets/textures/inventory.png b/assets/textures/inventory.png new file mode 100644 index 0000000..8bc6cf7 Binary files /dev/null and b/assets/textures/inventory.png differ diff --git a/android/assets/mobs/char/0_0.png b/assets/textures/mobs/char/0.png similarity index 100% rename from android/assets/mobs/char/0_0.png rename to assets/textures/mobs/char/0.png diff --git a/android/assets/mobs/char/0_1.png b/assets/textures/mobs/char/1.png similarity index 100% rename from android/assets/mobs/char/0_1.png rename to assets/textures/mobs/char/1.png diff --git a/android/assets/mobs/char/0_2.png b/assets/textures/mobs/char/2.png similarity index 100% rename from android/assets/mobs/char/0_2.png rename to assets/textures/mobs/char/2.png diff --git a/android/assets/mobs/char/0_3.png b/assets/textures/mobs/char/3.png similarity index 100% rename from android/assets/mobs/char/0_3.png rename to assets/textures/mobs/char/3.png diff --git a/android/assets/mobs/pig/0_0.png b/assets/textures/mobs/pig/0.png similarity index 100% rename from android/assets/mobs/pig/0_0.png rename to assets/textures/mobs/pig/0.png diff --git a/android/assets/mobs/pig/0_1.png b/assets/textures/mobs/pig/1.png similarity index 100% rename from android/assets/mobs/pig/0_1.png rename to assets/textures/mobs/pig/1.png diff --git a/android/assets/textures/blocks/bed_l.png b/assets/textures/textures/blocks/bed_l.png similarity index 100% rename from android/assets/textures/blocks/bed_l.png rename to assets/textures/textures/blocks/bed_l.png diff --git a/android/assets/textures/blocks/bed_r.png b/assets/textures/textures/blocks/bed_r.png similarity index 100% rename from android/assets/textures/blocks/bed_r.png rename to assets/textures/textures/blocks/bed_r.png diff --git a/android/assets/textures/blocks/bedrock.png b/assets/textures/textures/blocks/bedrock.png similarity index 100% rename from android/assets/textures/blocks/bedrock.png rename to assets/textures/textures/blocks/bedrock.png diff --git a/android/assets/textures/blocks/bookshelf.png b/assets/textures/textures/blocks/bookshelf.png similarity index 100% rename from android/assets/textures/blocks/bookshelf.png rename to assets/textures/textures/blocks/bookshelf.png diff --git a/android/assets/textures/blocks/bricks.png b/assets/textures/textures/blocks/bricks.png similarity index 100% rename from android/assets/textures/blocks/bricks.png rename to assets/textures/textures/blocks/bricks.png diff --git a/android/assets/textures/blocks/cactus.png b/assets/textures/textures/blocks/cactus.png similarity index 100% rename from android/assets/textures/blocks/cactus.png rename to assets/textures/textures/blocks/cactus.png diff --git a/android/assets/textures/blocks/cake.png b/assets/textures/textures/blocks/cake.png similarity index 100% rename from android/assets/textures/blocks/cake.png rename to assets/textures/textures/blocks/cake.png diff --git a/assets/textures/textures/blocks/chest.png b/assets/textures/textures/blocks/chest.png new file mode 100644 index 0000000..a2ca371 Binary files /dev/null and b/assets/textures/textures/blocks/chest.png differ diff --git a/android/assets/textures/blocks/clay.png b/assets/textures/textures/blocks/clay.png similarity index 100% rename from android/assets/textures/blocks/clay.png rename to assets/textures/textures/blocks/clay.png diff --git a/android/assets/textures/blocks/coal_block.png b/assets/textures/textures/blocks/coal_block.png similarity index 100% rename from android/assets/textures/blocks/coal_block.png rename to assets/textures/textures/blocks/coal_block.png diff --git a/android/assets/textures/blocks/coal_ore.png b/assets/textures/textures/blocks/coal_ore.png similarity index 100% rename from android/assets/textures/blocks/coal_ore.png rename to assets/textures/textures/blocks/coal_ore.png diff --git a/android/assets/textures/blocks/cobblestone.png b/assets/textures/textures/blocks/cobblestone.png similarity index 100% rename from android/assets/textures/blocks/cobblestone.png rename to assets/textures/textures/blocks/cobblestone.png diff --git a/android/assets/textures/blocks/cobblestone_mossy.png b/assets/textures/textures/blocks/cobblestone_mossy.png similarity index 100% rename from android/assets/textures/blocks/cobblestone_mossy.png rename to assets/textures/textures/blocks/cobblestone_mossy.png diff --git a/android/assets/textures/blocks/crafting_table.png b/assets/textures/textures/blocks/crafting_table.png similarity index 100% rename from android/assets/textures/blocks/crafting_table.png rename to assets/textures/textures/blocks/crafting_table.png diff --git a/android/assets/textures/blocks/dandelion.png b/assets/textures/textures/blocks/dandelion.png similarity index 100% rename from android/assets/textures/blocks/dandelion.png rename to assets/textures/textures/blocks/dandelion.png diff --git a/android/assets/textures/blocks/deadbush.png b/assets/textures/textures/blocks/deadbush.png similarity index 100% rename from android/assets/textures/blocks/deadbush.png rename to assets/textures/textures/blocks/deadbush.png diff --git a/android/assets/textures/blocks/diamond_block.png b/assets/textures/textures/blocks/diamond_block.png similarity index 100% rename from android/assets/textures/blocks/diamond_block.png rename to assets/textures/textures/blocks/diamond_block.png diff --git a/android/assets/textures/blocks/diamond_ore.png b/assets/textures/textures/blocks/diamond_ore.png similarity index 100% rename from android/assets/textures/blocks/diamond_ore.png rename to assets/textures/textures/blocks/diamond_ore.png diff --git a/android/assets/textures/blocks/dirt.png b/assets/textures/textures/blocks/dirt.png similarity index 100% rename from android/assets/textures/blocks/dirt.png rename to assets/textures/textures/blocks/dirt.png diff --git a/assets/textures/textures/blocks/furnace.png b/assets/textures/textures/blocks/furnace.png new file mode 100644 index 0000000..080d8ad Binary files /dev/null and b/assets/textures/textures/blocks/furnace.png differ diff --git a/android/assets/textures/blocks/glass.png b/assets/textures/textures/blocks/glass.png similarity index 100% rename from android/assets/textures/blocks/glass.png rename to assets/textures/textures/blocks/glass.png diff --git a/android/assets/textures/blocks/gold_block.png b/assets/textures/textures/blocks/gold_block.png similarity index 100% rename from android/assets/textures/blocks/gold_block.png rename to assets/textures/textures/blocks/gold_block.png diff --git a/android/assets/textures/blocks/gold_ore.png b/assets/textures/textures/blocks/gold_ore.png similarity index 100% rename from android/assets/textures/blocks/gold_ore.png rename to assets/textures/textures/blocks/gold_ore.png diff --git a/android/assets/textures/blocks/grass.png b/assets/textures/textures/blocks/grass.png similarity index 100% rename from android/assets/textures/blocks/grass.png rename to assets/textures/textures/blocks/grass.png diff --git a/assets/textures/textures/blocks/grass_snowed.png b/assets/textures/textures/blocks/grass_snowed.png new file mode 100644 index 0000000..730996d Binary files /dev/null and b/assets/textures/textures/blocks/grass_snowed.png differ diff --git a/android/assets/textures/blocks/gravel.png b/assets/textures/textures/blocks/gravel.png similarity index 100% rename from android/assets/textures/blocks/gravel.png rename to assets/textures/textures/blocks/gravel.png diff --git a/android/assets/textures/blocks/iron_bars.png b/assets/textures/textures/blocks/iron_bars.png similarity index 100% rename from android/assets/textures/blocks/iron_bars.png rename to assets/textures/textures/blocks/iron_bars.png diff --git a/android/assets/textures/blocks/iron_block.png b/assets/textures/textures/blocks/iron_block.png similarity index 100% rename from android/assets/textures/blocks/iron_block.png rename to assets/textures/textures/blocks/iron_block.png diff --git a/android/assets/textures/blocks/iron_ore.png b/assets/textures/textures/blocks/iron_ore.png similarity index 100% rename from android/assets/textures/blocks/iron_ore.png rename to assets/textures/textures/blocks/iron_ore.png diff --git a/android/assets/textures/blocks/ladder.png b/assets/textures/textures/blocks/ladder.png similarity index 100% rename from android/assets/textures/blocks/ladder.png rename to assets/textures/textures/blocks/ladder.png diff --git a/android/assets/textures/blocks/lapis_block.png b/assets/textures/textures/blocks/lapis_block.png similarity index 100% rename from android/assets/textures/blocks/lapis_block.png rename to assets/textures/textures/blocks/lapis_block.png diff --git a/assets/textures/textures/blocks/lapis_ore.png b/assets/textures/textures/blocks/lapis_ore.png new file mode 100644 index 0000000..44fcd7f Binary files /dev/null and b/assets/textures/textures/blocks/lapis_ore.png differ diff --git a/android/assets/textures/blocks/lava_flow.png b/assets/textures/textures/blocks/lava_flow.png similarity index 100% rename from android/assets/textures/blocks/lava_flow.png rename to assets/textures/textures/blocks/lava_flow.png diff --git a/android/assets/textures/blocks/lava_still.png b/assets/textures/textures/blocks/lava_still.png similarity index 100% rename from android/assets/textures/blocks/lava_still.png rename to assets/textures/textures/blocks/lava_still.png diff --git a/assets/textures/textures/blocks/leaves_oak.png b/assets/textures/textures/blocks/leaves_oak.png new file mode 100644 index 0000000..d902752 Binary files /dev/null and b/assets/textures/textures/blocks/leaves_oak.png differ diff --git a/assets/textures/textures/blocks/leaves_spruce.png b/assets/textures/textures/blocks/leaves_spruce.png new file mode 100644 index 0000000..0be1897 Binary files /dev/null and b/assets/textures/textures/blocks/leaves_spruce.png differ diff --git a/android/assets/textures/blocks/log_birch.png b/assets/textures/textures/blocks/log_birch.png similarity index 100% rename from android/assets/textures/blocks/log_birch.png rename to assets/textures/textures/blocks/log_birch.png diff --git a/android/assets/textures/blocks/log_oak.png b/assets/textures/textures/blocks/log_oak.png similarity index 100% rename from android/assets/textures/blocks/log_oak.png rename to assets/textures/textures/blocks/log_oak.png diff --git a/android/assets/textures/blocks/log_spruce.png b/assets/textures/textures/blocks/log_spruce.png similarity index 100% rename from android/assets/textures/blocks/log_spruce.png rename to assets/textures/textures/blocks/log_spruce.png diff --git a/android/assets/textures/blocks/mushroom_brown.png b/assets/textures/textures/blocks/mushroom_brown.png similarity index 100% rename from android/assets/textures/blocks/mushroom_brown.png rename to assets/textures/textures/blocks/mushroom_brown.png diff --git a/android/assets/textures/blocks/mushroom_red.png b/assets/textures/textures/blocks/mushroom_red.png similarity index 100% rename from android/assets/textures/blocks/mushroom_red.png rename to assets/textures/textures/blocks/mushroom_red.png diff --git a/android/assets/textures/blocks/noteblock.png b/assets/textures/textures/blocks/noteblock.png similarity index 100% rename from android/assets/textures/blocks/noteblock.png rename to assets/textures/textures/blocks/noteblock.png diff --git a/android/assets/textures/blocks/obsidian.png b/assets/textures/textures/blocks/obsidian.png similarity index 100% rename from android/assets/textures/blocks/obsidian.png rename to assets/textures/textures/blocks/obsidian.png diff --git a/android/assets/textures/blocks/planks_birch.png b/assets/textures/textures/blocks/planks_birch.png similarity index 100% rename from android/assets/textures/blocks/planks_birch.png rename to assets/textures/textures/blocks/planks_birch.png diff --git a/android/assets/textures/blocks/planks_oak.png b/assets/textures/textures/blocks/planks_oak.png similarity index 100% rename from android/assets/textures/blocks/planks_oak.png rename to assets/textures/textures/blocks/planks_oak.png diff --git a/android/assets/textures/blocks/planks_spruce.png b/assets/textures/textures/blocks/planks_spruce.png similarity index 100% rename from android/assets/textures/blocks/planks_spruce.png rename to assets/textures/textures/blocks/planks_spruce.png diff --git a/android/assets/textures/blocks/rose.png b/assets/textures/textures/blocks/rose.png similarity index 100% rename from android/assets/textures/blocks/rose.png rename to assets/textures/textures/blocks/rose.png diff --git a/android/assets/textures/blocks/sand.png b/assets/textures/textures/blocks/sand.png similarity index 100% rename from android/assets/textures/blocks/sand.png rename to assets/textures/textures/blocks/sand.png diff --git a/android/assets/textures/blocks/sandstone.png b/assets/textures/textures/blocks/sandstone.png similarity index 100% rename from android/assets/textures/blocks/sandstone.png rename to assets/textures/textures/blocks/sandstone.png diff --git a/android/assets/textures/blocks/sapling_birch.png b/assets/textures/textures/blocks/sapling_birch.png similarity index 100% rename from android/assets/textures/blocks/sapling_birch.png rename to assets/textures/textures/blocks/sapling_birch.png diff --git a/android/assets/textures/blocks/sapling_oak.png b/assets/textures/textures/blocks/sapling_oak.png similarity index 100% rename from android/assets/textures/blocks/sapling_oak.png rename to assets/textures/textures/blocks/sapling_oak.png diff --git a/android/assets/textures/blocks/sapling_spruce.png b/assets/textures/textures/blocks/sapling_spruce.png similarity index 100% rename from android/assets/textures/blocks/sapling_spruce.png rename to assets/textures/textures/blocks/sapling_spruce.png diff --git a/assets/textures/textures/blocks/snow.png b/assets/textures/textures/blocks/snow.png new file mode 100644 index 0000000..20a836e Binary files /dev/null and b/assets/textures/textures/blocks/snow.png differ diff --git a/android/assets/textures/blocks/sponge.png b/assets/textures/textures/blocks/sponge.png similarity index 100% rename from android/assets/textures/blocks/sponge.png rename to assets/textures/textures/blocks/sponge.png diff --git a/android/assets/textures/blocks/sponge_wet.png b/assets/textures/textures/blocks/sponge_wet.png similarity index 100% rename from android/assets/textures/blocks/sponge_wet.png rename to assets/textures/textures/blocks/sponge_wet.png diff --git a/android/assets/textures/blocks/stone.png b/assets/textures/textures/blocks/stone.png similarity index 100% rename from android/assets/textures/blocks/stone.png rename to assets/textures/textures/blocks/stone.png diff --git a/android/assets/textures/blocks/stone_slab.png b/assets/textures/textures/blocks/stone_slab.png similarity index 100% rename from android/assets/textures/blocks/stone_slab.png rename to assets/textures/textures/blocks/stone_slab.png diff --git a/android/assets/textures/blocks/stonebrick.png b/assets/textures/textures/blocks/stonebrick.png similarity index 100% rename from android/assets/textures/blocks/stonebrick.png rename to assets/textures/textures/blocks/stonebrick.png diff --git a/android/assets/textures/blocks/tallgrass.png b/assets/textures/textures/blocks/tallgrass.png similarity index 100% rename from android/assets/textures/blocks/tallgrass.png rename to assets/textures/textures/blocks/tallgrass.png diff --git a/android/assets/textures/blocks/water_flow.png b/assets/textures/textures/blocks/water_flow.png similarity index 100% rename from android/assets/textures/blocks/water_flow.png rename to assets/textures/textures/blocks/water_flow.png diff --git a/android/assets/textures/blocks/water_still.png b/assets/textures/textures/blocks/water_still.png similarity index 100% rename from android/assets/textures/blocks/water_still.png rename to assets/textures/textures/blocks/water_still.png diff --git a/android/assets/textures/blocks/web.png b/assets/textures/textures/blocks/web.png similarity index 100% rename from android/assets/textures/blocks/web.png rename to assets/textures/textures/blocks/web.png diff --git a/android/assets/textures/blocks/wool_colored_black.png b/assets/textures/textures/blocks/wool_colored_black.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_black.png rename to assets/textures/textures/blocks/wool_colored_black.png diff --git a/android/assets/textures/blocks/wool_colored_blue.png b/assets/textures/textures/blocks/wool_colored_blue.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_blue.png rename to assets/textures/textures/blocks/wool_colored_blue.png diff --git a/android/assets/textures/blocks/wool_colored_brown.png b/assets/textures/textures/blocks/wool_colored_brown.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_brown.png rename to assets/textures/textures/blocks/wool_colored_brown.png diff --git a/android/assets/textures/blocks/wool_colored_cyan.png b/assets/textures/textures/blocks/wool_colored_cyan.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_cyan.png rename to assets/textures/textures/blocks/wool_colored_cyan.png diff --git a/android/assets/textures/blocks/wool_colored_gray.png b/assets/textures/textures/blocks/wool_colored_gray.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_gray.png rename to assets/textures/textures/blocks/wool_colored_gray.png diff --git a/android/assets/textures/blocks/wool_colored_green.png b/assets/textures/textures/blocks/wool_colored_green.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_green.png rename to assets/textures/textures/blocks/wool_colored_green.png diff --git a/android/assets/textures/blocks/wool_colored_light_blue.png b/assets/textures/textures/blocks/wool_colored_light_blue.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_light_blue.png rename to assets/textures/textures/blocks/wool_colored_light_blue.png diff --git a/android/assets/textures/blocks/wool_colored_lime.png b/assets/textures/textures/blocks/wool_colored_lime.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_lime.png rename to assets/textures/textures/blocks/wool_colored_lime.png diff --git a/android/assets/textures/blocks/wool_colored_magenta.png b/assets/textures/textures/blocks/wool_colored_magenta.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_magenta.png rename to assets/textures/textures/blocks/wool_colored_magenta.png diff --git a/android/assets/textures/blocks/wool_colored_orange.png b/assets/textures/textures/blocks/wool_colored_orange.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_orange.png rename to assets/textures/textures/blocks/wool_colored_orange.png diff --git a/android/assets/textures/blocks/wool_colored_pink.png b/assets/textures/textures/blocks/wool_colored_pink.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_pink.png rename to assets/textures/textures/blocks/wool_colored_pink.png diff --git a/android/assets/textures/blocks/wool_colored_purple.png b/assets/textures/textures/blocks/wool_colored_purple.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_purple.png rename to assets/textures/textures/blocks/wool_colored_purple.png diff --git a/android/assets/textures/blocks/wool_colored_red.png b/assets/textures/textures/blocks/wool_colored_red.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_red.png rename to assets/textures/textures/blocks/wool_colored_red.png diff --git a/android/assets/textures/blocks/wool_colored_silver.png b/assets/textures/textures/blocks/wool_colored_silver.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_silver.png rename to assets/textures/textures/blocks/wool_colored_silver.png diff --git a/android/assets/textures/blocks/wool_colored_white.png b/assets/textures/textures/blocks/wool_colored_white.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_white.png rename to assets/textures/textures/blocks/wool_colored_white.png diff --git a/android/assets/textures/blocks/wool_colored_yellow.png b/assets/textures/textures/blocks/wool_colored_yellow.png similarity index 100% rename from android/assets/textures/blocks/wool_colored_yellow.png rename to assets/textures/textures/blocks/wool_colored_yellow.png diff --git a/assets/textures/textures/items/bed.png b/assets/textures/textures/items/bed.png new file mode 100644 index 0000000..0e1565d Binary files /dev/null and b/assets/textures/textures/items/bed.png differ diff --git a/android/assets/textures/items/bucket_empty.png b/assets/textures/textures/items/bucket_empty.png similarity index 100% rename from android/assets/textures/items/bucket_empty.png rename to assets/textures/textures/items/bucket_empty.png diff --git a/android/assets/textures/items/bucket_lava.png b/assets/textures/textures/items/bucket_lava.png similarity index 100% rename from android/assets/textures/items/bucket_lava.png rename to assets/textures/textures/items/bucket_lava.png diff --git a/android/assets/textures/items/bucket_milk.png b/assets/textures/textures/items/bucket_milk.png similarity index 100% rename from android/assets/textures/items/bucket_milk.png rename to assets/textures/textures/items/bucket_milk.png diff --git a/android/assets/textures/items/bucket_water.png b/assets/textures/textures/items/bucket_water.png similarity index 100% rename from android/assets/textures/items/bucket_water.png rename to assets/textures/textures/items/bucket_water.png diff --git a/assets/textures/textures/items/charcoal.png b/assets/textures/textures/items/charcoal.png new file mode 100644 index 0000000..9fe49a7 Binary files /dev/null and b/assets/textures/textures/items/charcoal.png differ diff --git a/assets/textures/textures/items/coal.png b/assets/textures/textures/items/coal.png new file mode 100644 index 0000000..c94ebad Binary files /dev/null and b/assets/textures/textures/items/coal.png differ diff --git a/assets/textures/textures/items/diamond.png b/assets/textures/textures/items/diamond.png new file mode 100644 index 0000000..eff11e0 Binary files /dev/null and b/assets/textures/textures/items/diamond.png differ diff --git a/assets/textures/textures/items/diamond_axe.png b/assets/textures/textures/items/diamond_axe.png new file mode 100644 index 0000000..ac81a23 Binary files /dev/null and b/assets/textures/textures/items/diamond_axe.png differ diff --git a/assets/textures/textures/items/diamond_hoe.png b/assets/textures/textures/items/diamond_hoe.png new file mode 100644 index 0000000..f0ad2c6 Binary files /dev/null and b/assets/textures/textures/items/diamond_hoe.png differ diff --git a/assets/textures/textures/items/diamond_pickaxe.png b/assets/textures/textures/items/diamond_pickaxe.png new file mode 100644 index 0000000..75823f6 Binary files /dev/null and b/assets/textures/textures/items/diamond_pickaxe.png differ diff --git a/android/assets/textures/items/diamond_shovel.png b/assets/textures/textures/items/diamond_shovel.png similarity index 100% rename from android/assets/textures/items/diamond_shovel.png rename to assets/textures/textures/items/diamond_shovel.png diff --git a/android/assets/textures/items/diamond_sword.png b/assets/textures/textures/items/diamond_sword.png similarity index 100% rename from android/assets/textures/items/diamond_sword.png rename to assets/textures/textures/items/diamond_sword.png diff --git a/assets/textures/textures/items/gold_axe.png b/assets/textures/textures/items/gold_axe.png new file mode 100644 index 0000000..d939170 Binary files /dev/null and b/assets/textures/textures/items/gold_axe.png differ diff --git a/assets/textures/textures/items/gold_hoe.png b/assets/textures/textures/items/gold_hoe.png new file mode 100644 index 0000000..da32a5d Binary files /dev/null and b/assets/textures/textures/items/gold_hoe.png differ diff --git a/assets/textures/textures/items/gold_ingot.png b/assets/textures/textures/items/gold_ingot.png new file mode 100644 index 0000000..b159455 Binary files /dev/null and b/assets/textures/textures/items/gold_ingot.png differ diff --git a/assets/textures/textures/items/gold_pickaxe.png b/assets/textures/textures/items/gold_pickaxe.png new file mode 100644 index 0000000..5b4e6d6 Binary files /dev/null and b/assets/textures/textures/items/gold_pickaxe.png differ diff --git a/android/assets/textures/items/gold_shovel.png b/assets/textures/textures/items/gold_shovel.png similarity index 100% rename from android/assets/textures/items/gold_shovel.png rename to assets/textures/textures/items/gold_shovel.png diff --git a/android/assets/textures/items/gold_sword.png b/assets/textures/textures/items/gold_sword.png similarity index 100% rename from android/assets/textures/items/gold_sword.png rename to assets/textures/textures/items/gold_sword.png diff --git a/assets/textures/textures/items/iron_axe.png b/assets/textures/textures/items/iron_axe.png new file mode 100644 index 0000000..52113da Binary files /dev/null and b/assets/textures/textures/items/iron_axe.png differ diff --git a/assets/textures/textures/items/iron_hoe.png b/assets/textures/textures/items/iron_hoe.png new file mode 100644 index 0000000..d56485d Binary files /dev/null and b/assets/textures/textures/items/iron_hoe.png differ diff --git a/assets/textures/textures/items/iron_ingot.png b/assets/textures/textures/items/iron_ingot.png new file mode 100644 index 0000000..1ff5c69 Binary files /dev/null and b/assets/textures/textures/items/iron_ingot.png differ diff --git a/assets/textures/textures/items/iron_pickaxe.png b/assets/textures/textures/items/iron_pickaxe.png new file mode 100644 index 0000000..0746cc0 Binary files /dev/null and b/assets/textures/textures/items/iron_pickaxe.png differ diff --git a/android/assets/textures/items/iron_shovel.png b/assets/textures/textures/items/iron_shovel.png similarity index 100% rename from android/assets/textures/items/iron_shovel.png rename to assets/textures/textures/items/iron_shovel.png diff --git a/android/assets/textures/items/iron_sword.png b/assets/textures/textures/items/iron_sword.png similarity index 100% rename from android/assets/textures/items/iron_sword.png rename to assets/textures/textures/items/iron_sword.png diff --git a/assets/textures/textures/items/lapis_lazuli.png b/assets/textures/textures/items/lapis_lazuli.png new file mode 100644 index 0000000..84bdcd4 Binary files /dev/null and b/assets/textures/textures/items/lapis_lazuli.png differ diff --git a/assets/textures/textures/items/porkchop_cooked.png b/assets/textures/textures/items/porkchop_cooked.png new file mode 100644 index 0000000..dbd02c5 Binary files /dev/null and b/assets/textures/textures/items/porkchop_cooked.png differ diff --git a/assets/textures/textures/items/porkchop_raw.png b/assets/textures/textures/items/porkchop_raw.png new file mode 100644 index 0000000..6c83dd0 Binary files /dev/null and b/assets/textures/textures/items/porkchop_raw.png differ diff --git a/assets/textures/textures/items/shears.png b/assets/textures/textures/items/shears.png new file mode 100644 index 0000000..bfa815c Binary files /dev/null and b/assets/textures/textures/items/shears.png differ diff --git a/assets/textures/textures/items/snowball.png b/assets/textures/textures/items/snowball.png new file mode 100644 index 0000000..56a11e9 Binary files /dev/null and b/assets/textures/textures/items/snowball.png differ diff --git a/assets/textures/textures/items/spawn_egg.png b/assets/textures/textures/items/spawn_egg.png new file mode 100644 index 0000000..8733a83 Binary files /dev/null and b/assets/textures/textures/items/spawn_egg.png differ diff --git a/assets/textures/textures/items/stick.png b/assets/textures/textures/items/stick.png new file mode 100644 index 0000000..81c915e Binary files /dev/null and b/assets/textures/textures/items/stick.png differ diff --git a/assets/textures/textures/items/stone_axe.png b/assets/textures/textures/items/stone_axe.png new file mode 100644 index 0000000..5f42de6 Binary files /dev/null and b/assets/textures/textures/items/stone_axe.png differ diff --git a/assets/textures/textures/items/stone_hoe.png b/assets/textures/textures/items/stone_hoe.png new file mode 100644 index 0000000..2118229 Binary files /dev/null and b/assets/textures/textures/items/stone_hoe.png differ diff --git a/assets/textures/textures/items/stone_pickaxe.png b/assets/textures/textures/items/stone_pickaxe.png new file mode 100644 index 0000000..13bdd75 Binary files /dev/null and b/assets/textures/textures/items/stone_pickaxe.png differ diff --git a/android/assets/textures/items/stone_shovel.png b/assets/textures/textures/items/stone_shovel.png similarity index 100% rename from android/assets/textures/items/stone_shovel.png rename to assets/textures/textures/items/stone_shovel.png diff --git a/android/assets/textures/items/stone_sword.png b/assets/textures/textures/items/stone_sword.png similarity index 100% rename from android/assets/textures/items/stone_sword.png rename to assets/textures/textures/items/stone_sword.png diff --git a/assets/textures/textures/items/wood_axe.png b/assets/textures/textures/items/wood_axe.png new file mode 100644 index 0000000..b0cbde6 Binary files /dev/null and b/assets/textures/textures/items/wood_axe.png differ diff --git a/assets/textures/textures/items/wood_hoe.png b/assets/textures/textures/items/wood_hoe.png new file mode 100644 index 0000000..3744c1b Binary files /dev/null and b/assets/textures/textures/items/wood_hoe.png differ diff --git a/assets/textures/textures/items/wood_pickaxe.png b/assets/textures/textures/items/wood_pickaxe.png new file mode 100644 index 0000000..5932623 Binary files /dev/null and b/assets/textures/textures/items/wood_pickaxe.png differ diff --git a/android/assets/textures/items/wood_shovel.png b/assets/textures/textures/items/wood_shovel.png similarity index 100% rename from android/assets/textures/items/wood_shovel.png rename to assets/textures/textures/items/wood_shovel.png diff --git a/android/assets/textures/items/wood_sword.png b/assets/textures/textures/items/wood_sword.png similarity index 100% rename from android/assets/textures/items/wood_sword.png rename to assets/textures/textures/items/wood_sword.png diff --git a/assets/touch_gui.png b/assets/touch_gui.png new file mode 100644 index 0000000..57dffc4 Binary files /dev/null and b/assets/touch_gui.png differ diff --git a/build.gradle b/build.gradle deleted file mode 100644 index ed846ef..0000000 --- a/build.gradle +++ /dev/null @@ -1,78 +0,0 @@ -buildscript { - - 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" - } -} - -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' - } - - repositories { - mavenLocal() - mavenCentral() - jcenter() - google() - maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } - maven { url "https://oss.sonatype.org/content/repositories/releases/" } - } -} - -project(":desktop") { - apply plugin: "java-library" - - dependencies { - implementation project(":core") - api "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion" - api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" - } -} - -project(":android") { - apply plugin: "android" - - configurations { natives } - - dependencies { - implementation project(":core") - 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" - } -} - -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' - } -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..fe87cf5 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,41 @@ +plugins { + ktlintGradle +} + +buildscript { + repositories { + mavenLocal() + mavenCentral() + google() + maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } + maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } + maven { url = uri("https://jitpack.io") } + maven { url = uri("https://mvn.fredboy.ru/releases/") } + } + + dependencies { + classpath(Dependencies.androidGradlePlugin) + classpath(Dependencies.Kotlin.gradlePlugin) + classpath(Dependencies.RoboVM.gradlePlugin) + } +} + +allprojects { + version = ApplicationInfo.versionName + + apply(plugin = "org.jlleitschuh.gradle.ktlint") + + ktlint { + version.set("1.6.0") + } + + repositories { + mavenLocal() + mavenCentral() + google() + maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } + maven { url = uri("https://oss.sonatype.org/content/repositories/releases/") } + maven { url = uri("https://jitpack.io") } + maven { url = uri("https://mvn.fredboy.ru/releases/") } + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..6806cd1 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + `kotlin-dsl` + id("org.jlleitschuh.gradle.ktlint") version "12.3.0" +} + +repositories { + mavenCentral() +} + +ktlint { + version.set("1.6.0") +} diff --git a/buildSrc/src/main/kotlin/ApplicationInfo.kt b/buildSrc/src/main/kotlin/ApplicationInfo.kt new file mode 100644 index 0000000..9e03e59 --- /dev/null +++ b/buildSrc/src/main/kotlin/ApplicationInfo.kt @@ -0,0 +1,11 @@ +import org.gradle.api.JavaVersion + +object ApplicationInfo { + const val name = "CaveDroid" + const val versionName = "alpha0.9.4" + const val versionCode = 27 + + const val packageName = "ru.deadsoftware.cavedroid" + + val sourceCompatibility = JavaVersion.VERSION_17 +} diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt new file mode 100644 index 0000000..6710a89 --- /dev/null +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -0,0 +1,57 @@ +object Dependencies { + + object LibGDX { + const val gdx = "com.badlogicgames.gdx:gdx:${Versions.gdx}" + + object Android { + const val backend = "com.badlogicgames.gdx:gdx-backend-android:${Versions.gdx}" + + object Natives { + const val armeabi = "com.badlogicgames.gdx:gdx-platform:${Versions.gdx}:natives-armeabi-v7a" + const val arm64 = "com.badlogicgames.gdx:gdx-platform:${Versions.gdx}:natives-arm64-v8a" + const val x86 = "com.badlogicgames.gdx:gdx-platform:${Versions.gdx}:natives-x86" + const val x86_64 = "com.badlogicgames.gdx:gdx-platform:${Versions.gdx}:natives-x86_64" + } + } + + object Desktop { + const val backend = "com.badlogicgames.gdx:gdx-backend-lwjgl3:${Versions.gdx}" + const val natives = "com.badlogicgames.gdx:gdx-platform:${Versions.gdx}:natives-desktop" + } + + object Ios { + const val backend = "com.badlogicgames.gdx:gdx-backend-robovm:${Versions.gdx}" + const val natives = "com.badlogicgames.gdx:gdx-platform:${Versions.gdx}:natives-ios" + } + } + + object Dagger { + const val dagger = "com.google.dagger:dagger:${Versions.dagger}" + const val compiler = "com.google.dagger:dagger-compiler:${Versions.dagger}" + } + + object Kotlin { + const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}" + const val kspPlugin = "com.google.devtools.ksp:${Versions.ksp}" + const val bom = "org.jetbrains.kotlin:kotlin-bom:${Versions.kotlin}" + const val stdlib = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}" + + object Serialization { + const val json = "org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.kotlinxSerialization}" + const val protobuf = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf:${Versions.kotlinxSerialization}" + } + } + + object Automultibind { + const val annotations = "ru.fredboy:automultibind-annotations:${Versions.automultibind}" + const val ksp = "ru.fredboy:automultibind-ksp:${Versions.automultibind}" + } + + object RoboVM { + const val rt = "com.mobidevelop.robovm:robovm-rt:${Versions.roboVM}" + const val cocoatouch = "com.mobidevelop.robovm:robovm-cocoatouch:${Versions.roboVM}" + const val gradlePlugin = "com.mobidevelop.robovm:robovm-gradle-plugin:${Versions.roboVM}" + } + + const val androidGradlePlugin = "com.android.tools.build:gradle:${Versions.agp}" +} diff --git a/buildSrc/src/main/kotlin/DependencyHandlerExtentions.kt b/buildSrc/src/main/kotlin/DependencyHandlerExtentions.kt new file mode 100644 index 0000000..9a3c3bb --- /dev/null +++ b/buildSrc/src/main/kotlin/DependencyHandlerExtentions.kt @@ -0,0 +1,77 @@ +import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.kotlin.dsl.project + +private fun DependencyHandler.implementation(dependency: String) = add("implementation", dependency) + +private fun DependencyHandler.ksp(dependency: String) = add("ksp", dependency) + +fun DependencyHandler.useModule(moduleName: String) { + add("implementation", project(moduleName)) +} + +fun DependencyHandler.useCommonModule() { + useModule(":core:common") +} + +fun DependencyHandler.useDataModules() { + useModule(":core:data:assets") + useModule(":core:data:configuration") + useModule(":core:data:items") + useModule(":core:data:menu") + useModule(":core:data:save") +} + +fun DependencyHandler.useDomainModules() { + useModule(":core:domain:assets") + useModule(":core:domain:configuration") + useModule(":core:domain:items") + useModule(":core:domain:save") + useModule(":core:domain:menu") + useModule(":core:domain:world") +} + +fun DependencyHandler.useEntityModules() { + useModule(":core:entity:container") + useModule(":core:entity:drop") + useModule(":core:entity:mob") +} + +fun DependencyHandler.useGameModules() { + useModule(":core:game:controller:container") + useModule(":core:game:controller:drop") + useModule(":core:game:controller:mob") + useModule(":core:game:window") + useModule(":core:game:world") +} + +fun DependencyHandler.useUxModules() { + useModule(":core:ux:controls") + useModule(":core:ux:physics") + useModule(":core:ux:rendering") +} + +fun DependencyHandler.useZygoteModule() { + useModule(":core:zygote") +} + +fun DependencyHandler.useAutomultibind() { + implementation(Dependencies.Automultibind.annotations) + ksp(Dependencies.Automultibind.ksp) +} + +fun DependencyHandler.useLibgdx() { + implementation(Dependencies.LibGDX.gdx) +} + +fun DependencyHandler.useDagger() { + implementation(Dependencies.Dagger.dagger) + ksp(Dependencies.Dagger.compiler) +} + +fun DependencyHandler.useKotlinxSerializationJson() { + implementation(Dependencies.Kotlin.Serialization.json) +} + +fun DependencyHandler.useKotlinxSerializationProtobuf() { + implementation(Dependencies.Kotlin.Serialization.protobuf) +} diff --git a/buildSrc/src/main/kotlin/PluginDependencySpecScopeExtentions.kt b/buildSrc/src/main/kotlin/PluginDependencySpecScopeExtentions.kt new file mode 100644 index 0000000..b2de321 --- /dev/null +++ b/buildSrc/src/main/kotlin/PluginDependencySpecScopeExtentions.kt @@ -0,0 +1,11 @@ +import org.gradle.kotlin.dsl.PluginDependenciesSpecScope +import org.gradle.kotlin.dsl.version + +val PluginDependenciesSpecScope.kotlin + get() = id("kotlin") +val PluginDependenciesSpecScope.ksp + get() = id("com.google.devtools.ksp") version Versions.ksp +val PluginDependenciesSpecScope.kotlinxSerialization + get() = id("org.jetbrains.kotlin.plugin.serialization") version Versions.kotlin +val PluginDependenciesSpecScope.ktlintGradle + get() = id("org.jlleitschuh.gradle.ktlint") version Versions.ktlintGradle diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt new file mode 100644 index 0000000..4af3580 --- /dev/null +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -0,0 +1,77 @@ +object Versions { + + /** + * Android gradle plugin version + */ + const val agp = "8.2.2" + + /** + * LibGDX version + * + * [Source](https://github.com/libgdx/libgdx) + */ + const val gdx = "1.12.0" + + /** + * Dagger version + * + * [Source](https://github.com/google/dagger) + */ + const val dagger = "2.51.1" + + /** + * Kotlin version + * + * [Source](https://github.com/JetBrains/kotlin) + */ + const val kotlin = "1.9.24" + + /** + * Kotlinx serialization version + * + * [Source](https://github.com/Kotlin/kotlinx.serialization/) + */ + const val kotlinxSerialization = "1.6.3" + + /** + * Kotlin Symbol Processing version + * + * [Source](https://github.com/google/ksp) + */ + const val ksp = "1.9.24-1.0.20" + + /** + * Kotlin poet version + * + * [Source](https://github.com/square/kotlinpoet) + */ + const val kotlinPoetKsp = "1.16.0" + + /** + * JetBrains annotations version + * + * [Source](https://github.com/JetBrains/java-annotations) + */ + const val jetbrainsAnnotations = "23.1.0" + + /** + * Automultibind version + * + * [Source](https://github.com/fredboy/automultibind) + */ + const val automultibind = "1.0.0" + + /** + * Ktlint gradle plugin + * + * [Source](https://github.com/JLLeitschuh/ktlint-gradle) + */ + const val ktlintGradle = "12.3.0" + + /** + * RoboVM is an ahead-of-time compiler for Java bytecode, targeting Linux, Mac OS X and iOS. + * + * [Source](https://github.com/MobiVM/robovm) + */ + const val roboVM = "2.3.23" +} diff --git a/core/build.gradle b/core/build.gradle deleted file mode 100644 index f17b102..0000000 --- a/core/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.jvm" - id "java" - id "idea" - id "net.ltgt.apt" version "0.21" -} - -sourceCompatibility = 1.8 - -[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' - -sourceSets.main.java.srcDirs = [ "src/" ] \ No newline at end of file diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts new file mode 100644 index 0000000..114a577 --- /dev/null +++ b/core/common/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() +} diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/CaveDroidConstants.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/CaveDroidConstants.kt new file mode 100644 index 0000000..1eb2600 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/CaveDroidConstants.kt @@ -0,0 +1,6 @@ +package ru.fredboy.cavedroid.common + +object CaveDroidConstants { + + const val VERSION = "alpha 0.9.4" +} diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/api/ApplicationController.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/api/ApplicationController.kt new file mode 100644 index 0000000..440f471 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/api/ApplicationController.kt @@ -0,0 +1,16 @@ +package ru.fredboy.cavedroid.common.api + +interface ApplicationController { + + fun quitGame() + + fun newGameCreative() + + fun newGameSurvival() + + fun loadGame() + + fun exitGame() + + fun triggerResize() +} diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/api/PreferencesStore.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/api/PreferencesStore.kt new file mode 100644 index 0000000..38921fc --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/api/PreferencesStore.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.common.api + +interface PreferencesStore { + + fun getPreference(key: String): String? + + fun setPreference(key: String, value: String?) +} diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/automultibind/MultibindingConfig.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/automultibind/MultibindingConfig.kt new file mode 100644 index 0000000..562b680 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/automultibind/MultibindingConfig.kt @@ -0,0 +1,5 @@ +package ru.fredboy.cavedroid.common.automultibind + +data object MultibindingConfig { + const val GENERATED_MODULES_PACKAGE = "ru.deadsoftware.cavedroid.generated.module" +} diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/base/MainConfiguration.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/base/MainConfiguration.kt new file mode 100644 index 0000000..858c2c9 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/base/MainConfiguration.kt @@ -0,0 +1,3 @@ +package ru.fredboy.cavedroid.common.base + +interface MainConfiguration diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/di/GameScope.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/di/GameScope.kt new file mode 100644 index 0000000..d97d8bf --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/di/GameScope.kt @@ -0,0 +1,7 @@ +package ru.fredboy.cavedroid.common.di + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.RUNTIME) +annotation class GameScope diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/di/MenuScope.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/di/MenuScope.kt new file mode 100644 index 0000000..52de4fe --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/di/MenuScope.kt @@ -0,0 +1,7 @@ +package ru.fredboy.cavedroid.common.di + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.RUNTIME) +annotation class MenuScope diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/model/Joystick.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/model/Joystick.kt new file mode 100644 index 0000000..3f88963 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/model/Joystick.kt @@ -0,0 +1,72 @@ +package ru.fredboy.cavedroid.common.model + +import com.badlogic.gdx.math.Vector2 +import com.badlogic.gdx.utils.TimeUtils + +class Joystick( + private val value: Float, +) { + + var active = false + private set + var centerX = 0f + private set + var centerY = 0f + private set + + var activeX = 0f + private set + var activeY = 0f + private set + + var pointer = 0 + private set + + private val stickVector = Vector2() + + private var activateTimeMs = 0L + + fun activate(touchX: Float, touchY: Float, pointer: Int) { + active = true + centerX = touchX + centerY = touchY + activateTimeMs = TimeUtils.millis() + this.pointer = pointer + } + + fun deactivate() { + active = false + } + + fun getVelocityVector(): Vector2 { + if (!active) { + return Vector2.Zero + } + return Vector2( + stickVector.x * value, + stickVector.y * value, + ) + } + + fun updateState(touchX: Float, touchY: Float) { + if (!active) { + return + } + + stickVector.x = touchX - centerX + stickVector.y = touchY - centerY + stickVector.clamp(0f, RADIUS) + + activeX = centerX + stickVector.x + activeY = centerY + stickVector.y + + stickVector.x /= RADIUS + stickVector.y /= RADIUS + } + + companion object { + const val RADIUS = 48f + const val SIZE = RADIUS * 2 + const val STICK_SIZE = 32f + } +} diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/model/SpriteOrigin.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/model/SpriteOrigin.kt new file mode 100644 index 0000000..e393fd8 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/model/SpriteOrigin.kt @@ -0,0 +1,28 @@ +package ru.fredboy.cavedroid.common.model + +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 = 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/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/CollectionsExtensions.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/CollectionsExtensions.kt new file mode 100644 index 0000000..2aceb25 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/CollectionsExtensions.kt @@ -0,0 +1,16 @@ +package ru.fredboy.cavedroid.common.utils + +fun MutableCollection.retrieveFirst(predicate: (T) -> Boolean): T? { + val iterator = this.iterator() + while (iterator.hasNext()) { + val element = iterator.next() + if (predicate.invoke(element)) { + iterator.remove() + return element + } + } + + return null +} + +fun MutableCollection.removeFirst(predicate: (T) -> Boolean): Boolean = retrieveFirst(predicate) != null diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/GdxExtensions.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/GdxExtensions.kt new file mode 100644 index 0000000..3701a62 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/GdxExtensions.kt @@ -0,0 +1,5 @@ +package ru.fredboy.cavedroid.common.utils + +import com.badlogic.gdx.Graphics + +val Graphics.ratio get() = width.toFloat() / height.toFloat() diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/MeasureUnitsUtils.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/MeasureUnitsUtils.kt new file mode 100644 index 0000000..490ca96 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/MeasureUnitsUtils.kt @@ -0,0 +1,25 @@ +package ru.fredboy.cavedroid.common.utils + +import com.badlogic.gdx.math.MathUtils + +const val BLOCK_SIZE_PX = 16f + +/** + * Converts this value in BLOCKS into pixels + */ +val Float.px get() = this * BLOCK_SIZE_PX + +/** + * Converts this value in BLOCKS into pixels + */ +val Int.px get() = this * BLOCK_SIZE_PX + +/** + * Converts this value in PIXELS into blocks + */ +val Float.bl get() = MathUtils.floor(this / BLOCK_SIZE_PX) + +/** + * Converts this value in PIXELS into blocks + */ +val Int.bl get() = MathUtils.floor(this / BLOCK_SIZE_PX) diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/RenderingUtils.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/RenderingUtils.kt new file mode 100644 index 0000000..3631fc5 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/RenderingUtils.kt @@ -0,0 +1,105 @@ +package ru.fredboy.cavedroid.common.utils + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.GL20 +import com.badlogic.gdx.graphics.g2d.BitmapFont +import com.badlogic.gdx.graphics.g2d.GlyphLayout +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.math.Rectangle + +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( + font: BitmapFont, + str: String, + x: Float, + y: Float, + color: Color = Color.WHITE, +): GlyphLayout { + font.color = color + return font.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) +} + +fun SpriteBatch.withScissors( + viewportWidth: Float, + viewportHeight: Float, + x: Float, + y: Float, + width: Float, + height: Float, + block: () -> Unit, +) { + val scaleX = Gdx.graphics.width / viewportWidth + val scaleY = Gdx.graphics.height / viewportHeight + + flush() + Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST) + Gdx.gl.glScissor( + /* x = */ (x * scaleX).toInt(), + /* y = */ ((viewportHeight - y - height) * scaleY).toInt(), + /* width = */ (width * scaleX).toInt(), + /* height = */ (height * scaleY).toInt(), + ) + block.invoke() + flush() + Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST) +} + +fun Int.blockCenterPx() = (toFloat() + .5f).px diff --git a/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/SpriteUtils.kt b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/SpriteUtils.kt new file mode 100644 index 0000000..91052d2 --- /dev/null +++ b/core/common/src/main/kotlin/ru/fredboy/cavedroid/common/utils/SpriteUtils.kt @@ -0,0 +1,37 @@ +package ru.fredboy.cavedroid.common.utils + +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.Sprite +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import ru.fredboy.cavedroid.common.model.SpriteOrigin + +/** + * 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(), + tint: Color? = null, +) { + val oldColor = sprite.color + + sprite.setPosition(x, y) + sprite.setSize(width, height) + sprite.rotation = rotation + tint?.let(sprite::setColor) + + sprite.draw(this) + + sprite.setSize(sprite.regionWidth.toFloat(), sprite.regionHeight.toFloat()) + sprite.rotation = 0f + sprite.color = oldColor +} + +fun Sprite.applyOrigin(origin: SpriteOrigin) { + origin.applyToSprite(this) +} diff --git a/core/data/assets/build.gradle.kts b/core/data/assets/build.gradle.kts new file mode 100644 index 0000000..8ab6f7c --- /dev/null +++ b/core/data/assets/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin + ksp + kotlinxSerialization +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useModule(":core:domain:assets") + useLibgdx() + useKotlinxSerializationJson() + useDagger() +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/di/DataAssetsModule.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/di/DataAssetsModule.kt new file mode 100644 index 0000000..de7564c --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/di/DataAssetsModule.kt @@ -0,0 +1,57 @@ +package ru.fredboy.cavedroid.data.assets.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.data.assets.internal.BlockAssetsRepositoryImpl +import ru.fredboy.cavedroid.data.assets.internal.BlockDamageAssetsRepositoryImpl +import ru.fredboy.cavedroid.data.assets.internal.FontAssetsRepositoryImpl +import ru.fredboy.cavedroid.data.assets.internal.ItemsAssetsRepositoryImpl +import ru.fredboy.cavedroid.data.assets.internal.MobAssetsRepositoryImpl +import ru.fredboy.cavedroid.data.assets.internal.TextureRegionsAssetsRepositoryImpl +import ru.fredboy.cavedroid.data.assets.internal.TouchButtonsRepositoryImpl +import ru.fredboy.cavedroid.domain.assets.repository.BlockAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.BlockDamageAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.FontAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.ItemsAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.MobAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.TextureRegionsAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.TouchButtonsAssetsRepository + +@Module +abstract class DataAssetsModule { + + @Binds + internal abstract fun bindBlockDamageAssetsRepository( + impl: BlockDamageAssetsRepositoryImpl, + ): BlockDamageAssetsRepository + + @Binds + internal abstract fun bindMobAssetsRepository( + impl: MobAssetsRepositoryImpl, + ): MobAssetsRepository + + @Binds + internal abstract fun bindTextureRegionsAssetsRepository( + impl: TextureRegionsAssetsRepositoryImpl, + ): TextureRegionsAssetsRepository + + @Binds + internal abstract fun bindFontAssetsRepository( + impl: FontAssetsRepositoryImpl, + ): FontAssetsRepository + + @Binds + internal abstract fun bindBlockAssetsRepository( + impl: BlockAssetsRepositoryImpl, + ): BlockAssetsRepository + + @Binds + internal abstract fun bindItemsAssetsRepository( + impl: ItemsAssetsRepositoryImpl, + ): ItemsAssetsRepository + + @Binds + internal abstract fun bindTouchButtonsAssetsRepository( + impl: TouchButtonsRepositoryImpl, + ): TouchButtonsAssetsRepository +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/BlockAssetsRepositoryImpl.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/BlockAssetsRepositoryImpl.kt new file mode 100644 index 0000000..41ed4c5 --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/BlockAssetsRepositoryImpl.kt @@ -0,0 +1,27 @@ +package ru.fredboy.cavedroid.data.assets.internal + +import com.badlogic.gdx.graphics.Texture +import ru.fredboy.cavedroid.domain.assets.repository.BlockAssetsRepository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class BlockAssetsRepositoryImpl @Inject constructor() : BlockAssetsRepository() { + + private val blockTexturesCache = HashMap() + + override fun getBlockTexture(textureName: String): Texture = resolveTexture(textureName, BLOCKS_TEXTURES_PATH, blockTexturesCache) + + override fun initialize() { + // no-op + } + + override fun dispose() { + super.dispose() + blockTexturesCache.clear() + } + + companion object { + private const val BLOCKS_TEXTURES_PATH = "textures/textures/blocks" + } +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/BlockDamageAssetsRepositoryImpl.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/BlockDamageAssetsRepositoryImpl.kt new file mode 100644 index 0000000..b30dc61 --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/BlockDamageAssetsRepositoryImpl.kt @@ -0,0 +1,49 @@ +package ru.fredboy.cavedroid.data.assets.internal + +import com.badlogic.gdx.graphics.g2d.Sprite +import ru.fredboy.cavedroid.common.utils.BLOCK_SIZE_PX +import ru.fredboy.cavedroid.common.utils.bl +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.assets.repository.BlockDamageAssetsRepository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class BlockDamageAssetsRepositoryImpl @Inject constructor() : BlockDamageAssetsRepository() { + + private var blockDamageSprites: Array? = null + + override val damageStages: Int + get() = requireNotNull(blockDamageSprites).size + + private fun loadBlockDamage() { + val blockDamageTexture = loadTexture(BLOCK_DAMAGE_SHEET_PATH) + val size = blockDamageTexture.width.bl + val blockSize = BLOCK_SIZE_PX.toInt() + + blockDamageSprites = Array(size) { index -> + flippedSprite( + texture = blockDamageTexture, + x = index.px.toInt(), + y = 0, + width = blockSize, + height = blockSize, + ) + } + } + + override fun getBlockDamageSprite(stage: Int): Sprite = requireNotNull(blockDamageSprites)[stage] + + override fun initialize() { + loadBlockDamage() + } + + override fun dispose() { + super.dispose() + blockDamageSprites = null + } + + companion object { + private const val BLOCK_DAMAGE_SHEET_PATH = "textures/break.png" + } +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/FontAssetsRepositoryImpl.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/FontAssetsRepositoryImpl.kt new file mode 100644 index 0000000..608a9cf --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/FontAssetsRepositoryImpl.kt @@ -0,0 +1,46 @@ +package ru.fredboy.cavedroid.data.assets.internal + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.g2d.BitmapFont +import com.badlogic.gdx.graphics.g2d.GlyphLayout +import ru.fredboy.cavedroid.domain.assets.repository.FontAssetsRepository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class FontAssetsRepositoryImpl @Inject constructor() : FontAssetsRepository() { + + private val glyphLayout = GlyphLayout() + + private var font: BitmapFont? = null + + override fun getStringWidth(string: String): Float { + glyphLayout.setText(font, string) + return glyphLayout.width + } + + override fun getStringHeight(string: String): Float { + glyphLayout.setText(font, string) + return glyphLayout.height + } + + override fun getFont(): BitmapFont = requireNotNull(font) + + override fun initialize() { + font = BitmapFont(Gdx.files.internal(FONT_FILE_PATH), true) + .apply { + data.setScale(.375f) + setUseIntegerPositions(false) + } + } + + override fun dispose() { + super.dispose() + font?.dispose() + } + + companion object { + private const val FONT_FILE_PATH = "font.fnt" + private const val FONT_SCALE = .375f + } +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/ItemsAssetsRepositoryImpl.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/ItemsAssetsRepositoryImpl.kt new file mode 100644 index 0000000..e23d09f --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/ItemsAssetsRepositoryImpl.kt @@ -0,0 +1,27 @@ +package ru.fredboy.cavedroid.data.assets.internal + +import com.badlogic.gdx.graphics.Texture +import ru.fredboy.cavedroid.domain.assets.repository.ItemsAssetsRepository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class ItemsAssetsRepositoryImpl @Inject constructor() : ItemsAssetsRepository() { + + private val itemTexturesCache = HashMap() + + override fun getItemTexture(textureName: String): Texture = resolveTexture(textureName, ITEMS_TEXTURES_PATH, itemTexturesCache) + + override fun initialize() { + // no-op + } + + override fun dispose() { + super.dispose() + itemTexturesCache.clear() + } + + companion object { + private const val ITEMS_TEXTURES_PATH = "textures/textures/items" + } +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/MobAssetsRepositoryImpl.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/MobAssetsRepositoryImpl.kt new file mode 100644 index 0000000..0ddc78a --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/MobAssetsRepositoryImpl.kt @@ -0,0 +1,58 @@ +package ru.fredboy.cavedroid.data.assets.internal + +import ru.fredboy.cavedroid.domain.assets.model.MobSprite +import ru.fredboy.cavedroid.domain.assets.repository.MobAssetsRepository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class MobAssetsRepositoryImpl @Inject constructor() : MobAssetsRepository() { + + private var playerSprite: MobSprite.Player? = null + + private var pigSprite: MobSprite.Pig? = null + + private fun loadPlayerSprite() { + val (headTexture, bodyTexture, handTexture, legTexture) = List(4) { index -> + loadTexture("$PLAYER_SPRITES_PATH/$index.png") + } + + playerSprite = MobSprite.Player( + head = flippedSprite(headTexture), + hand = flippedSprite(handTexture), + body = flippedSprite(bodyTexture), + leg = flippedSprite(legTexture), + ) + } + + private fun loadPigSprite() { + val (bodyTexture, legTexture) = List(2) { index -> + loadTexture("$PIG_SPRITES_PATH/$index.png") + } + + pigSprite = MobSprite.Pig( + headAndBody = flippedSprite(bodyTexture), + leg = flippedSprite(legTexture), + ) + } + + override fun getPlayerSprites(): MobSprite.Player = requireNotNull(playerSprite) + + override fun getPigSprites(): MobSprite.Pig = requireNotNull(pigSprite) + + override fun initialize() { + loadPlayerSprite() + loadPigSprite() + } + + override fun dispose() { + super.dispose() + playerSprite = null + pigSprite = null + } + + companion object { + private const val PLAYER_SPRITES_PATH = "textures/mobs/char" + private const val PIG_SPRITES_PATH = "textures/mobs/pig" + } +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/TextureRegionsAssetsRepositoryImpl.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/TextureRegionsAssetsRepositoryImpl.kt new file mode 100644 index 0000000..ae66b9e --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/TextureRegionsAssetsRepositoryImpl.kt @@ -0,0 +1,67 @@ +package ru.fredboy.cavedroid.data.assets.internal + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.g2d.TextureRegion +import kotlinx.serialization.json.Json +import ru.fredboy.cavedroid.data.assets.model.region.TextureRegionsDto +import ru.fredboy.cavedroid.domain.assets.repository.TextureRegionsAssetsRepository +import java.io.File +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class TextureRegionsAssetsRepositoryImpl @Inject constructor() : TextureRegionsAssetsRepository() { + + private var textureRegions: HashMap? = null + + private fun loadTextureRegions() { + val file = Gdx.files.internal(JSON_TEXTURE_REGIONS) + val dto = JsonFormat.decodeFromString(file.readString()) + + val regions = HashMap() + + dto.forEach { (fileName, fileData) -> + val texture = loadTexture("$fileName.png") + + if (fileData.isEmpty()) { + regions[fileName.split(File.separator).last()] = flippedRegion( + texture = texture, + x = 0, + y = 0, + width = texture.width, + height = texture.height, + ) + } else { + fileData.forEach { (regionName, regionData) -> + regions[regionName.split(File.separator).last()] = flippedRegion( + texture = texture, + x = regionData.x, + y = regionData.y, + width = regionData.width ?: texture.width, + height = regionData.height ?: texture.height, + ) + } + } + } + + textureRegions = regions + } + + override fun initialize() { + loadTextureRegions() + } + + override fun getTextureRegionByName(name: String): TextureRegion? = requireNotNull(textureRegions)[name] + + override fun dispose() { + super.dispose() + textureRegions = null + } + + companion object { + private val JsonFormat = Json { ignoreUnknownKeys = true } + + private const val JSON_TEXTURE_REGIONS = "json/texture_regions.json" + } +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/TouchButtonsRepositoryImpl.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/TouchButtonsRepositoryImpl.kt new file mode 100644 index 0000000..d7e36c5 --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/internal/TouchButtonsRepositoryImpl.kt @@ -0,0 +1,56 @@ +package ru.fredboy.cavedroid.data.assets.internal + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.Input +import com.badlogic.gdx.math.Rectangle +import kotlinx.serialization.json.Json +import ru.fredboy.cavedroid.data.assets.model.button.TouchButtonsDto +import ru.fredboy.cavedroid.domain.assets.model.TouchButton +import ru.fredboy.cavedroid.domain.assets.repository.TouchButtonsAssetsRepository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class TouchButtonsRepositoryImpl @Inject constructor() : TouchButtonsAssetsRepository() { + + private val guiMap = HashMap() + + override fun getTouchButtons(): Map = guiMap + + private fun getMouseKey(name: String): Int = when (name) { + "Left" -> Input.Buttons.LEFT + "Right" -> Input.Buttons.RIGHT + "Middle" -> Input.Buttons.MIDDLE + "Back" -> Input.Buttons.BACK + "Forward" -> Input.Buttons.FORWARD + else -> -1 + } + + private fun loadTouchButtons() { + val file = Gdx.files.internal(JSON_TOUCH_BUTTONS) + val buttons = JsonFormat.decodeFromString(file.readString()) + + buttons.forEach { (name, data) -> + guiMap[name] = TouchButton( + rectangle = Rectangle(data.x, data.y, data.width, data.height), + code = if (data.isMouse) getMouseKey(name) else Input.Keys.valueOf(data.key), + isMouse = data.isMouse, + ) + } + } + + override fun initialize() { + loadTouchButtons() + } + + override fun dispose() { + super.dispose() + guiMap.clear() + } + + companion object { + private val JsonFormat = Json { ignoreUnknownKeys = true } + + private const val JSON_TOUCH_BUTTONS = "json/touch_buttons.json" + } +} diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/button/TouchButtonDto.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/button/TouchButtonDto.kt new file mode 100644 index 0000000..c4e2e01 --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/button/TouchButtonDto.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.data.assets.model.button + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class TouchButtonDto( + @SerialName("x") val x: Float, + @SerialName("y") val y: Float, + @SerialName("w") val width: Float, + @SerialName("h") val height: Float, + @SerialName("mouse") val isMouse: Boolean = false, + @SerialName("key") val key: String, +) diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/button/TouchButtonsDto.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/button/TouchButtonsDto.kt new file mode 100644 index 0000000..6682347 --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/button/TouchButtonsDto.kt @@ -0,0 +1,3 @@ +package ru.fredboy.cavedroid.data.assets.model.button + +typealias TouchButtonsDto = Map diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/region/TextureRegionDto.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/region/TextureRegionDto.kt new file mode 100644 index 0000000..6f2d171 --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/region/TextureRegionDto.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.data.assets.model.region + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +internal data class TextureRegionDto( + @SerialName("x") val x: Int = 0, + @SerialName("y") val y: Int = 0, + @SerialName("w") val width: Int? = null, + @SerialName("h") val height: Int? = null, +) diff --git a/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/region/TextureRegionsDto.kt b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/region/TextureRegionsDto.kt new file mode 100644 index 0000000..d0bf265 --- /dev/null +++ b/core/data/assets/src/main/kotlin/ru/fredboy/cavedroid/data/assets/model/region/TextureRegionsDto.kt @@ -0,0 +1,3 @@ +package ru.fredboy.cavedroid.data.assets.model.region + +internal typealias TextureRegionsDto = Map> diff --git a/core/data/configuration/build.gradle.kts b/core/data/configuration/build.gradle.kts new file mode 100644 index 0000000..2092f36 --- /dev/null +++ b/core/data/configuration/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useDagger() + useLibgdx() + useCommonModule() + useModule(":core:domain:configuration") +} diff --git a/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/di/ApplicationContextModule.kt b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/di/ApplicationContextModule.kt new file mode 100644 index 0000000..8d83e3d --- /dev/null +++ b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/di/ApplicationContextModule.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.data.configuration.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.data.configuration.repository.ApplicationContextRepositoryImpl +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository + +@Module +abstract class ApplicationContextModule { + + @Binds + internal abstract fun bindApplicationContextRepository(impl: ApplicationContextRepositoryImpl): ApplicationContextRepository +} diff --git a/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/di/GameContextModule.kt b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/di/GameContextModule.kt new file mode 100644 index 0000000..05606ca --- /dev/null +++ b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/di/GameContextModule.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.data.configuration.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.data.configuration.repository.GameContextRepositoryImpl +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository + +@Module +abstract class GameContextModule { + + @Binds + internal abstract fun bindGameContextRepository(impl: GameContextRepositoryImpl): GameContextRepository +} diff --git a/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/model/ApplicationContext.kt b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/model/ApplicationContext.kt new file mode 100644 index 0000000..b103291 --- /dev/null +++ b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/model/ApplicationContext.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.data.configuration.model + +class ApplicationContext( + internal val isDebug: Boolean, + internal var isTouch: Boolean, + internal var isFullscreen: Boolean, + internal var useDynamicCamera: Boolean, + internal var gameDirectory: String, + internal var width: Float, + internal var height: Float, + internal var screenScale: Int, +) diff --git a/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/model/GameContext.kt b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/model/GameContext.kt new file mode 100644 index 0000000..2edef45 --- /dev/null +++ b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/model/GameContext.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.data.configuration.model + +import ru.fredboy.cavedroid.common.model.Joystick +import ru.fredboy.cavedroid.domain.configuration.model.CameraContext + +class GameContext( + internal val isLoadGame: Boolean, + internal var showInfo: Boolean, + internal var showMap: Boolean, + internal var joystick: Joystick, + internal var cameraContext: CameraContext, +) diff --git a/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/repository/ApplicationContextRepositoryImpl.kt b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/repository/ApplicationContextRepositoryImpl.kt new file mode 100644 index 0000000..d7557dc --- /dev/null +++ b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/repository/ApplicationContextRepositoryImpl.kt @@ -0,0 +1,68 @@ +package ru.fredboy.cavedroid.data.configuration.repository + +import com.badlogic.gdx.Gdx +import ru.fredboy.cavedroid.data.configuration.store.ApplicationContextStore +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ApplicationContextRepositoryImpl @Inject constructor( + private val applicationContextStore: ApplicationContextStore, +) : ApplicationContextRepository { + + override fun isDebug(): Boolean = applicationContextStore.isDebug + + override fun isTouch(): Boolean = applicationContextStore.isTouch + + override fun isFullscreen(): Boolean = applicationContextStore.isFullscreen + + override fun useDynamicCamera(): Boolean = applicationContextStore.useDynamicCamera + + override fun getGameDirectory(): String = applicationContextStore.gameDirectory + + override fun getWidth(): Float = applicationContextStore.width + + override fun getHeight(): Float = applicationContextStore.height + + override fun getScreenScale(): Int = applicationContextStore.screenScale + + override fun setTouch(isTouch: Boolean) { + applicationContextStore.isTouch = isTouch + } + + override fun setFullscreen(fullscreen: Boolean) { + if (!Gdx.graphics.supportsDisplayModeChange()) { + return + } + + if (fullscreen) { + Gdx.graphics.setFullscreenMode(Gdx.graphics.displayMode) + } else { + Gdx.graphics.setWindowedMode(960, 540) + setWidth(Gdx.graphics.width.toFloat() / 2) + setHeight(Gdx.graphics.height.toFloat() / 2) + } + applicationContextStore.isFullscreen = fullscreen + } + + override fun setUseDynamicCamera(use: Boolean) { + applicationContextStore.useDynamicCamera = use + } + + override fun setGameDirectory(path: String) { + applicationContextStore.gameDirectory = path + } + + override fun setWidth(width: Float) { + applicationContextStore.width = width + } + + override fun setHeight(height: Float) { + applicationContextStore.height = height + } + + override fun setScreenScale(scale: Int) { + applicationContextStore.screenScale = scale + } +} diff --git a/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/repository/GameContextRepositoryImpl.kt b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/repository/GameContextRepositoryImpl.kt new file mode 100644 index 0000000..bf2f949 --- /dev/null +++ b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/repository/GameContextRepositoryImpl.kt @@ -0,0 +1,40 @@ +package ru.fredboy.cavedroid.data.configuration.repository + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.model.Joystick +import ru.fredboy.cavedroid.data.configuration.store.GameContextStore +import ru.fredboy.cavedroid.domain.configuration.model.CameraContext +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import javax.inject.Inject + +@GameScope +class GameContextRepositoryImpl @Inject constructor( + private val gameContextStore: GameContextStore, +) : GameContextRepository { + + override fun isLoadGame(): Boolean = gameContextStore.isLoadGame + + override fun shouldShowInfo(): Boolean = gameContextStore.showInfo + + override fun shouldShowMap(): Boolean = gameContextStore.showMap + + override fun getJoystick(): Joystick = gameContextStore.joystick + + override fun getCameraContext(): CameraContext = gameContextStore.cameraContext + + override fun setShowInfo(show: Boolean) { + gameContextStore.showInfo = show + } + + override fun setShowMap(show: Boolean) { + gameContextStore.showMap = show + } + + override fun setJoystick(joystick: Joystick) { + gameContextStore.joystick = joystick + } + + override fun setCameraContext(cameraContext: CameraContext) { + gameContextStore.cameraContext = cameraContext + } +} diff --git a/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/store/ApplicationContextStore.kt b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/store/ApplicationContextStore.kt new file mode 100644 index 0000000..7670bb8 --- /dev/null +++ b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/store/ApplicationContextStore.kt @@ -0,0 +1,61 @@ +package ru.fredboy.cavedroid.data.configuration.store + +import ru.fredboy.cavedroid.common.api.PreferencesStore +import ru.fredboy.cavedroid.data.configuration.model.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ApplicationContextStore @Inject constructor( + private val applicationContext: ApplicationContext, + private val preferencesStore: PreferencesStore, +) { + + private val lock = Any() + + val isDebug: Boolean + get() = synchronized(lock) { applicationContext.isDebug } + + var isTouch: Boolean + get() = synchronized(lock) { applicationContext.isTouch } + set(value) = synchronized(lock) { applicationContext.isTouch = value } + + var isFullscreen: Boolean + get() = synchronized(lock) { applicationContext.isFullscreen } + set(value) = synchronized(lock) { + applicationContext.isFullscreen = value + preferencesStore.setPreference(KEY_FULLSCREEN_PREF, value.toString()) + } + + var useDynamicCamera: Boolean + get() = synchronized(lock) { applicationContext.useDynamicCamera } + set(value) = synchronized(lock) { + applicationContext.useDynamicCamera = value + preferencesStore.setPreference(KEY_DYNAMIC_CAMERA_PREF, value.toString()) + } + + var gameDirectory: String + get() = synchronized(lock) { applicationContext.gameDirectory } + set(value) = synchronized(lock) { applicationContext.gameDirectory = value } + + var width: Float + get() = synchronized(lock) { applicationContext.width } + set(value) = synchronized(lock) { applicationContext.width = value } + + var height: Float + get() = synchronized(lock) { applicationContext.height } + set(value) = synchronized(lock) { applicationContext.height = value } + + var screenScale: Int + get() = synchronized(lock) { applicationContext.screenScale } + set(value) = synchronized(lock) { + applicationContext.screenScale = value + preferencesStore.setPreference(KEY_SCREEN_SCALE_PREF, value.toString()) + } + + private companion object { + private const val KEY_FULLSCREEN_PREF = "fullscreen" + private const val KEY_DYNAMIC_CAMERA_PREF = "dyncam" + private const val KEY_SCREEN_SCALE_PREF = "screen_scale" + } +} diff --git a/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/store/GameContextStore.kt b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/store/GameContextStore.kt new file mode 100644 index 0000000..21d7638 --- /dev/null +++ b/core/data/configuration/src/main/kotlin/ru/fredboy/cavedroid/data/configuration/store/GameContextStore.kt @@ -0,0 +1,38 @@ +package ru.fredboy.cavedroid.data.configuration.store + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.model.Joystick +import ru.fredboy.cavedroid.data.configuration.model.GameContext +import ru.fredboy.cavedroid.domain.configuration.model.CameraContext +import javax.inject.Inject + +@GameScope +class GameContextStore @Inject constructor( + private val gameContext: GameContext, +) { + + private val lock = Any() + + val isLoadGame: Boolean + get() = gameContext.isLoadGame + + var showInfo: Boolean + get() = synchronized(lock) { gameContext.showInfo } + set(value) = synchronized(lock) { gameContext.showInfo = value } + + var showMap: Boolean + get() = synchronized(lock) { gameContext.showMap } + set(value) = synchronized(lock) { gameContext.showMap = value } + + var joystick: Joystick + get() = synchronized(lock) { gameContext.joystick } + set(value) = synchronized(lock) { gameContext.joystick = value } + + var cameraContext: CameraContext + get() = synchronized(lock) { gameContext.cameraContext } + set(value) = synchronized(lock) { gameContext.cameraContext = value } + + companion object { + private const val TAG = "GameConfigurationStore" + } +} diff --git a/core/data/items/build.gradle.kts b/core/data/items/build.gradle.kts new file mode 100644 index 0000000..dfd5c0f --- /dev/null +++ b/core/data/items/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin + ksp + kotlinxSerialization +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useLibgdx() + useKotlinxSerializationJson() + useDagger() + + useModule(":core:domain:assets") + useModule(":core:domain:items") +} diff --git a/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/di/DataItemsModule.kt b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/di/DataItemsModule.kt new file mode 100644 index 0000000..2af776a --- /dev/null +++ b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/di/DataItemsModule.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.data.items.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.data.items.repository.ItemsRepositoryImpl +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository + +@Module +abstract class DataItemsModule { + + @Binds + internal abstract fun bindItemsRepository(impl: ItemsRepositoryImpl): ItemsRepository +} diff --git a/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/mapper/BlockMapper.kt b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/mapper/BlockMapper.kt new file mode 100644 index 0000000..14a583d --- /dev/null +++ b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/mapper/BlockMapper.kt @@ -0,0 +1,112 @@ +package ru.fredboy.cavedroid.data.items.mapper + +import com.badlogic.gdx.graphics.Texture +import ru.fredboy.cavedroid.data.items.model.BlockDto +import ru.fredboy.cavedroid.data.items.repository.ItemsRepositoryImpl +import ru.fredboy.cavedroid.domain.assets.usecase.GetBlockTextureUseCase +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.block.Block.Chest +import ru.fredboy.cavedroid.domain.items.model.block.Block.Furnace +import ru.fredboy.cavedroid.domain.items.model.block.Block.Lava +import ru.fredboy.cavedroid.domain.items.model.block.Block.None +import ru.fredboy.cavedroid.domain.items.model.block.Block.Normal +import ru.fredboy.cavedroid.domain.items.model.block.Block.Slab +import ru.fredboy.cavedroid.domain.items.model.block.Block.Water +import ru.fredboy.cavedroid.domain.items.model.block.BlockAnimationInfo +import ru.fredboy.cavedroid.domain.items.model.block.BlockDropInfo +import ru.fredboy.cavedroid.domain.items.model.block.BlockMargins +import ru.fredboy.cavedroid.domain.items.model.block.CommonBlockParams +import ru.fredboy.cavedroid.domain.items.model.item.Item +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class BlockMapper @Inject constructor( + private val getBlockTexture: GetBlockTextureUseCase, +) { + + 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)) + "furnace" -> Furnace(commonBlockParams) + "chest" -> Chest(commonBlockParams) + "none" -> None(commonBlockParams) + else -> Normal(commonBlockParams) + } + } + + private fun mapCommonParams(key: String, dto: BlockDto): CommonBlockParams = CommonBlockParams( + 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, + isFallable = dto.fallable, + ) + + private fun mapToolType(dto: BlockDto): Class? = 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 == ItemsRepositoryImpl.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 == ItemsRepositoryImpl.FALLBACK_BLOCK_KEY) { + return null + } + + return getBlockTexture[textureName] + } +} diff --git a/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/mapper/ItemMapper.kt b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/mapper/ItemMapper.kt new file mode 100644 index 0000000..3affe70 --- /dev/null +++ b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/mapper/ItemMapper.kt @@ -0,0 +1,126 @@ +package ru.fredboy.cavedroid.data.items.mapper + +import com.badlogic.gdx.graphics.g2d.Sprite +import ru.fredboy.cavedroid.common.model.SpriteOrigin +import ru.fredboy.cavedroid.common.utils.colorFromHexString +import ru.fredboy.cavedroid.data.items.model.ItemDto +import ru.fredboy.cavedroid.data.items.repository.ItemsRepositoryImpl +import ru.fredboy.cavedroid.domain.assets.usecase.GetItemTextureUseCase +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.item.CommonItemParams +import ru.fredboy.cavedroid.domain.items.model.item.Item +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ItemMapper @Inject constructor( + private val getItemTexture: GetItemTextureUseCase, +) { + + 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" -> Item.Normal( + params = params, + sprite = requireNotNull(loadSprite(dto)), + ) + + "usable" -> Item.Usable( + params = params, + sprite = requireNotNull(loadSprite(dto)), + useActionKey = requireNotNull(dto.actionKey), + ) + + "shovel" -> Item.Shovel( + params = params, + sprite = requireNotNull(loadSprite(dto)), + mobDamageMultiplier = dto.mobDamageMultiplier, + blockDamageMultiplier = dto.blockDamageMultiplier, + level = requireNotNull(dto.toolLevel), + ) + + "sword" -> Item.Sword( + params = params, + sprite = requireNotNull(loadSprite(dto)), + mobDamageMultiplier = dto.mobDamageMultiplier, + blockDamageMultiplier = dto.blockDamageMultiplier, + level = requireNotNull(dto.toolLevel), + ) + + "pickaxe" -> Item.Pickaxe( + params = params, + sprite = requireNotNull(loadSprite(dto)), + mobDamageMultiplier = dto.mobDamageMultiplier, + blockDamageMultiplier = dto.blockDamageMultiplier, + level = requireNotNull(dto.toolLevel), + ) + + "axe" -> Item.Axe( + params = params, + sprite = requireNotNull(loadSprite(dto)), + mobDamageMultiplier = dto.mobDamageMultiplier, + blockDamageMultiplier = dto.blockDamageMultiplier, + level = requireNotNull(dto.toolLevel), + ) + + "shears" -> Item.Shears( + params = params, + sprite = requireNotNull(loadSprite(dto)), + mobDamageMultiplier = dto.mobDamageMultiplier, + blockDamageMultiplier = dto.blockDamageMultiplier, + level = requireNotNull(dto.toolLevel), + ) + + "block" -> Item.Block( + params = params, + block = requireNotNull(block), + ) + + "slab" -> Item.Slab( + params = params, + topPartBlock = requireNotNull(slabTopBlock), + bottomPartBlock = requireNotNull(slabBottomBlock), + ) + + "food" -> Item.Food( + params = params, + sprite = requireNotNull(loadSprite(dto)), + heal = requireNotNull(dto.heal), + ) + + "none" -> Item.None( + params = params, + ) + + else -> throw IllegalArgumentException("Unknown item type ${dto.type}") + } + } + + private fun mapCommonParams(key: String, dto: ItemDto): CommonItemParams = CommonItemParams( + key = key, + name = dto.name, + inHandSpriteOrigin = SpriteOrigin( + x = dto.originX, + y = dto.origin_y, + ), + maxStack = dto.maxStack, + burningTimeMs = dto.burningTime, + smeltProductKey = dto.smeltProduct, + ) + + private fun loadSprite(dto: ItemDto): Sprite? { + if (dto.type == "none" || dto.type == "block" || dto.texture == ItemsRepositoryImpl.FALLBACK_ITEM_KEY) { + return null + } + + val texture = getItemTexture[dto.texture] + return Sprite(texture) + .apply { + flip(false, true) + dto.tint?.let { + color = colorFromHexString(it) + } + } + } +} diff --git a/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/BlockDto.kt b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/BlockDto.kt new file mode 100644 index 0000000..7875461 --- /dev/null +++ b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/BlockDto.kt @@ -0,0 +1,35 @@ +package ru.fredboy.cavedroid.data.items.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BlockDto( + @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, + @SerialName("fallable") val fallable: Boolean = false, +) diff --git a/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/CraftingDto.kt b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/CraftingDto.kt new file mode 100644 index 0000000..1346aa6 --- /dev/null +++ b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/CraftingDto.kt @@ -0,0 +1,9 @@ +package ru.fredboy.cavedroid.data.items.model + +import kotlinx.serialization.Serializable + +@Serializable +data class CraftingDto( + val input: List, + val count: Int = 1, +) diff --git a/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/GameItemsDto.kt b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/GameItemsDto.kt new file mode 100644 index 0000000..5dab153 --- /dev/null +++ b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/GameItemsDto.kt @@ -0,0 +1,10 @@ +package ru.fredboy.cavedroid.data.items.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GameItemsDto( + @SerialName("blocks") val blocks: Map, + @SerialName("items") val items: Map, +) diff --git a/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/ItemDto.kt b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/ItemDto.kt new file mode 100644 index 0000000..1c28444 --- /dev/null +++ b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/model/ItemDto.kt @@ -0,0 +1,24 @@ +package ru.fredboy.cavedroid.data.items.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ItemDto( + @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, + @SerialName("tint") val tint: String? = null, + @SerialName("burning_time") val burningTime: Long? = null, + @SerialName("smelt_product") val smeltProduct: String? = null, + @SerialName("heal") val heal: Int? = null, +) diff --git a/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/repository/ItemsRepositoryImpl.kt b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/repository/ItemsRepositoryImpl.kt new file mode 100644 index 0000000..c8cfdc4 --- /dev/null +++ b/core/data/items/src/main/kotlin/ru/fredboy/cavedroid/data/items/repository/ItemsRepositoryImpl.kt @@ -0,0 +1,176 @@ +package ru.fredboy.cavedroid.data.items.repository + +import com.badlogic.gdx.Gdx +import kotlinx.serialization.json.Json +import ru.fredboy.cavedroid.data.items.mapper.BlockMapper +import ru.fredboy.cavedroid.data.items.mapper.ItemMapper +import ru.fredboy.cavedroid.data.items.model.BlockDto +import ru.fredboy.cavedroid.data.items.model.CraftingDto +import ru.fredboy.cavedroid.data.items.model.GameItemsDto +import ru.fredboy.cavedroid.data.items.model.ItemDto +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.craft.CraftingRecipe +import ru.fredboy.cavedroid.domain.items.model.craft.CraftingResult +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.collections.LinkedHashMap + +@Singleton +internal class ItemsRepositoryImpl @Inject constructor( + private val itemMapper: ItemMapper, + private val blockMapper: BlockMapper, +) : ItemsRepository { + + private var _initialized: Boolean = false + + private val blocksMap = LinkedHashMap() + private val itemsMap = LinkedHashMap() + private val craftingRecipes = LinkedList() + + override lateinit var fallbackBlock: Block.None + private set + + override lateinit var fallbackItem: Item.None + private set + + init { + initialize() + } + + private fun loadBlocks(dtoMap: Map) { + dtoMap.forEach { (key, dto) -> + blocksMap[key] = blockMapper.map(key, dto) + .apply(Block::initialize) + } + + fallbackBlock = blocksMap[FALLBACK_BLOCK_KEY] as? Block.None + ?: throw IllegalArgumentException("Fallback block key '$FALLBACK_BLOCK_KEY' not found") + } + + private fun loadItems(dtoMap: Map) { + 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] as? Item.None + ?: throw IllegalArgumentException("Fallback item key '$FALLBACK_ITEM_KEY' not found") + } + + private fun loadCraftingRecipes() { + val jsonString = Gdx.files.internal("json/crafting.json").readString() + val jsonMap = JsonFormat.decodeFromString>(jsonString) + + if (jsonMap.isNotEmpty() && itemsMap.isEmpty()) { + throw IllegalStateException("items should be loaded before crafting") + } + + jsonMap.forEach { (key, value) -> + craftingRecipes += CraftingRecipe( + input = value.input.map(::Regex), + output = CraftingResult(getItemByKey(key), value.count), + ) + } + } + + private fun Map.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 + } + + override fun initialize() { + if (_initialized) { + Gdx.app.debug(TAG, "Attempted to init when already initialized") + return + } + + val jsonString = Gdx.files.internal("json/game_items.json").readString() + val gameItemsDto = JsonFormat.decodeFromString(jsonString) + + loadBlocks(gameItemsDto.blocks) + loadItems(gameItemsDto.items) + + _initialized = true + + loadCraftingRecipes() + } + + override fun getItemByKey(key: String): Item = itemsMap.getOrFallback(key, fallbackItem) { + "No item with key '$key' found. Returning $FALLBACK_ITEM_KEY" + } + + override fun getItemByIndex(index: Int): Item = if (index in itemsMap.values.indices) { + itemsMap.values.elementAt(index) + } else { + fallbackItem + } + + override fun getBlockByKey(key: String): Block = blocksMap.getOrFallback(key, fallbackBlock) { + "No block with key '$key' found. Returning $FALLBACK_BLOCK_KEY" + } + + override fun getBlocksByType(type: Class): List = blocksMap.values.filterIsInstance(type) + + override fun getCraftingResult(input: List): InventoryItem { + val startIndex = input.indexOfFirst { !it.isNone() }.takeIf { it >= 0 } + ?: return fallbackItem.toInventoryItem() + + val output = craftingRecipes.firstOrNull { rec -> + for (i in rec.input.indices) { + if (startIndex + i >= input.size) { + return@firstOrNull rec.input.subList(i, rec.input.size).all { it.matches("none") } + } + if (!input[startIndex + i].params.key.matches(rec.input[i])) { + return@firstOrNull false + } + } + return@firstOrNull true + }?.output + + return output?.toInventoryItem() ?: fallbackItem.toInventoryItem() + } + + override fun getAllItems(): Collection = itemsMap.values + + override fun dispose() { + _initialized = false + + blocksMap.clear() + itemsMap.clear() + craftingRecipes.clear() + } + + companion object { + private const val TAG = "ItemsRepositoryImpl" + + private val JsonFormat = Json { ignoreUnknownKeys = true } + + const val FALLBACK_BLOCK_KEY = "none" + const val FALLBACK_ITEM_KEY = "none" + } +} diff --git a/core/data/menu/build.gradle.kts b/core/data/menu/build.gradle.kts new file mode 100644 index 0000000..73fdbcc --- /dev/null +++ b/core/data/menu/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin + ksp + kotlinxSerialization +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useLibgdx() + useKotlinxSerializationJson() + useDagger() + + useModule(":core:domain:assets") + useModule(":core:domain:menu") +} diff --git a/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/di/DataMenuModule.kt b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/di/DataMenuModule.kt new file mode 100644 index 0000000..29e3e29 --- /dev/null +++ b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/di/DataMenuModule.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.data.menu.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.data.menu.repository.MenuButtonRepositoryImpl +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository + +@Module +abstract class DataMenuModule { + + @Binds + internal abstract fun bindMenuButtonsRepository(impl: MenuButtonRepositoryImpl): MenuButtonRepository +} diff --git a/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/mapper/MenuButtonMapper.kt b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/mapper/MenuButtonMapper.kt new file mode 100644 index 0000000..94ad55c --- /dev/null +++ b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/mapper/MenuButtonMapper.kt @@ -0,0 +1,57 @@ +package ru.fredboy.cavedroid.data.menu.mapper + +import com.badlogic.gdx.Application +import com.badlogic.gdx.Gdx +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.data.menu.model.MenuButtonDto +import ru.fredboy.cavedroid.data.menu.model.MenuButtonVisibilityDto +import ru.fredboy.cavedroid.domain.menu.model.MenuButton +import javax.inject.Inject + +@MenuScope +class MenuButtonMapper @Inject constructor() { + + fun map(dto: MenuButtonDto): MenuButton = when (dto.type) { + "boolean_option" -> MenuButton.BooleanOption( + label = dto.label.toString(), + isVisible = mapVisibility(dto.visibility), + actionKey = dto.actionKey.orEmpty(), + optionKeys = dto.options.orEmpty(), + isEnabled = dto.enabled ?: true, + ) + + "numerical_option" -> MenuButton.NumericalOption( + label = dto.label.toString(), + isVisible = mapVisibility(dto.visibility), + actionKey = dto.actionKey.orEmpty(), + optionKeys = dto.options.orEmpty(), + isEnabled = dto.enabled ?: true, + ) + + "default", null -> MenuButton.Simple( + label = dto.label.toString(), + isVisible = mapVisibility(dto.visibility), + actionKey = dto.actionKey.orEmpty(), + isEnabled = dto.enabled ?: true, + ) + + else -> + throw IllegalArgumentException("Unknown button type: ${dto.type}") + } + + fun setButtonEnabled(button: MenuButton, isEnabled: Boolean): MenuButton = when (button) { + is MenuButton.Simple -> button.copy(isEnabled = isEnabled) + is MenuButton.BooleanOption -> button.copy(isEnabled = isEnabled) + is MenuButton.NumericalOption -> button.copy(isEnabled = isEnabled) + } + + private fun mapVisibility(dto: MenuButtonVisibilityDto?): Boolean { + dto ?: return true + + return when (Gdx.app.type) { + Application.ApplicationType.Android -> dto.android + Application.ApplicationType.iOS -> dto.ios + else -> dto.desktop + } + } +} diff --git a/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/model/MenuButtonDto.kt b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/model/MenuButtonDto.kt new file mode 100644 index 0000000..18819b1 --- /dev/null +++ b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/model/MenuButtonDto.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.data.menu.model + +import kotlinx.serialization.Serializable + +@Serializable +data class MenuButtonDto( + val label: String? = null, + val actionKey: String? = null, + val type: String? = null, + val enabled: Boolean? = null, + val visibility: MenuButtonVisibilityDto? = null, + val options: List? = null, +) diff --git a/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/model/MenuButtonVisibilityDto.kt b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/model/MenuButtonVisibilityDto.kt new file mode 100644 index 0000000..fd4996a --- /dev/null +++ b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/model/MenuButtonVisibilityDto.kt @@ -0,0 +1,10 @@ +package ru.fredboy.cavedroid.data.menu.model + +import kotlinx.serialization.Serializable + +@Serializable +data class MenuButtonVisibilityDto( + val android: Boolean = true, + val desktop: Boolean = true, + val ios: Boolean = true, +) diff --git a/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/repository/MenuButtonRepositoryImpl.kt b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/repository/MenuButtonRepositoryImpl.kt new file mode 100644 index 0000000..2b3d94b --- /dev/null +++ b/core/data/menu/src/main/kotlin/ru/fredboy/cavedroid/data/menu/repository/MenuButtonRepositoryImpl.kt @@ -0,0 +1,87 @@ +package ru.fredboy.cavedroid.data.menu.repository + +import com.badlogic.gdx.Gdx +import kotlinx.serialization.json.Json +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.data.menu.mapper.MenuButtonMapper +import ru.fredboy.cavedroid.data.menu.model.MenuButtonDto +import ru.fredboy.cavedroid.domain.menu.model.MenuButton +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository +import javax.inject.Inject + +@MenuScope +class MenuButtonRepositoryImpl @Inject constructor( + private val menuButtonMapper: MenuButtonMapper, +) : MenuButtonRepository { + + private var _initialized = false + + private val menuMap = LinkedHashMap>() + + private var currentMenu = "main" + + init { + initialize() + } + + override fun initialize() { + if (_initialized) { + Gdx.app.debug(TAG, "Attempted to init when already initialized") + return + } + + val jsonString = Gdx.files.internal("json/menu.json").readString() + + JsonFormat.decodeFromString>>(jsonString) + .forEach { (key, value) -> + menuMap[key] = value.mapValues { (_, dto) -> menuButtonMapper.map(dto) }.toMutableMap() + } + + _initialized = true + } + + override fun getButtonsForMenu(menuKey: String): Map? { + require(_initialized) { "$TAG was not initialized before access!" } + + return menuMap[menuKey].also { + if (it == null) { + Gdx.app.debug(TAG, "No buttons for $menuKey menu found") + } + } + } + + override fun setCurrentMenu(menuKey: String) { + currentMenu = menuKey + } + + override fun getCurrentMenuButtons(): Map? { + require(_initialized) { "$TAG was not initialized before access!" } + + return menuMap[currentMenu]?.filterValues { button -> button.isVisible } + } + + override fun setButtonEnabled(menuKey: String, buttonKey: String, enabled: Boolean) { + val menu = menuMap[menuKey] ?: run { + Gdx.app.error(TAG, "setButtonEnabled: menu with key '$menuKey' not found") + return + } + + val button = menu[buttonKey] ?: run { + Gdx.app.error(TAG, "setButtonEnabled: button with key '$buttonKey' not found") + return + } + + menu[buttonKey] = menuButtonMapper.setButtonEnabled(button, enabled) + } + + override fun dispose() { + menuMap.clear() + _initialized = false + } + + companion object { + private const val TAG = "MenuButtonsRepositoryImpl" + + private val JsonFormat = Json { ignoreUnknownKeys = true } + } +} diff --git a/core/data/save/build.gradle.kts b/core/data/save/build.gradle.kts new file mode 100644 index 0000000..1153b84 --- /dev/null +++ b/core/data/save/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + kotlin + ksp + kotlinxSerialization +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useLibgdx() + useKotlinxSerializationProtobuf() + useDagger() + + useModule(":core:domain:assets") + useModule(":core:domain:items") + useModule(":core:domain:world") + useModule(":core:domain:save") + + useModule(":core:entity:container") + useModule(":core:entity:drop") + useModule(":core:entity:mob") + + useModule(":core:game:controller:container") + useModule(":core:game:controller:drop") + useModule(":core:game:controller:mob") + + useModule(":core:game:world") +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/di/DataSaveModule.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/di/DataSaveModule.kt new file mode 100644 index 0000000..e725055 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/di/DataSaveModule.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.data.save.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.data.save.repository.SaveDataRepositoryImpl +import ru.fredboy.cavedroid.domain.save.repository.SaveDataRepository + +@Module +abstract class DataSaveModule { + + @Binds + internal abstract fun bindSaveDataRepository(impl: SaveDataRepositoryImpl): SaveDataRepository +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ChestMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ChestMapper.kt new file mode 100644 index 0000000..072d599 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ChestMapper.kt @@ -0,0 +1,33 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import ru.fredboy.cavedroid.entity.container.model.Chest +import javax.inject.Inject + +@Reusable +class ChestMapper @Inject constructor( + private val inventoryItemMapper: InventoryItemMapper, + private val getFallbackItemUseCase: GetFallbackItemUseCase, +) { + + fun mapSaveData(chest: Chest): SaveDataDto.ChestSaveDataDto = SaveDataDto.ChestSaveDataDto( + version = SAVE_DATA_VERSION, + size = chest.size, + items = chest.items.map(inventoryItemMapper::mapSaveData), + ) + + fun mapChest(saveDataDto: SaveDataDto.ChestSaveDataDto): Chest { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return Chest( + fallbackItem = getFallbackItemUseCase(), + initialItems = saveDataDto.items.map(inventoryItemMapper::mapInventoryItem), + ) + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ContainerControllerMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ContainerControllerMapper.kt new file mode 100644 index 0000000..0323bd0 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ContainerControllerMapper.kt @@ -0,0 +1,64 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerFactory +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerWorldAdapter +import ru.fredboy.cavedroid.entity.container.model.Chest +import ru.fredboy.cavedroid.entity.container.model.ContainerCoordinates +import ru.fredboy.cavedroid.entity.container.model.Furnace +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import javax.inject.Inject + +@Reusable +class ContainerControllerMapper @Inject constructor( + private val chestMapper: ChestMapper, + private val furnaceMapper: FurnaceMapper, + private val getItemByKeyUseCase: GetItemByKeyUseCase, +) { + + fun mapSaveData(containerController: ContainerController): SaveDataDto.ContainerControllerSaveDataDto = SaveDataDto.ContainerControllerSaveDataDto( + version = SAVE_DATA_VERSION, + containerMap = containerController.containerMap.mapNotNull { (key, container) -> + when (container) { + is Furnace -> furnaceMapper.mapSaveData(container) + is Chest -> chestMapper.mapSaveData(container) + else -> null + }?.let { value -> key.toString() to value } + }.toMap(), + ) + + fun mapContainerController( + saveDataDto: SaveDataDto.ContainerControllerSaveDataDto, + containerWorldAdapter: ContainerWorldAdapter, + containerFactory: ContainerFactory, + dropAdapter: DropAdapter, + ): ContainerController { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return ContainerController( + getItemByKeyUseCase = getItemByKeyUseCase, + containerWorldAdapter = containerWorldAdapter, + containerFactory = containerFactory, + dropAdapter = dropAdapter, + ).apply { + saveDataDto.containerMap.forEach { (key, value) -> + val container = when (value) { + is SaveDataDto.FurnaceSaveDataDto -> furnaceMapper.mapFurnace(value) + is SaveDataDto.ChestSaveDataDto -> chestMapper.mapChest(value) + else -> null + } + + if (container != null) { + containerMap.put(ContainerCoordinates.fromString(key), container) + } + } + } + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ControlModeMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ControlModeMapper.kt new file mode 100644 index 0000000..7c0e6dd --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/ControlModeMapper.kt @@ -0,0 +1,37 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import com.badlogic.gdx.Gdx +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.entity.mob.model.Player +import javax.inject.Inject + +@Reusable +class ControlModeMapper @Inject constructor() { + + fun mapSaveData(direction: Player.ControlMode): SaveDataDto.ControlModeSaveDataDto = SaveDataDto.ControlModeSaveDataDto( + version = SAVE_DATA_VERSION, + value = when (direction) { + Player.ControlMode.WALK -> 0 + Player.ControlMode.CURSOR -> 1 + }, + ) + + fun mapControlMode(saveDataDto: SaveDataDto.ControlModeSaveDataDto): Player.ControlMode { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return when (saveDataDto.value) { + 0 -> Player.ControlMode.WALK + 1 -> Player.ControlMode.CURSOR + else -> { + Gdx.app.error(TAG, "Unknown control mode value: ${saveDataDto.value}") + Player.ControlMode.CURSOR + } + } + } + + companion object { + private const val TAG = "ControlModeMapper" + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DirectionMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DirectionMapper.kt new file mode 100644 index 0000000..aaee277 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DirectionMapper.kt @@ -0,0 +1,37 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import com.badlogic.gdx.Gdx +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.entity.mob.model.Direction +import javax.inject.Inject + +@Reusable +class DirectionMapper @Inject constructor() { + + fun mapSaveData(direction: Direction): SaveDataDto.DirectionSaveDataDto = SaveDataDto.DirectionSaveDataDto( + version = SAVE_DATA_VERSION, + value = when (direction) { + Direction.LEFT -> 0 + Direction.RIGHT -> 1 + }, + ) + + fun mapDirection(saveDataDto: SaveDataDto.DirectionSaveDataDto): Direction { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return when (saveDataDto.value) { + 0 -> Direction.LEFT + 1 -> Direction.RIGHT + else -> { + Gdx.app.error(TAG, "Unknown direction value: ${saveDataDto.value}") + Direction.RIGHT + } + } + } + + companion object { + private const val TAG = "DirectionMapper" + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DropControllerMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DropControllerMapper.kt new file mode 100644 index 0000000..159de62 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DropControllerMapper.kt @@ -0,0 +1,37 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.entity.drop.abstraction.DropWorldAdapter +import ru.fredboy.cavedroid.game.controller.drop.DropController +import javax.inject.Inject + +@Reusable +class DropControllerMapper @Inject constructor( + private val dropMapper: DropMapper, + private val itemsRepository: ItemsRepository, +) { + + fun mapSaveData(dropController: DropController): SaveDataDto.DropControllerSaveDataDto = SaveDataDto.DropControllerSaveDataDto( + version = SAVE_DATA_VERSION, + drops = dropController.getAllDrop().map(dropMapper::mapSaveData), + ) + + fun mapDropController( + saveDataDto: SaveDataDto.DropControllerSaveDataDto, + dropWorldAdapter: DropWorldAdapter, + ): DropController { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return DropController( + initialDrop = saveDataDto.drops.map(dropMapper::mapDrop), + dropWorldAdapter = dropWorldAdapter, + itemsRepository = itemsRepository, + ) + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DropMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DropMapper.kt new file mode 100644 index 0000000..f950755 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/DropMapper.kt @@ -0,0 +1,47 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.entity.drop.model.Drop +import javax.inject.Inject + +@Reusable +class DropMapper @Inject constructor( + private val getItemByKeyUseCase: GetItemByKeyUseCase, +) { + + fun mapSaveData(drop: Drop): SaveDataDto.DropSaveDataDto = SaveDataDto.DropSaveDataDto( + version = SAVE_DATA_VERSION, + x = drop.x, + y = drop.y, + width = drop.width, + height = drop.height, + velocityX = drop.velocity.x, + velocityY = drop.velocity.y, + itemKey = drop.item.params.key, + amount = drop.amount, + pickedUp = drop.isPickedUp, + ) + + fun mapDrop(saveDataDto: SaveDataDto.DropSaveDataDto): Drop { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return Drop( + x = saveDataDto.x, + y = saveDataDto.y, + item = getItemByKeyUseCase[saveDataDto.itemKey], + amount = saveDataDto.amount, + ).apply { + width = saveDataDto.width + height = saveDataDto.height + velocity.y = saveDataDto.velocityY + velocity.x = saveDataDto.velocityX + isPickedUp = saveDataDto.pickedUp + } + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/FallingBlockMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/FallingBlockMapper.kt new file mode 100644 index 0000000..20441c5 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/FallingBlockMapper.kt @@ -0,0 +1,60 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.usecase.GetBlockByKeyUseCase +import ru.fredboy.cavedroid.entity.mob.model.FallingBlock +import ru.fredboy.cavedroid.game.controller.mob.behavior.FallingBlockMobBehavior +import javax.inject.Inject + +@Reusable +class FallingBlockMapper @Inject constructor( + private val directionMapper: DirectionMapper, + private val getBlockByKeyUseCase: GetBlockByKeyUseCase, +) { + + fun mapSaveData(fallingBlock: FallingBlock): SaveDataDto.FallingBlockSaveDataDto = SaveDataDto.FallingBlockSaveDataDto( + version = SAVE_DATA_VERSION, + x = fallingBlock.x, + y = fallingBlock.y, + width = fallingBlock.width, + height = fallingBlock.height, + velocityX = fallingBlock.velocity.x, + velocityY = fallingBlock.velocity.y, + animDelta = fallingBlock.animDelta, + anim = fallingBlock.anim, + direction = directionMapper.mapSaveData(fallingBlock.direction), + dead = fallingBlock.isDead, + canJump = fallingBlock.canJump, + flyMode = fallingBlock.isFlyMode, + maxHealth = fallingBlock.maxHealth, + health = fallingBlock.health, + blockKey = fallingBlock.block.params.key, + ) + + fun mapFallingBlock(saveDataDto: SaveDataDto.FallingBlockSaveDataDto): FallingBlock { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return FallingBlock( + block = getBlockByKeyUseCase[saveDataDto.blockKey], + x = saveDataDto.x, + y = saveDataDto.y, + behavior = FallingBlockMobBehavior(), + ).apply { + width = saveDataDto.width + height = saveDataDto.height + velocity.x = saveDataDto.velocityX + velocity.x = saveDataDto.velocityY + animDelta = saveDataDto.animDelta + anim = saveDataDto.anim + direction = directionMapper.mapDirection(saveDataDto.direction) + canJump = saveDataDto.canJump + isFlyMode = saveDataDto.flyMode + health = saveDataDto.health + } + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/FurnaceMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/FurnaceMapper.kt new file mode 100644 index 0000000..ad52058 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/FurnaceMapper.kt @@ -0,0 +1,46 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.entity.container.model.Furnace +import javax.inject.Inject + +@Reusable +class FurnaceMapper @Inject constructor( + private val inventoryItemMapper: InventoryItemMapper, + private val getFallbackItem: GetFallbackItemUseCase, + private val getItemByKey: GetItemByKeyUseCase, +) { + + fun mapSaveData(furnace: Furnace): SaveDataDto.FurnaceSaveDataDto = SaveDataDto.FurnaceSaveDataDto( + version = SAVE_DATA_VERSION, + size = furnace.size, + currentFuelItemKey = furnace.currentFuelKey, + items = furnace.items.map(inventoryItemMapper::mapSaveData), + startBurnTimeMs = furnace.startBurnTimeMs, + startSmeltTimeMs = furnace.smeltStarTimeMs, + burnProgress = furnace.burnProgress, + smeltProgress = furnace.smeltProgress, + ) + + fun mapFurnace(saveDataDto: SaveDataDto.FurnaceSaveDataDto): Furnace { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return Furnace( + fallbackItem = getFallbackItem(), + initialItems = saveDataDto.items.map(inventoryItemMapper::mapInventoryItem), + ).apply { + currentFuel = saveDataDto.currentFuelItemKey?.let(getItemByKey::get) ?: getFallbackItem() + startBurnTimeMs = saveDataDto.startBurnTimeMs + smeltStarTimeMs = saveDataDto.startSmeltTimeMs + burnProgress = saveDataDto.burnProgress + smeltProgress = saveDataDto.smeltProgress + } + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/InventoryItemMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/InventoryItemMapper.kt new file mode 100644 index 0000000..a963663 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/InventoryItemMapper.kt @@ -0,0 +1,32 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import javax.inject.Inject + +@Reusable +class InventoryItemMapper @Inject constructor( + private val getItemByKeyUseCase: GetItemByKeyUseCase, +) { + + fun mapSaveData(inventoryItem: InventoryItem): SaveDataDto.InventoryItemSaveDataDto = SaveDataDto.InventoryItemSaveDataDto( + version = SAVE_DATA_VERSION, + itemKey = inventoryItem.item.params.key, + amount = inventoryItem.amount, + ) + + fun mapInventoryItem(saveDataDto: SaveDataDto.InventoryItemSaveDataDto): InventoryItem { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return InventoryItem( + item = getItemByKeyUseCase[saveDataDto.itemKey], + _amount = saveDataDto.amount, + ) + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/InventoryMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/InventoryMapper.kt new file mode 100644 index 0000000..7be7c51 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/InventoryMapper.kt @@ -0,0 +1,34 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.model.inventory.Inventory +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import javax.inject.Inject + +@Reusable +class InventoryMapper @Inject constructor( + private val inventoryItemMapper: InventoryItemMapper, + private val getFallbackItem: GetFallbackItemUseCase, +) { + + fun mapSaveData(inventory: Inventory): SaveDataDto.InventorySaveDataDto = SaveDataDto.InventorySaveDataDto( + version = SAVE_DATA_VERSION, + size = inventory.size, + items = inventory.items.map(inventoryItemMapper::mapSaveData), + ) + + fun mapInventory(saveDataDto: SaveDataDto.InventorySaveDataDto): Inventory { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return Inventory( + size = saveDataDto.size, + fallbackItem = getFallbackItem(), + initialItems = saveDataDto.items.map(inventoryItemMapper::mapInventoryItem), + ) + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/MobControllerMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/MobControllerMapper.kt new file mode 100644 index 0000000..095610a --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/MobControllerMapper.kt @@ -0,0 +1,62 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.assets.repository.MobAssetsRepository +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.entity.mob.model.FallingBlock +import ru.fredboy.cavedroid.entity.mob.model.Pig +import ru.fredboy.cavedroid.game.controller.mob.MobController +import javax.inject.Inject + +@Reusable +class MobControllerMapper @Inject constructor( + private val pigMapper: PigMapper, + private val fallingBlockMapper: FallingBlockMapper, + private val playerMapper: PlayerMapper, + private val mobAssetsRepository: MobAssetsRepository, + private val getFallbackItemUseCase: GetFallbackItemUseCase, +) { + + fun mapSaveData(mobController: MobController): SaveDataDto.MobControllerSaveDataDto = SaveDataDto.MobControllerSaveDataDto( + version = SAVE_DATA_VERSION, + mobs = mobController.mobs.mapNotNull { mob -> + when (mob) { + is Pig -> pigMapper.mapSaveData(mob) + is FallingBlock -> fallingBlockMapper.mapSaveData(mob) + else -> null + } + }, + player = playerMapper.mapSaveData(mobController.player), + ) + + fun mapMobController( + saveDataDto: SaveDataDto.MobControllerSaveDataDto, + mobWorldAdapter: MobWorldAdapter, + ): MobController { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return MobController( + mobAssetsRepository = mobAssetsRepository, + getFallbackItemUseCase = getFallbackItemUseCase, + mobWorldAdapter = mobWorldAdapter, + ).apply { + (mobs as MutableList).addAll( + saveDataDto.mobs.mapNotNull { mob -> + when (mob) { + is SaveDataDto.PigSaveDataDto -> pigMapper.mapPig(mob) + is SaveDataDto.FallingBlockSaveDataDto -> fallingBlockMapper.mapFallingBlock(mob) + else -> null + } + }, + ) + + player = playerMapper.mapPlayer(saveDataDto.player) + } + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/PigMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/PigMapper.kt new file mode 100644 index 0000000..102e7a8 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/PigMapper.kt @@ -0,0 +1,59 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.assets.usecase.GetPigSpritesUseCase +import ru.fredboy.cavedroid.entity.mob.model.Pig +import ru.fredboy.cavedroid.game.controller.mob.behavior.PigMobBehavior +import javax.inject.Inject + +@Reusable +class PigMapper @Inject constructor( + private val directionMapper: DirectionMapper, + private val getPigSpriteUseCase: GetPigSpritesUseCase, +) { + + fun mapSaveData(pig: Pig): SaveDataDto.PigSaveDataDto = SaveDataDto.PigSaveDataDto( + version = SAVE_DATA_VERSION, + x = pig.x, + y = pig.y, + width = pig.width, + height = pig.height, + velocityX = pig.velocity.x, + velocityY = pig.velocity.y, + animDelta = pig.animDelta, + anim = pig.anim, + direction = directionMapper.mapSaveData(pig.direction), + dead = pig.isDead, + canJump = pig.canJump, + flyMode = pig.isFlyMode, + maxHealth = pig.maxHealth, + health = pig.health, + ) + + fun mapPig(saveDataDto: SaveDataDto.PigSaveDataDto): Pig { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return Pig( + sprite = getPigSpriteUseCase(), + x = saveDataDto.x, + y = saveDataDto.y, + behavior = PigMobBehavior(), + ).apply { + width = saveDataDto.width + height = saveDataDto.height + velocity.x = saveDataDto.velocityX + velocity.x = saveDataDto.velocityY + animDelta = saveDataDto.animDelta + anim = saveDataDto.anim + direction = directionMapper.mapDirection(saveDataDto.direction) + canJump = saveDataDto.canJump + isFlyMode = saveDataDto.flyMode + health = saveDataDto.health + } + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/PlayerMapper.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/PlayerMapper.kt new file mode 100644 index 0000000..b451b95 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/mapper/PlayerMapper.kt @@ -0,0 +1,94 @@ +package ru.fredboy.cavedroid.data.save.mapper + +import com.badlogic.gdx.math.Vector2 +import dagger.Reusable +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.assets.usecase.GetPlayerSpritesUseCase +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.behavior.PlayerMobBehavior +import javax.inject.Inject + +@Reusable +class PlayerMapper @Inject constructor( + private val directionMapper: DirectionMapper, + private val inventoryMapper: InventoryMapper, + private val controlModeMapper: ControlModeMapper, + private val getPlayerSpritesUseCase: GetPlayerSpritesUseCase, + private val getFallbackItemUseCase: GetFallbackItemUseCase, +) { + + fun mapSaveData(player: Player): SaveDataDto.PlayerSaveDataDto = SaveDataDto.PlayerSaveDataDto( + version = SAVE_DATA_VERSION, + x = player.x, + y = player.y, + width = player.width, + height = player.height, + velocityX = player.velocity.x, + velocityY = player.velocity.y, + animDelta = player.animDelta, + anim = player.anim, + direction = directionMapper.mapSaveData(player.direction), + dead = player.isDead, + canJump = player.canJump, + flyMode = player.isFlyMode, + maxHealth = player.maxHealth, + health = player.health, + hitting = player.isHitting, + hittingWithDamage = player.isHittingWithDamage, + hitAnim = player.hitAnim, + hitAnimDelta = player.hitAnimDelta, + inventory = inventoryMapper.mapSaveData(player.inventory), + gameMode = player.gameMode, + swim = player.swim, + headRotation = player.headRotation, + blockDamage = player.blockDamage, + cursorX = player.cursorX, + cursorY = player.cursorY, + spawnPointX = player.spawnPoint?.x ?: 0f, + spawnPointY = player.spawnPoint?.y ?: 0f, + controlMode = controlModeMapper.mapSaveData(player.controlMode), + activeSlot = player.activeSlot, + ) + + fun mapPlayer(saveDataDto: SaveDataDto.PlayerSaveDataDto): Player { + saveDataDto.verifyVersion(SAVE_DATA_VERSION) + + return Player( + sprite = getPlayerSpritesUseCase(), + getFallbackItem = getFallbackItemUseCase, + x = saveDataDto.x, + y = saveDataDto.y, + behavior = PlayerMobBehavior(), + ).apply { + width = saveDataDto.width + height = saveDataDto.height + velocity.x = saveDataDto.velocityX + velocity.x = saveDataDto.velocityY + animDelta = saveDataDto.animDelta + anim = saveDataDto.anim + direction = directionMapper.mapDirection(saveDataDto.direction) + canJump = saveDataDto.canJump + isFlyMode = saveDataDto.flyMode + health = saveDataDto.health + isHitting = saveDataDto.hitting + isHittingWithDamage = saveDataDto.hittingWithDamage + hitAnim = saveDataDto.hitAnim + hitAnimDelta = saveDataDto.hitAnimDelta + inventory = inventoryMapper.mapInventory(saveDataDto.inventory) + gameMode = saveDataDto.gameMode + swim = saveDataDto.swim + headRotation = saveDataDto.headRotation + blockDamage = saveDataDto.blockDamage + cursorX = saveDataDto.cursorX + cursorY = saveDataDto.cursorY + controlMode = controlModeMapper.mapControlMode(saveDataDto.controlMode) + spawnPoint = Vector2(saveDataDto.spawnPointX, saveDataDto.spawnPointY) + activeSlot = saveDataDto.activeSlot + } + } + + companion object { + private const val SAVE_DATA_VERSION = 3 + } +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/model/SaveDataDto.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/model/SaveDataDto.kt new file mode 100644 index 0000000..6305b50 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/model/SaveDataDto.kt @@ -0,0 +1,195 @@ +package ru.fredboy.cavedroid.data.save.model + +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable + +@Serializable +sealed class SaveDataDto { + + abstract val version: Int + + fun verifyVersion(expectedVersion: Int) { + require(version == expectedVersion) { + "${this::class.simpleName} version mismatch ($version != $expectedVersion)" + } + } + + @Serializable + data class DirectionSaveDataDto( + override val version: Int, + val value: Int, + ) : SaveDataDto() + + @Serializable + data class ControlModeSaveDataDto( + override val version: Int, + val value: Int, + ) : SaveDataDto() + + @Serializable + sealed class ContainerSaveDataDto : SaveDataDto() { + abstract val size: Int + abstract val items: List + } + + @Serializable + sealed class RectangleObjectSaveDataDto : SaveDataDto() { + abstract val x: Float + abstract val y: Float + abstract val width: Float + abstract val height: Float + abstract val velocityX: Float + abstract val velocityY: Float + } + + @Serializable + sealed class MobSaveDataDto : RectangleObjectSaveDataDto() { + abstract val animDelta: Float + abstract val anim: Float + abstract val direction: DirectionSaveDataDto + abstract val dead: Boolean + abstract val canJump: Boolean + abstract val flyMode: Boolean + abstract val maxHealth: Int + abstract val health: Int + } + + @Serializable + data class InventoryItemSaveDataDto( + override val version: Int, + val itemKey: String, + val amount: Int, + ) : SaveDataDto() + + @Serializable + data class InventorySaveDataDto( + override val version: Int, + override val size: Int, + override val items: List, + ) : ContainerSaveDataDto() + + @Serializable + data class FurnaceSaveDataDto( + override val version: Int, + override val size: Int, + val currentFuelItemKey: String?, + override val items: List, + val startBurnTimeMs: Long, + val startSmeltTimeMs: Long, + val burnProgress: Float, + val smeltProgress: Float, + ) : ContainerSaveDataDto() + + @Serializable + data class ChestSaveDataDto( + override val version: Int, + override val size: Int, + override val items: List, + ) : ContainerSaveDataDto() + + @Serializable + data class ContainerControllerSaveDataDto( + override val version: Int, + val containerMap: Map, + ) : SaveDataDto() + + @Serializable + data class DropSaveDataDto( + override val version: Int, + override val x: Float, + override val y: Float, + override val width: Float, + override val height: Float, + override val velocityX: Float, + override val velocityY: Float, + val itemKey: String, + val amount: Int, + val pickedUp: Boolean, + ) : RectangleObjectSaveDataDto() + + @Serializable + data class DropControllerSaveDataDto( + override val version: Int, + val drops: List, + ) : SaveDataDto() + + @Serializable + data class PigSaveDataDto( + override val version: Int, + override val x: Float, + override val y: Float, + override val width: Float, + override val height: Float, + override val velocityX: Float, + override val velocityY: Float, + override val animDelta: Float, + override val anim: Float, + override val direction: DirectionSaveDataDto, + override val dead: Boolean, + override val canJump: Boolean, + override val flyMode: Boolean, + override val maxHealth: Int, + override val health: Int, + ) : MobSaveDataDto() + + @Serializable + data class FallingBlockSaveDataDto( + override val version: Int, + override val x: Float, + override val y: Float, + override val width: Float, + override val height: Float, + override val velocityX: Float, + override val velocityY: Float, + override val animDelta: Float, + override val anim: Float, + override val direction: DirectionSaveDataDto, + override val dead: Boolean, + override val canJump: Boolean, + override val flyMode: Boolean, + override val maxHealth: Int, + override val health: Int, + val blockKey: String, + ) : MobSaveDataDto() + + @Serializable + data class PlayerSaveDataDto( + override val version: Int, + override val animDelta: Float, + override val anim: Float, + override val direction: DirectionSaveDataDto, + override val dead: Boolean, + override val canJump: Boolean, + override val flyMode: Boolean, + override val maxHealth: Int, + override val health: Int, + override val x: Float, + override val y: Float, + override val width: Float, + override val height: Float, + override val velocityX: Float, + override val velocityY: Float, + val hitting: Boolean, + val hittingWithDamage: Boolean, + val hitAnim: Float, + val hitAnimDelta: Float, + val inventory: InventorySaveDataDto, + val gameMode: Int, + val swim: Boolean, + val headRotation: Float, + val blockDamage: Float, + val cursorX: Int, + val cursorY: Int, + val spawnPointX: Float, + val spawnPointY: Float, + val controlMode: ControlModeSaveDataDto, + val activeSlot: Int, + ) : MobSaveDataDto() + + @Serializable + data class MobControllerSaveDataDto( + override val version: Int, + val mobs: List<@Contextual MobSaveDataDto>, + val player: PlayerSaveDataDto, + ) : SaveDataDto() +} diff --git a/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/repository/SaveDataRepositoryImpl.kt b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/repository/SaveDataRepositoryImpl.kt new file mode 100644 index 0000000..0f2d619 --- /dev/null +++ b/core/data/save/src/main/kotlin/ru/fredboy/cavedroid/data/save/repository/SaveDataRepositoryImpl.kt @@ -0,0 +1,290 @@ +package ru.fredboy.cavedroid.data.save.repository + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.files.FileHandle +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import kotlinx.serialization.protobuf.ProtoBuf +import ru.fredboy.cavedroid.data.save.mapper.ContainerControllerMapper +import ru.fredboy.cavedroid.data.save.mapper.DropControllerMapper +import ru.fredboy.cavedroid.data.save.mapper.MobControllerMapper +import ru.fredboy.cavedroid.data.save.model.SaveDataDto +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.domain.save.model.GameMapSaveData +import ru.fredboy.cavedroid.domain.save.repository.SaveDataRepository +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerFactory +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerWorldAdapter +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter +import ru.fredboy.cavedroid.entity.drop.abstraction.DropWorldAdapter +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import java.nio.ByteBuffer +import java.util.zip.GZIPInputStream +import java.util.zip.GZIPOutputStream +import javax.inject.Inject + +internal class SaveDataRepositoryImpl @Inject constructor( + private val itemsRepository: ItemsRepository, + private val dropControllerMapper: DropControllerMapper, + private val containerControllerMapper: ContainerControllerMapper, + private val mobControllerMapper: MobControllerMapper, +) : SaveDataRepository { + + private fun Int.toByteArray(): ByteArray = ByteBuffer.allocate(Int.SIZE_BYTES) + .putInt(this) + .array() + + private fun Short.toByteArray(): ByteArray = ByteBuffer.allocate(Short.SIZE_BYTES) + .putShort(this) + .array() + + private fun buildBlocksDictionary( + foreMap: Array>, + backMap: Array>, + ): Map { + val maps = sequenceOf(foreMap.asSequence(), backMap.asSequence()) + + return maps.flatten() + .flatMap(Array::asSequence) + .map { it.params.key } + .toSet() + .mapIndexed { index, key -> key to index } + .toMap() + } + + private fun saveDict(file: FileHandle, dict: Map) { + val result = dict.asSequence() + .sortedBy { it.value } + .joinToString(separator = "\n") { it.key } + .encodeToByteArray() + + file.writeBytes(result, false) + } + + private fun compressMap(map: Array>, dict: Map): ByteArray { + if (dict.size > 0xff) { + throw IllegalArgumentException("Cannot save this map as bytes") + } + + val width = map.size + val height = map[0].size + + val blocks = sequence { + for (y in 0.. + val key = block.params.key + + val blockId = dict[key]?.toUByte() + ?: throw IllegalArgumentException("Dictionary does not contain key $key") + + if (blockId != runValue || run == Int.MAX_VALUE) { + if (run > 0 && runValue != null) { + run.toByteArray().forEach { yield(it) } + yield(runValue!!.toByte()) + } + run = 1 + runValue = blockId + } else { + run++ + } + } + + run.toByteArray().forEach { yield(it) } + yield(runValue!!.toByte()) + } + + return result.toList().toByteArray() + } + + private fun decompressMap( + bytes: ByteArray, + dict: List, + itemsRepository: ItemsRepository, + ): Array> { + val version = bytes.first().toUByte() + require(version == MAP_SAVE_VERSION) + + val width = ByteBuffer.wrap(bytes, 1, Int.SIZE_BYTES).getInt() + val height = ByteBuffer.wrap(bytes, 1 + Int.SIZE_BYTES, Int.SIZE_BYTES).getInt() + + val blocks = buildList { + for (i in 1 + (Int.SIZE_BYTES shl 1)..bytes.lastIndex step Int.SIZE_BYTES + 1) { + val run = ByteBuffer.wrap(bytes, i, Int.SIZE_BYTES).getInt() + val blockId = bytes[i + Int.SIZE_BYTES].toUByte().toInt() + + for (j in 0.. + Array(height) { y -> + blocks[x + y * width] + } + } + } + + private fun internalLoadMap( + savesPath: String, + ): Pair>, Array>> { + val dict = Gdx.files.absolute("$savesPath$DICT_FILE").readString().split("\n") + + val foreMap: Array> + with(GZIPInputStream(Gdx.files.absolute("$savesPath$FOREMAP_FILE").read())) { + foreMap = decompressMap(readBytes(), dict, itemsRepository) + close() + } + + val backMap: Array> + with(GZIPInputStream(Gdx.files.absolute("$savesPath$BACKMAP_FILE").read())) { + backMap = decompressMap(readBytes(), dict, itemsRepository) + close() + } + + return foreMap to backMap + } + + private fun saveMap(gameWorld: GameWorld, savesPath: String) { + val fullForeMap = gameWorld.foreMap + val fullBackMap = gameWorld.backMap + + val dict = buildBlocksDictionary(fullForeMap, fullBackMap) + + saveDict(Gdx.files.absolute("$savesPath$DICT_FILE"), dict) + + with(GZIPOutputStream(Gdx.files.absolute("$savesPath$FOREMAP_FILE").write(false))) { + write(compressMap(fullForeMap, dict)) + close() + } + + with(GZIPOutputStream(Gdx.files.absolute("$savesPath$BACKMAP_FILE").write(false))) { + write(compressMap(fullBackMap, dict)) + close() + } + } + + override fun save( + gameDataFolder: String, + dropController: DropController, + mobController: MobController, + containerController: ContainerController, + gameWorld: GameWorld, + ) { + val savesPath = "$gameDataFolder$SAVES_DIR" + + Gdx.files.absolute(savesPath).mkdirs() + + val dropFile = Gdx.files.absolute("$savesPath$DROP_FILE") + val mobsFile = Gdx.files.absolute("$savesPath$MOBS_FILE") + val containersFile = Gdx.files.absolute("$savesPath$CONTAINERS_FILE") + + val dropBytes = ProtoBuf.encodeToByteArray(dropControllerMapper.mapSaveData(dropController as DropController)) + val mobsBytes = ProtoBuf.encodeToByteArray(mobControllerMapper.mapSaveData(mobController as MobController)) + val containersBytes = + ProtoBuf.encodeToByteArray(containerControllerMapper.mapSaveData(containerController as ContainerController)) + + dropFile.writeBytes(dropBytes, false) + mobsFile.writeBytes(mobsBytes, false) + containersFile.writeBytes(containersBytes, false) + + saveMap(gameWorld, savesPath) + } + + override fun loadMap(gameDataFolder: String): GameMapSaveData { + val savesPath = "$gameDataFolder$SAVES_DIR" + val (foreMap, backMap) = internalLoadMap(savesPath) + return GameMapSaveData(foreMap, backMap) + } + + override fun loadContainerController( + gameDataFolder: String, + containerWorldAdapter: ContainerWorldAdapter, + containerFactory: ContainerFactory, + dropAdapter: DropAdapter, + ): ContainerController { + val savesPath = "$gameDataFolder$SAVES_DIR" + val containersFile = Gdx.files.absolute("$savesPath$CONTAINERS_FILE") + val containersBytes = containersFile.readBytes() + + return ProtoBuf.decodeFromByteArray( + containersBytes, + ).let { saveData -> + containerControllerMapper.mapContainerController( + saveDataDto = saveData, + containerWorldAdapter = containerWorldAdapter, + containerFactory = containerFactory, + dropAdapter = dropAdapter, + ) + } + } + + override fun loadDropController(gameDataFolder: String, dropWorldAdapter: DropWorldAdapter): DropController { + val savesPath = "$gameDataFolder$SAVES_DIR" + val dropFile = Gdx.files.absolute("$savesPath$DROP_FILE") + val dropBytes = dropFile.readBytes() + + return ProtoBuf.decodeFromByteArray(dropBytes) + .let { saveData -> + dropControllerMapper.mapDropController( + saveDataDto = saveData, + dropWorldAdapter = dropWorldAdapter, + ) + } + } + + override fun loadMobController(gameDataFolder: String, mobWorldAdapter: MobWorldAdapter): MobController { + val savesPath = "$gameDataFolder$SAVES_DIR" + val mobsFile = Gdx.files.absolute("$savesPath$MOBS_FILE") + val mobsBytes = mobsFile.readBytes() + + return ProtoBuf.decodeFromByteArray(mobsBytes) + .let { saveData -> + mobControllerMapper.mapMobController( + saveDataDto = saveData, + mobWorldAdapter = mobWorldAdapter, + ) + } + } + + override fun exists(gameDataFolder: String): Boolean { + val savesPath = "$gameDataFolder$SAVES_DIR" + + return Gdx.files.absolute("$savesPath$DROP_FILE").exists() && + Gdx.files.absolute("$savesPath$MOBS_FILE").exists() && + Gdx.files.absolute("$savesPath$CONTAINERS_FILE").exists() && + Gdx.files.absolute("$savesPath$DICT_FILE").exists() && + Gdx.files.absolute("$savesPath$FOREMAP_FILE").exists() && + Gdx.files.absolute("$savesPath$BACKMAP_FILE").exists() + } + + companion object { + private const val MAP_SAVE_VERSION: UByte = 2u + + private const val SAVES_DIR = "/saves" + private const val DROP_FILE = "/drop.dat" + private const val MOBS_FILE = "/mobs.dat" + private const val CONTAINERS_FILE = "/containers.dat" + private const val DICT_FILE = "/dict" + private const val FOREMAP_FILE = "/foremap.dat.gz" + private const val BACKMAP_FILE = "/backmap.dat.gz" + } +} diff --git a/core/domain/assets/build.gradle.kts b/core/domain/assets/build.gradle.kts new file mode 100644 index 0000000..9d3840d --- /dev/null +++ b/core/domain/assets/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useLibgdx() + useDagger() +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/GameAssetsHolder.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/GameAssetsHolder.kt new file mode 100644 index 0000000..08a9618 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/GameAssetsHolder.kt @@ -0,0 +1,70 @@ +package ru.fredboy.cavedroid.domain.assets + +import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.g2d.BitmapFont +import com.badlogic.gdx.graphics.g2d.Sprite +import com.badlogic.gdx.graphics.g2d.TextureRegion +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.model.MobSprite +import ru.fredboy.cavedroid.domain.assets.model.TouchButton +import ru.fredboy.cavedroid.domain.assets.repository.AssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.BlockAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.BlockDamageAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.FontAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.ItemsAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.MobAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.TextureRegionsAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.TouchButtonsAssetsRepository +import javax.inject.Inject + +@Reusable +class GameAssetsHolder @Inject constructor( + private val blockAssetsRepository: BlockAssetsRepository, + private val blockDamageAssetsRepository: BlockDamageAssetsRepository, + private val fontAssetsRepository: FontAssetsRepository, + private val itemsAssetsRepository: ItemsAssetsRepository, + private val mobAssetsRepository: MobAssetsRepository, + private val textureRegionsAssetsRepository: TextureRegionsAssetsRepository, + private val touchButtonsAssetsRepository: TouchButtonsAssetsRepository, +) { + + private val repositories = sequenceOf( + blockAssetsRepository, + blockDamageAssetsRepository, + fontAssetsRepository, + itemsAssetsRepository, + mobAssetsRepository, + textureRegionsAssetsRepository, + touchButtonsAssetsRepository, + ) + + fun initializeRepository() { + repositories.forEach(AssetsRepository::initialize) + } + + fun dispose() { + repositories.forEach(AssetsRepository::dispose) + } + + fun getBlockTexture(textureName: String): Texture = blockAssetsRepository.getBlockTexture(textureName) + + fun getItemTexture(textureName: String): Texture = itemsAssetsRepository.getItemTexture(textureName) + + fun getBlockDamageFrameCount(): Int = blockDamageAssetsRepository.damageStages + + fun getBlockDamageSprite(stage: Int): Sprite = blockDamageAssetsRepository.getBlockDamageSprite(stage) + + fun getStringWidth(string: String): Float = fontAssetsRepository.getStringWidth(string) + + fun getStringHeight(string: String): Float = fontAssetsRepository.getStringHeight(string) + + fun getFont(): BitmapFont = fontAssetsRepository.getFont() + + fun getPlayerSprites(): MobSprite.Player = mobAssetsRepository.getPlayerSprites() + + fun getPigSprites(): MobSprite.Pig = mobAssetsRepository.getPigSprites() + + fun getTouchButtons(): Map = touchButtonsAssetsRepository.getTouchButtons() + + fun getTextureRegionByName(name: String): TextureRegion? = textureRegionsAssetsRepository.getTextureRegionByName(name) +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/model/MobSprite.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/model/MobSprite.kt new file mode 100644 index 0000000..8805db4 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/model/MobSprite.kt @@ -0,0 +1,31 @@ +package ru.fredboy.cavedroid.domain.assets.model + +import com.badlogic.gdx.graphics.g2d.Sprite + +sealed interface MobSprite { + + data class Player( + val head: Sprite, + val hand: Sprite, + val body: Sprite, + val leg: Sprite, + ) : MobSprite { + fun getBodyRelativeX() = 2 + + fun getBodyRelativeY() = 8 + + fun getLegsRelativeY() = 20 + } + + data class Pig( + val headAndBody: Sprite, + val leg: Sprite, + ) : MobSprite { + + fun getLeftLegRelativeX(directionIndex: Int) = 9 - directionIndex * 9 + + fun getRightLegRelativeX(directionIndex: Int) = 21 - (9 * directionIndex) + + fun getLegsRelativeY() = 12 + } +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/model/TouchButton.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/model/TouchButton.kt new file mode 100644 index 0000000..5b1d1eb --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/model/TouchButton.kt @@ -0,0 +1,9 @@ +package ru.fredboy.cavedroid.domain.assets.model + +import com.badlogic.gdx.math.Rectangle + +data class TouchButton( + val rectangle: Rectangle, + val code: Int, + val isMouse: Boolean, +) diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/AssetsRepository.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/AssetsRepository.kt new file mode 100644 index 0000000..671b4c2 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/AssetsRepository.kt @@ -0,0 +1,61 @@ +package ru.fredboy.cavedroid.domain.assets.repository + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.g2d.Sprite +import com.badlogic.gdx.graphics.g2d.TextureRegion +import com.badlogic.gdx.utils.Disposable +import java.util.* + +abstract class AssetsRepository : Disposable { + + protected val loadedTextures = LinkedList() + + protected fun loadTexture(path: String): Texture = Texture(Gdx.files.internal(path)).also { texture -> + loadedTextures.add(texture) + } + + protected fun flippedRegion( + texture: Texture, + x: Int, + y: Int, + width: Int, + height: Int, + ): TextureRegion = TextureRegion(texture, x, y + height, width, -height) + + protected fun flippedSprite( + texture: Texture, + ): Sprite = Sprite(texture).apply { flip(false, true) } + + protected fun flippedSprite( + texture: Texture, + x: Int, + y: Int, + width: Int, + height: Int, + ): Sprite = Sprite(flippedRegion(texture, x, y, width, height)) + + protected fun resolveTexture( + textureName: String, + lookupPath: String, + cache: MutableMap, + ): Texture { + val cached = cache[textureName] + + if (cached != null) { + return cached + } + + val texture = loadTexture("$lookupPath/$textureName.png") + cache[textureName] = texture + + return texture + } + + override fun dispose() { + loadedTextures.forEach(Texture::dispose) + loadedTextures.clear() + } + + abstract fun initialize() +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/BlockAssetsRepository.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/BlockAssetsRepository.kt new file mode 100644 index 0000000..153a05a --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/BlockAssetsRepository.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.domain.assets.repository + +import com.badlogic.gdx.graphics.Texture + +abstract class BlockAssetsRepository : AssetsRepository() { + + abstract fun getBlockTexture(textureName: String): Texture +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/BlockDamageAssetsRepository.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/BlockDamageAssetsRepository.kt new file mode 100644 index 0000000..82c6669 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/BlockDamageAssetsRepository.kt @@ -0,0 +1,10 @@ +package ru.fredboy.cavedroid.domain.assets.repository + +import com.badlogic.gdx.graphics.g2d.Sprite + +abstract class BlockDamageAssetsRepository : AssetsRepository() { + + abstract val damageStages: Int + + abstract fun getBlockDamageSprite(stage: Int): Sprite +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/FontAssetsRepository.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/FontAssetsRepository.kt new file mode 100644 index 0000000..0b70687 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/FontAssetsRepository.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.domain.assets.repository + +import com.badlogic.gdx.graphics.g2d.BitmapFont + +abstract class FontAssetsRepository : AssetsRepository() { + + abstract fun getStringWidth(string: String): Float + + abstract fun getStringHeight(string: String): Float + + abstract fun getFont(): BitmapFont +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/ItemsAssetsRepository.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/ItemsAssetsRepository.kt new file mode 100644 index 0000000..5a98779 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/ItemsAssetsRepository.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.domain.assets.repository + +import com.badlogic.gdx.graphics.Texture + +abstract class ItemsAssetsRepository : AssetsRepository() { + + abstract fun getItemTexture(textureName: String): Texture +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/MobAssetsRepository.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/MobAssetsRepository.kt new file mode 100644 index 0000000..ecc8d52 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/MobAssetsRepository.kt @@ -0,0 +1,10 @@ +package ru.fredboy.cavedroid.domain.assets.repository + +import ru.fredboy.cavedroid.domain.assets.model.MobSprite + +abstract class MobAssetsRepository : AssetsRepository() { + + abstract fun getPlayerSprites(): MobSprite.Player + + abstract fun getPigSprites(): MobSprite.Pig +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/TextureRegionsAssetsRepository.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/TextureRegionsAssetsRepository.kt new file mode 100644 index 0000000..a26c8fa --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/TextureRegionsAssetsRepository.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.domain.assets.repository + +import com.badlogic.gdx.graphics.g2d.TextureRegion + +abstract class TextureRegionsAssetsRepository : AssetsRepository() { + + abstract fun getTextureRegionByName(name: String): TextureRegion? +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/TouchButtonsAssetsRepository.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/TouchButtonsAssetsRepository.kt new file mode 100644 index 0000000..ebf6373 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/repository/TouchButtonsAssetsRepository.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.domain.assets.repository + +import ru.fredboy.cavedroid.domain.assets.model.TouchButton + +abstract class TouchButtonsAssetsRepository : AssetsRepository() { + + abstract fun getTouchButtons(): Map +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/DisposeAssetsUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/DisposeAssetsUseCase.kt new file mode 100644 index 0000000..e4fd256 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/DisposeAssetsUseCase.kt @@ -0,0 +1,15 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class DisposeAssetsUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke() { + gameAssetsHolder.dispose() + } +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockDamageFrameCountUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockDamageFrameCountUseCase.kt new file mode 100644 index 0000000..d2adb23 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockDamageFrameCountUseCase.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class GetBlockDamageFrameCountUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke(): Int = gameAssetsHolder.getBlockDamageFrameCount() +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockDamageSpriteUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockDamageSpriteUseCase.kt new file mode 100644 index 0000000..e3be345 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockDamageSpriteUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import com.badlogic.gdx.graphics.g2d.Sprite +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class GetBlockDamageSpriteUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun get(stage: Int): Sprite = gameAssetsHolder.getBlockDamageSprite(stage) +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockTextureUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockTextureUseCase.kt new file mode 100644 index 0000000..a7760f9 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetBlockTextureUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import com.badlogic.gdx.graphics.Texture +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class GetBlockTextureUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun get(name: String): Texture = gameAssetsHolder.getBlockTexture(name) +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetFontUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetFontUseCase.kt new file mode 100644 index 0000000..177b196 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetFontUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import com.badlogic.gdx.graphics.g2d.BitmapFont +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class GetFontUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke(): BitmapFont = gameAssetsHolder.getFont() +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetItemTextureUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetItemTextureUseCase.kt new file mode 100644 index 0000000..9043a36 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetItemTextureUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import com.badlogic.gdx.graphics.Texture +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class GetItemTextureUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun get(name: String): Texture = gameAssetsHolder.getItemTexture(name) +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetPigSpritesUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetPigSpritesUseCase.kt new file mode 100644 index 0000000..025dac3 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetPigSpritesUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import ru.fredboy.cavedroid.domain.assets.model.MobSprite +import javax.inject.Inject + +@Reusable +class GetPigSpritesUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke(): MobSprite.Pig = gameAssetsHolder.getPigSprites() +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetPlayerSpritesUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetPlayerSpritesUseCase.kt new file mode 100644 index 0000000..acfcafd --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetPlayerSpritesUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import ru.fredboy.cavedroid.domain.assets.model.MobSprite +import javax.inject.Inject + +@Reusable +class GetPlayerSpritesUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke(): MobSprite.Player = gameAssetsHolder.getPlayerSprites() +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetStringHeightUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetStringHeightUseCase.kt new file mode 100644 index 0000000..50031ee --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetStringHeightUseCase.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class GetStringHeightUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke(string: String): Float = gameAssetsHolder.getStringHeight(string) +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetStringWidthUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetStringWidthUseCase.kt new file mode 100644 index 0000000..03ca640 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetStringWidthUseCase.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class GetStringWidthUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke(string: String): Float = gameAssetsHolder.getStringWidth(string) +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetTextureRegionByNameUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetTextureRegionByNameUseCase.kt new file mode 100644 index 0000000..2614ec0 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetTextureRegionByNameUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import com.badlogic.gdx.graphics.g2d.TextureRegion +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class GetTextureRegionByNameUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun get(name: String): TextureRegion? = gameAssetsHolder.getTextureRegionByName(name) +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetTouchButtonsUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetTouchButtonsUseCase.kt new file mode 100644 index 0000000..ba7124d --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/GetTouchButtonsUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import ru.fredboy.cavedroid.domain.assets.model.TouchButton +import javax.inject.Inject + +@Reusable +class GetTouchButtonsUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke(): Map = gameAssetsHolder.getTouchButtons() +} diff --git a/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/InitializeAssetsUseCase.kt b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/InitializeAssetsUseCase.kt new file mode 100644 index 0000000..659e769 --- /dev/null +++ b/core/domain/assets/src/main/kotlin/ru/fredboy/cavedroid/domain/assets/usecase/InitializeAssetsUseCase.kt @@ -0,0 +1,15 @@ +package ru.fredboy.cavedroid.domain.assets.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import javax.inject.Inject + +@Reusable +class InitializeAssetsUseCase @Inject constructor( + private val gameAssetsHolder: GameAssetsHolder, +) { + + operator fun invoke() { + gameAssetsHolder.initializeRepository() + } +} diff --git a/core/domain/configuration/build.gradle.kts b/core/domain/configuration/build.gradle.kts new file mode 100644 index 0000000..86474f1 --- /dev/null +++ b/core/domain/configuration/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useCommonModule() +} diff --git a/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/model/CameraContext.kt b/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/model/CameraContext.kt new file mode 100644 index 0000000..c907ba5 --- /dev/null +++ b/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/model/CameraContext.kt @@ -0,0 +1,17 @@ +package ru.fredboy.cavedroid.domain.configuration.model + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.OrthographicCamera +import com.badlogic.gdx.math.Rectangle + +data class CameraContext( + val viewport: Rectangle, + val camera: OrthographicCamera, +) { + + fun xOnViewport(x: Int) = viewport.width / Gdx.graphics.width * x.toFloat() + + fun yOnViewport(y: Int) = viewport.height / Gdx.graphics.height * y.toFloat() + + fun getViewportCoordinates(x: Int, y: Int): Pair = xOnViewport(x) to yOnViewport(y) +} diff --git a/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/repository/ApplicationContextRepository.kt b/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/repository/ApplicationContextRepository.kt new file mode 100644 index 0000000..fd0070e --- /dev/null +++ b/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/repository/ApplicationContextRepository.kt @@ -0,0 +1,34 @@ +package ru.fredboy.cavedroid.domain.configuration.repository + +interface ApplicationContextRepository { + + fun isDebug(): Boolean + + fun isTouch(): Boolean + + fun isFullscreen(): Boolean + + fun useDynamicCamera(): Boolean + + fun getGameDirectory(): String + + fun getWidth(): Float + + fun getHeight(): Float + + fun getScreenScale(): Int + + fun setTouch(isTouch: Boolean) + + fun setFullscreen(fullscreen: Boolean) + + fun setUseDynamicCamera(use: Boolean) + + fun setGameDirectory(path: String) + + fun setWidth(width: Float) + + fun setHeight(height: Float) + + fun setScreenScale(scale: Int) +} diff --git a/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/repository/GameContextRepository.kt b/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/repository/GameContextRepository.kt new file mode 100644 index 0000000..5277a86 --- /dev/null +++ b/core/domain/configuration/src/main/kotlin/ru/fredboy/cavedroid/domain/configuration/repository/GameContextRepository.kt @@ -0,0 +1,25 @@ +package ru.fredboy.cavedroid.domain.configuration.repository + +import ru.fredboy.cavedroid.common.model.Joystick +import ru.fredboy.cavedroid.domain.configuration.model.CameraContext + +interface GameContextRepository { + + fun isLoadGame(): Boolean + + fun shouldShowInfo(): Boolean + + fun shouldShowMap(): Boolean + + fun getJoystick(): Joystick + + fun getCameraContext(): CameraContext + + fun setShowInfo(show: Boolean) + + fun setShowMap(show: Boolean) + + fun setJoystick(joystick: Joystick) + + fun setCameraContext(cameraContext: CameraContext) +} diff --git a/core/domain/items/build.gradle.kts b/core/domain/items/build.gradle.kts new file mode 100644 index 0000000..9d3840d --- /dev/null +++ b/core/domain/items/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useLibgdx() + useDagger() +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/Block.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/Block.kt new file mode 100644 index 0000000..6432fb4 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/Block.kt @@ -0,0 +1,203 @@ +package ru.fredboy.cavedroid.domain.items.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.fredboy.cavedroid.common.utils.BLOCK_SIZE_PX +import ru.fredboy.cavedroid.common.utils.colorFromHexString +import ru.fredboy.cavedroid.domain.items.model.item.Item +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +@OptIn(ExperimentalContracts::class) +sealed class Block { + + abstract val params: CommonBlockParams + + val width: Float get() = BLOCK_SIZE_PX - params.collisionMargins.left - params.collisionMargins.right + val height: Float get() = BLOCK_SIZE_PX - params.collisionMargins.top - params.collisionMargins.bottom + + val spriteWidth: Float get() = BLOCK_SIZE_PX - params.spriteMargins.left - params.spriteMargins.right + val spriteHeight: Float get() = BLOCK_SIZE_PX - params.spriteMargins.top - params.spriteMargins.bottom + + protected var animation: Array? = null + + private var _sprite: Sprite? = null + get() { + return animation?.get(currentAnimationFrame) ?: field + } + + open 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 = params.key.hashCode() + + override fun equals(other: Any?): Boolean = 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 isContainer(): Boolean { + contract { returns(true) implies (this@Block is Container) } + return this is Container + } + + fun isFurnace(): Boolean { + contract { returns(true) implies (this@Block is Furnace) } + return this is Furnace + } + + fun isChest(): Boolean { + contract { returns(true) implies (this@Block is Chest) } + return this is Chest + } + + fun isNone(): Boolean { + contract { returns(true) implies (this@Block is None) } + return this is None + } + + fun getRectangle(x: Int, y: Int): Rectangle = Rectangle( + /* x = */ x * BLOCK_SIZE_PX + params.collisionMargins.left, + /* y = */ y * BLOCK_SIZE_PX + params.collisionMargins.top, + /* width = */ width, + /* height = */ height, + ) + + sealed class Container : Block() + + data class None( + override val params: CommonBlockParams, + ) : Block() + + data class Normal( + override val params: CommonBlockParams, + ) : Block() + + data class Furnace( + override val params: CommonBlockParams, + ) : Container() { + + override val sprite: Sprite + get() = getSprite(false) + + private fun getSprite(isActive: Boolean): Sprite = animation?.let { animation -> + if (isActive) { + animation[1] + } else { + animation[0] + } + } ?: sprite + + fun draw(spriter: SpriteBatch, x: Float, y: Float, isActive: Boolean) { + getSprite(isActive).apply { + setBounds( + /* x = */ x + params.spriteMargins.left, + /* y = */ y + params.spriteMargins.top, + /* width = */ spriteWidth, + /* height = */ spriteHeight, + ) + draw(spriter) + } + } + } + + data class Chest( + override val params: CommonBlockParams, + ) : Container() + + data class Slab( + override val params: CommonBlockParams, + val fullBlockKey: String, + 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() + + companion object { + private const val ANIMATION_FRAME_DURATION_MS = 100L + } +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockAnimationInfo.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockAnimationInfo.kt new file mode 100644 index 0000000..098bf97 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockAnimationInfo.kt @@ -0,0 +1,5 @@ +package ru.fredboy.cavedroid.domain.items.model.block + +data class BlockAnimationInfo( + val framesCount: Int, +) diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockDropInfo.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockDropInfo.kt new file mode 100644 index 0000000..e60757f --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockDropInfo.kt @@ -0,0 +1,6 @@ +package ru.fredboy.cavedroid.domain.items.model.block + +data class BlockDropInfo( + val itemKey: String, + val count: Int, +) diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockMargins.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockMargins.kt new file mode 100644 index 0000000..6fd2d24 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/BlockMargins.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.items.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/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/CommonBlockParams.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/CommonBlockParams.kt new file mode 100644 index 0000000..4ce0f2e --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/block/CommonBlockParams.kt @@ -0,0 +1,23 @@ +package ru.fredboy.cavedroid.domain.items.model.block + +import com.badlogic.gdx.graphics.Texture +import ru.fredboy.cavedroid.domain.items.model.item.Item + +data class CommonBlockParams( + 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?, + val damage: Int, + val tint: String?, + val isFallable: Boolean, +) diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/craft/CraftingRecipe.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/craft/CraftingRecipe.kt new file mode 100644 index 0000000..838345f --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/craft/CraftingRecipe.kt @@ -0,0 +1,6 @@ +package ru.fredboy.cavedroid.domain.items.model.craft + +data class CraftingRecipe( + val input: List, + val output: CraftingResult, +) diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/craft/CraftingResult.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/craft/CraftingResult.kt new file mode 100644 index 0000000..b4f5350 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/craft/CraftingResult.kt @@ -0,0 +1,11 @@ +package ru.fredboy.cavedroid.domain.items.model.craft + +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item + +data class CraftingResult( + val item: Item, + val amount: Int, +) { + fun toInventoryItem() = InventoryItem(item, amount) +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/inventory/Inventory.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/inventory/Inventory.kt new file mode 100644 index 0000000..28ea06f --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/inventory/Inventory.kt @@ -0,0 +1,91 @@ +package ru.fredboy.cavedroid.domain.items.model.inventory + +import ru.fredboy.cavedroid.domain.items.model.item.Item + +class Inventory @JvmOverloads constructor( + val size: Int, + private val fallbackItem: Item.None, + initialItems: List? = null, +) { + + private val _items: Array = Array(size) { index -> + initialItems?.getOrNull(index) ?: fallbackItem.toInventoryItem() + } + + val items get() = _items.asList() as MutableList + + fun getAvailableSlotForItem(item: Item): Int { + for (i in _items.indices) { + val inventoryItem = _items[i] + + if (item == inventoryItem.item && inventoryItem.canBeAdded()) { + return i + } + } + + for (i in _items.indices) { + val inventoryItem = _items[i] + + if (inventoryItem.item.isNone()) { + return i + } + } + + return -1 + } + + fun canPickItem(item: Item): Boolean = getAvailableSlotForItem(item) >= 0 + + fun addItem(item: Item) { + _items.copyInto( + destination = _items, + destinationOffset = 1, + startIndex = 0, + endIndex = size - 1, + ) + + _items[0] = item.toInventoryItem(item.params.maxStack) + } + + /** + * @return true if all amount was picked up + */ + fun pickUpItem(pickingItem: InventoryItem): Boolean { + val slot = getAvailableSlotForItem(pickingItem.item).takeIf { it >= 0 } ?: return false + val inventoryItem = _items[slot] + + if (inventoryItem.item == pickingItem.item) { + if (inventoryItem.canBeAdded(pickingItem.amount)) { + inventoryItem.add(pickingItem.amount) + return true + } else { + val addCount = inventoryItem.item.params.maxStack - inventoryItem.amount + inventoryItem.add(addCount) + pickingItem.subtract(addCount) + return false + } + } else { + _items[slot] = pickingItem + return true + } + } + + @JvmOverloads + fun decreaseItemAmount(slot: Int, count: Int = 1) { + val item = _items[slot] + item.subtract(count) + if (item.amount <= 0) { + _items[slot] = fallbackItem.toInventoryItem() + } + } + + fun clear() { + for (i in _items.indices) { + _items[i] = fallbackItem.toInventoryItem() + } + } + + companion object { + private const val SAVE_DATA_VERSION = 1 + } +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/inventory/InventoryItem.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/inventory/InventoryItem.kt new file mode 100644 index 0000000..80ed021 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/inventory/InventoryItem.kt @@ -0,0 +1,134 @@ +package ru.fredboy.cavedroid.domain.items.model.inventory + +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.BitmapFont +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import ru.fredboy.cavedroid.common.utils.drawSprite +import ru.fredboy.cavedroid.common.utils.drawString +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.items.model.item.Item +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +class InventoryItem( + val item: Item, + _amount: Int = 1, +) { + + var amount = _amount + set(value) { + field = if (value < 0) { + 0 + } else { + value + } + } + + fun add(count: Int = 1) { + if (count > 0 && Int.MAX_VALUE - count < amount) { + throw IllegalArgumentException("$amount + $count exceeds Int.MAX_VALUE") + } + + amount += count + } + + fun subtract(count: Int = 1) { + if (count < 0) { + throw IllegalArgumentException("Can't subtract negative amount") + } + + add(-count) + } + + fun canBeAdded(count: Int = 1): Boolean = amount + count <= item.params.maxStack + + private fun drawAmountText( + spriteBatch: SpriteBatch, + font: BitmapFont, + text: String, + x: Float, + y: Float, + ) { + spriteBatch.drawString(font, text, x + 1, y + 1, Color.BLACK) + spriteBatch.drawString(font, text, x, y, Color.WHITE) + } + + fun drawSelected( + spriteBatch: SpriteBatch, + font: BitmapFont, + x: Float, + y: Float, + getStringWidth: (String) -> Float, + getStringHeight: (String) -> 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, + font = font, + text = amountString, + x = x + 10f - getStringWidth(amountString) + 1f, + y = y + 10f - getStringHeight(amountString) + 1f, + ) + } + + fun draw( + spriteBatch: SpriteBatch, + shapeRenderer: ShapeRenderer, + font: BitmapFont, + x: Float, + y: Float, + getStringWidth: (String) -> Float, + getStringHeight: (String) -> Float, + ) { + if (item.isNone()) { + return + } + + val sprite = item.sprite + val placeableMarginTop = (item as? Item.Placeable)?.block?.params?.spriteMargins?.top ?: 0 + val placeableMarginLeft = (item as? Item.Placeable)?.block?.params?.spriteMargins?.left ?: 0 + spriteBatch.drawSprite(sprite, x + placeableMarginLeft, y + placeableMarginTop) + + if (amount < 2) { + return + } + + 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, + font = font, + text = amountString, + x = x + 1.px - getStringWidth(amountString), + y = y + 1.px - getStringHeight(amountString), + ) + } + } + + companion object { + @OptIn(ExperimentalContracts::class) + fun InventoryItem?.isNoneOrNull(): Boolean { + contract { returns(false) implies (this@isNoneOrNull != null) } + return this?.item == null || this.item.isNone() + } + } +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/item/CommonItemParams.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/item/CommonItemParams.kt new file mode 100644 index 0000000..878eaee --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/item/CommonItemParams.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.domain.items.model.item + +import ru.fredboy.cavedroid.common.model.SpriteOrigin + +data class CommonItemParams( + val key: String, + val name: String, + val inHandSpriteOrigin: SpriteOrigin, + val maxStack: Int, + val burningTimeMs: Long?, + val smeltProductKey: String?, +) diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/item/Item.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/item/Item.kt new file mode 100644 index 0000000..aaf85b3 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/model/item/Item.kt @@ -0,0 +1,144 @@ +package ru.fredboy.cavedroid.domain.items.model.item + +import com.badlogic.gdx.graphics.g2d.Sprite +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract +import ru.fredboy.cavedroid.domain.items.model.block.Block as DomainBlockModel + +@OptIn(ExperimentalContracts::class) +sealed class Item { + + abstract val params: CommonItemParams + abstract val sprite: Sprite + + override fun hashCode(): Int = params.key.hashCode() + + override fun equals(other: Any?): Boolean = 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 Usable) } + return this is Usable + } + + fun isFood(): Boolean { + contract { returns(true) implies (this@Item is Food) } + return this is Food + } + + @JvmOverloads + fun toInventoryItem(amount: Int = 1): InventoryItem = 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 Placeable : Item() { + abstract val block: DomainBlockModel + 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 Usable( + override val params: CommonItemParams, + override val sprite: Sprite, + val useActionKey: String, + ) : Item() + + data class Block( + override val params: CommonItemParams, + override val block: DomainBlockModel, + ) : Placeable() + + data class Slab( + override val params: CommonItemParams, + val topPartBlock: DomainBlockModel.Slab, + val bottomPartBlock: DomainBlockModel.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 Food( + override val params: CommonItemParams, + override val sprite: Sprite, + val heal: Int, + ) : Item() +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/repository/ItemsRepository.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/repository/ItemsRepository.kt new file mode 100644 index 0000000..76ea648 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/repository/ItemsRepository.kt @@ -0,0 +1,27 @@ +package ru.fredboy.cavedroid.domain.items.repository + +import com.badlogic.gdx.utils.Disposable +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item + +interface ItemsRepository : Disposable { + + val fallbackBlock: Block.None + + val fallbackItem: Item.None + + fun initialize() + + fun getItemByKey(key: String): Item + + fun getItemByIndex(index: Int): Item + + fun getBlockByKey(key: String): Block + + fun getBlocksByType(type: Class): List + + fun getCraftingResult(input: List): InventoryItem + + fun getAllItems(): Collection +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/DisposeItemsRepositoryUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/DisposeItemsRepositoryUseCase.kt new file mode 100644 index 0000000..fd6e1dd --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/DisposeItemsRepositoryUseCase.kt @@ -0,0 +1,15 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class DisposeItemsRepositoryUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun invoke() { + itemsRepository.dispose() + } +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetBlockByKeyUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetBlockByKeyUseCase.kt new file mode 100644 index 0000000..e9fbeb5 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetBlockByKeyUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class GetBlockByKeyUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun get(key: String): Block = itemsRepository.getBlockByKey(key) +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetBlocksByTypeUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetBlocksByTypeUseCase.kt new file mode 100644 index 0000000..9b9b41c --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetBlocksByTypeUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class GetBlocksByTypeUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun get(type: Class): List = itemsRepository.getBlocksByType(type) +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetCraftingResultUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetCraftingResultUseCase.kt new file mode 100644 index 0000000..d6a1e04 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetCraftingResultUseCase.kt @@ -0,0 +1,15 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class GetCraftingResultUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun get(input: List): InventoryItem = itemsRepository.getCraftingResult(input) +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetFallbackBlockUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetFallbackBlockUseCase.kt new file mode 100644 index 0000000..e2f5df6 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetFallbackBlockUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class GetFallbackBlockUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun invoke(): Block.None = itemsRepository.fallbackBlock +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetFallbackItemUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetFallbackItemUseCase.kt new file mode 100644 index 0000000..23f3535 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetFallbackItemUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class GetFallbackItemUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun invoke(): Item.None = itemsRepository.fallbackItem +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetItemByIndexUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetItemByIndexUseCase.kt new file mode 100644 index 0000000..0e9eea0 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetItemByIndexUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class GetItemByIndexUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun get(index: Int): Item = itemsRepository.getItemByIndex(index) +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetItemByKeyUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetItemByKeyUseCase.kt new file mode 100644 index 0000000..df8958a --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/GetItemByKeyUseCase.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class GetItemByKeyUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun get(key: String): Item = itemsRepository.getItemByKey(key) +} diff --git a/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/InitializeItemsRepositoryUseCase.kt b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/InitializeItemsRepositoryUseCase.kt new file mode 100644 index 0000000..55c2813 --- /dev/null +++ b/core/domain/items/src/main/kotlin/ru/fredboy/cavedroid/domain/items/usecase/InitializeItemsRepositoryUseCase.kt @@ -0,0 +1,15 @@ +package ru.fredboy.cavedroid.domain.items.usecase + +import dagger.Reusable +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import javax.inject.Inject + +@Reusable +class InitializeItemsRepositoryUseCase @Inject constructor( + private val itemsRepository: ItemsRepository, +) { + + operator fun invoke() { + itemsRepository.initialize() + } +} diff --git a/core/domain/menu/build.gradle.kts b/core/domain/menu/build.gradle.kts new file mode 100644 index 0000000..9d3840d --- /dev/null +++ b/core/domain/menu/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useLibgdx() + useDagger() +} diff --git a/core/domain/menu/src/main/kotlin/ru/fredboy/cavedroid/domain/menu/model/MenuButton.kt b/core/domain/menu/src/main/kotlin/ru/fredboy/cavedroid/domain/menu/model/MenuButton.kt new file mode 100644 index 0000000..d467d38 --- /dev/null +++ b/core/domain/menu/src/main/kotlin/ru/fredboy/cavedroid/domain/menu/model/MenuButton.kt @@ -0,0 +1,36 @@ +package ru.fredboy.cavedroid.domain.menu.model + +sealed class MenuButton { + + abstract val label: String + abstract val isVisible: Boolean + abstract val actionKey: String + abstract val isEnabled: Boolean + + data class Simple( + override val label: String, + override val isVisible: Boolean, + override val actionKey: String, + override val isEnabled: Boolean, + ) : MenuButton() + + sealed class Option : MenuButton() { + abstract val optionKeys: List + } + + data class BooleanOption( + override val label: String, + override val isVisible: Boolean, + override val actionKey: String, + override val isEnabled: Boolean, + override val optionKeys: List, + ) : Option() + + data class NumericalOption( + override val label: String, + override val isVisible: Boolean, + override val actionKey: String, + override val isEnabled: Boolean, + override val optionKeys: List, + ) : Option() +} diff --git a/core/domain/menu/src/main/kotlin/ru/fredboy/cavedroid/domain/menu/repository/MenuButtonRepository.kt b/core/domain/menu/src/main/kotlin/ru/fredboy/cavedroid/domain/menu/repository/MenuButtonRepository.kt new file mode 100644 index 0000000..38d7e36 --- /dev/null +++ b/core/domain/menu/src/main/kotlin/ru/fredboy/cavedroid/domain/menu/repository/MenuButtonRepository.kt @@ -0,0 +1,17 @@ +package ru.fredboy.cavedroid.domain.menu.repository + +import com.badlogic.gdx.utils.Disposable +import ru.fredboy.cavedroid.domain.menu.model.MenuButton + +interface MenuButtonRepository : Disposable { + + fun initialize() + + fun getButtonsForMenu(menuKey: String): Map? + + fun setCurrentMenu(menuKey: String) + + fun getCurrentMenuButtons(): Map? + + fun setButtonEnabled(menuKey: String, buttonKey: String, enabled: Boolean) +} diff --git a/core/domain/save/build.gradle.kts b/core/domain/save/build.gradle.kts new file mode 100644 index 0000000..8ebc8cb --- /dev/null +++ b/core/domain/save/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useLibgdx() + useDagger() + + useModule(":core:domain:items") + useModule(":core:domain:assets") + useModule(":core:domain:world") + + useModule(":core:entity:container") + useModule(":core:entity:drop") + useModule(":core:entity:mob") + + useModule(":core:game:controller:container") + useModule(":core:game:controller:drop") + useModule(":core:game:controller:mob") + useModule(":core:game:world") +} diff --git a/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/model/GameMapSaveData.kt b/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/model/GameMapSaveData.kt new file mode 100644 index 0000000..3364559 --- /dev/null +++ b/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/model/GameMapSaveData.kt @@ -0,0 +1,22 @@ +package ru.fredboy.cavedroid.domain.save.model + +import ru.fredboy.cavedroid.domain.items.model.block.Block + +class GameMapSaveData( + private var foreMap: Array>?, + private var backMap: Array>?, +) { + fun retrieveForeMap(): Array> { + val value = requireNotNull(foreMap) + foreMap = null + return value + } + + fun retrieveBackMap(): Array> { + val value = requireNotNull(backMap) + backMap = null + return value + } + + fun isEmpty() = foreMap == null && backMap == null +} diff --git a/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/model/GameSaveData.kt b/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/model/GameSaveData.kt new file mode 100644 index 0000000..be0b262 --- /dev/null +++ b/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/model/GameSaveData.kt @@ -0,0 +1,34 @@ +package ru.fredboy.cavedroid.domain.save.model + +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController + +class GameSaveData( + private var mobController: MobController?, + private var dropController: DropController?, + private var containerController: ContainerController?, +) { + + fun retrieveMobsController(): MobController { + val value = requireNotNull(mobController) + mobController = null + return value + } + + fun retrieveDropController(): DropController { + val value = requireNotNull(dropController) + dropController = null + return value + } + + fun retrieveContainerController(): ContainerController { + val value = requireNotNull(containerController) + containerController = null + return value + } + + fun isEmpty(): Boolean = mobController == null && + dropController == null && + containerController == null +} diff --git a/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/repository/SaveDataRepository.kt b/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/repository/SaveDataRepository.kt new file mode 100644 index 0000000..9c0e371 --- /dev/null +++ b/core/domain/save/src/main/kotlin/ru/fredboy/cavedroid/domain/save/repository/SaveDataRepository.kt @@ -0,0 +1,46 @@ +package ru.fredboy.cavedroid.domain.save.repository + +import ru.fredboy.cavedroid.domain.save.model.GameMapSaveData +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerFactory +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerWorldAdapter +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter +import ru.fredboy.cavedroid.entity.drop.abstraction.DropWorldAdapter +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld + +interface SaveDataRepository { + + fun save( + gameDataFolder: String, + dropController: DropController, + mobController: MobController, + containerController: ContainerController, + gameWorld: GameWorld, + ) + + fun loadMap( + gameDataFolder: String, + ): GameMapSaveData + + fun loadContainerController( + gameDataFolder: String, + containerWorldAdapter: ContainerWorldAdapter, + containerFactory: ContainerFactory, + dropAdapter: DropAdapter, + ): ContainerController + + fun loadDropController( + gameDataFolder: String, + dropWorldAdapter: DropWorldAdapter, + ): DropController + + fun loadMobController( + gameDataFolder: String, + mobWorldAdapter: MobWorldAdapter, + ): MobController + + fun exists(gameDataFolder: String): Boolean +} diff --git a/core/domain/world/build.gradle.kts b/core/domain/world/build.gradle.kts new file mode 100644 index 0000000..ef847ce --- /dev/null +++ b/core/domain/world/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useModule(":core:domain:items") +} diff --git a/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/listener/OnBlockDestroyedListener.kt b/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/listener/OnBlockDestroyedListener.kt new file mode 100644 index 0000000..df35a6c --- /dev/null +++ b/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/listener/OnBlockDestroyedListener.kt @@ -0,0 +1,9 @@ +package ru.fredboy.cavedroid.domain.world.listener + +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.world.model.Layer + +fun interface OnBlockDestroyedListener { + + fun onBlockDestroyed(block: Block, x: Int, y: Int, layer: Layer, withDrop: Boolean) +} diff --git a/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/listener/OnBlockPlacedListener.kt b/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/listener/OnBlockPlacedListener.kt new file mode 100644 index 0000000..79e970f --- /dev/null +++ b/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/listener/OnBlockPlacedListener.kt @@ -0,0 +1,9 @@ +package ru.fredboy.cavedroid.domain.world.listener + +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.world.model.Layer + +fun interface OnBlockPlacedListener { + + fun onBlockPlaced(block: Block, x: Int, y: Int, layer: Layer) +} diff --git a/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/model/Layer.kt b/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/model/Layer.kt new file mode 100644 index 0000000..cf6f841 --- /dev/null +++ b/core/domain/world/src/main/kotlin/ru/fredboy/cavedroid/domain/world/model/Layer.kt @@ -0,0 +1,6 @@ +package ru.fredboy.cavedroid.domain.world.model + +enum class Layer(val z: Int) { + FOREGROUND(0), + BACKGROUND(1), +} diff --git a/core/entity/container/build.gradle.kts b/core/entity/container/build.gradle.kts new file mode 100644 index 0000000..1cc022f --- /dev/null +++ b/core/entity/container/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useModule(":core:domain:items") + useModule(":core:domain:world") +} diff --git a/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/abstraction/ContainerFactory.kt b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/abstraction/ContainerFactory.kt new file mode 100644 index 0000000..44ddf4c --- /dev/null +++ b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/abstraction/ContainerFactory.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.entity.container.abstraction + +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.entity.container.model.Container + +interface ContainerFactory { + fun createContainer(type: Block.Container): Container +} diff --git a/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/abstraction/ContainerWorldAdapter.kt b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/abstraction/ContainerWorldAdapter.kt new file mode 100644 index 0000000..a954d26 --- /dev/null +++ b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/abstraction/ContainerWorldAdapter.kt @@ -0,0 +1,23 @@ +package ru.fredboy.cavedroid.entity.container.abstraction + +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.world.listener.OnBlockDestroyedListener +import ru.fredboy.cavedroid.domain.world.listener.OnBlockPlacedListener +import ru.fredboy.cavedroid.entity.container.model.ContainerCoordinates +import kotlin.reflect.KClass + +interface ContainerWorldAdapter { + + fun checkContainerAtCoordinates( + coordinates: ContainerCoordinates, + requiredType: KClass, + ): Boolean + + fun addOnBlockDestroyedListener(listener: OnBlockDestroyedListener) + + fun addOnBlockPlacedListener(listener: OnBlockPlacedListener) + + fun removeOnBlockDestroyedListener(listener: OnBlockDestroyedListener) + + fun removeOnBlockPlacedListener(listener: OnBlockPlacedListener) +} diff --git a/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Chest.kt b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Chest.kt new file mode 100644 index 0000000..22a1c90 --- /dev/null +++ b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Chest.kt @@ -0,0 +1,26 @@ +package ru.fredboy.cavedroid.entity.container.model + +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase + +class Chest( + fallbackItem: Item.None, + initialItems: List? = null, +) : Container( + size = SIZE, + fallbackItem = fallbackItem, + initialItems = initialItems, +) { + + override val type get() = Block.Chest::class + + override fun update(itemByKey: GetItemByKeyUseCase) { + // no-op + } + + companion object { + private const val SIZE = 27 + } +} diff --git a/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Container.kt b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Container.kt new file mode 100644 index 0000000..9e5e549 --- /dev/null +++ b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Container.kt @@ -0,0 +1,23 @@ +package ru.fredboy.cavedroid.entity.container.model + +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.inventory.Inventory +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import kotlin.reflect.KClass + +abstract class Container( + val size: Int, + protected val fallbackItem: Item.None, + initialItems: List? = null, +) { + + val inventory = Inventory(size, fallbackItem, initialItems) + + val items get() = inventory.items + + abstract val type: KClass + + abstract fun update(itemByKey: GetItemByKeyUseCase) +} diff --git a/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/ContainerCoordinates.kt b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/ContainerCoordinates.kt new file mode 100644 index 0000000..79460fe --- /dev/null +++ b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/ContainerCoordinates.kt @@ -0,0 +1,24 @@ +package ru.fredboy.cavedroid.entity.container.model + +data class ContainerCoordinates( + val x: Int, + val y: Int, + val z: Int, +) { + override fun toString(): String = "($x;$y;$z)" + + companion object { + + fun fromString(string: String): ContainerCoordinates { + val xyz = string.trim('(', ')').split(';').map(Integer::valueOf) + if (xyz.size != 3) { + throw IllegalArgumentException("Invalid ContainerCoordinates format") + } + return ContainerCoordinates( + x = xyz[0], + y = xyz[1], + z = xyz[2], + ) + } + } +} diff --git a/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Furnace.kt b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Furnace.kt new file mode 100644 index 0000000..9e3a822 --- /dev/null +++ b/core/entity/container/src/main/kotlin/ru/fredboy/cavedroid/entity/container/model/Furnace.kt @@ -0,0 +1,147 @@ +package ru.fredboy.cavedroid.entity.container.model + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.math.MathUtils +import com.badlogic.gdx.utils.TimeUtils +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem.Companion.isNoneOrNull +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase + +class Furnace( + fallbackItem: Item.None, + initialItems: List? = null, +) : Container( + size = SIZE, + fallbackItem = fallbackItem, + initialItems = initialItems, +) { + + override val type get() = Block.Furnace::class + + var fuel: InventoryItem + get() = items[FUEL_INDEX] + set(value) { + items[FUEL_INDEX] = value + } + + var input: InventoryItem + get() = items[INPUT_INDEX] + set(value) { + items[INPUT_INDEX] = value + } + + var result: InventoryItem + get() = items[RESULT_INDEX] + set(value) { + items[RESULT_INDEX] = value + } + + val isActive: Boolean get() = currentFuel != null + + var currentFuel: Item? = null + set(value) { + currentFuelKey = value?.params?.key + field = value + } + + var currentFuelKey: String? = null + + var startBurnTimeMs = 0L + var smeltStarTimeMs = 0L + + var burnProgress = 0f + set(value) { + field = MathUtils.clamp(value, 0f, 1f) + } + var smeltProgress = 0f + set(value) { + field = MathUtils.clamp(value, 0f, 1f) + } + + fun canSmelt(): Boolean = (result.isNoneOrNull() || (result.item.params.key == input.item.params.smeltProductKey)) && + !input.isNoneOrNull() && + input.item.params.smeltProductKey != null && + (!fuel.isNoneOrNull() || burnProgress > 0f) + + private fun startBurning() { + requireNotNull(fuel.item.params.burningTimeMs) { "Cant start burning without fuel" } + currentFuel = fuel.item + fuel.subtract() + if (fuel.amount <= 0) { + fuel = fallbackItem.toInventoryItem() + } + startBurnTimeMs = TimeUtils.millis() + burnProgress = 0f + } + + override fun update(itemByKey: GetItemByKeyUseCase) { + if (currentFuel?.isNone() == true) { + currentFuel = null + } + + currentFuel?.let { curFuel -> + val burningTimeMs = curFuel.params.burningTimeMs ?: run { + Gdx.app.error(TAG, "Burning item has no burning time. Item : ${curFuel.params.key}") + return + } + + if (TimeUtils.timeSinceMillis(startBurnTimeMs).toDouble() / burningTimeMs >= 0.01) { + burnProgress += 0.01f + startBurnTimeMs = TimeUtils.millis() + } + } + + if (currentFuel?.isNone() == false && burnProgress >= 1f) { + if (canSmelt()) { + startBurning() + } else { + currentFuel = null + burnProgress = 0f + smeltProgress = 0f + } + } + + if (!canSmelt()) { + return + } + if (currentFuel == null && !fuel.isNoneOrNull()) { + startBurning() + smeltStarTimeMs = startBurnTimeMs + smeltProgress = 0f + } + + if ((TimeUtils.timeSinceMillis(smeltStarTimeMs).toDouble() / SMELTING_TIME_MS) >= 0.01) { + smeltProgress += 0.01f + smeltStarTimeMs = TimeUtils.millis() + } + + if (isActive && smeltProgress >= 1f) { + val productKey = requireNotNull(input.item.params.smeltProductKey) + val res = itemByKey[productKey] + if (result.isNoneOrNull()) { + result = res.toInventoryItem() + } else { + result.add() + } + input.subtract() + if (input.amount <= 0) { + input = fallbackItem.toInventoryItem() + } + smeltStarTimeMs = TimeUtils.millis() + smeltProgress = 0f + } + } + + companion object { + private const val SIZE = 3 + private const val TAG = "Furnace" + + const val FUEL_INDEX = 0 + const val INPUT_INDEX = 1 + const val RESULT_INDEX = 2 + + const val SMELTING_TIME_MS = 10000L + } +} diff --git a/core/entity/drop/build.gradle.kts b/core/entity/drop/build.gradle.kts new file mode 100644 index 0000000..1cc022f --- /dev/null +++ b/core/entity/drop/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useModule(":core:domain:items") + useModule(":core:domain:world") +} diff --git a/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/abstraction/DropAdapter.kt b/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/abstraction/DropAdapter.kt new file mode 100644 index 0000000..1c443d8 --- /dev/null +++ b/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/abstraction/DropAdapter.kt @@ -0,0 +1,11 @@ +package ru.fredboy.cavedroid.entity.drop.abstraction + +import ru.fredboy.cavedroid.domain.items.model.inventory.Inventory +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem + +interface DropAdapter { + + fun dropInventory(x: Float, y: Float, inventory: Inventory) + + fun dropItems(x: Float, y: Float, items: List) +} diff --git a/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/abstraction/DropWorldAdapter.kt b/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/abstraction/DropWorldAdapter.kt new file mode 100644 index 0000000..d8cc303 --- /dev/null +++ b/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/abstraction/DropWorldAdapter.kt @@ -0,0 +1,15 @@ +package ru.fredboy.cavedroid.entity.drop.abstraction + +import ru.fredboy.cavedroid.domain.world.listener.OnBlockDestroyedListener +import ru.fredboy.cavedroid.domain.world.listener.OnBlockPlacedListener + +interface DropWorldAdapter { + + fun addOnBlockDestroyedListener(listener: OnBlockDestroyedListener) + + fun addOnBlockPlacedListener(listener: OnBlockPlacedListener) + + fun removeOnBlockDestroyedListener(listener: OnBlockDestroyedListener) + + fun removeOnBlockPlacedListener(listener: OnBlockPlacedListener) +} diff --git a/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/model/Drop.kt b/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/model/Drop.kt new file mode 100644 index 0000000..3075c06 --- /dev/null +++ b/core/entity/drop/src/main/kotlin/ru/fredboy/cavedroid/entity/drop/model/Drop.kt @@ -0,0 +1,47 @@ +package ru.fredboy.cavedroid.entity.drop.model + +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.fredboy.cavedroid.common.utils.BLOCK_SIZE_PX +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item + +class Drop( + x: Float, + y: Float, + val inventoryItem: InventoryItem, +) : Rectangle(x, y, DROP_SIZE, DROP_SIZE) { + + constructor(x: Float, y: Float, item: Item, amount: Int = 1) : this(x, y, InventoryItem(item, amount)) + + val velocity = getInitialVelocity() + + var isPickedUp = false + + val item get() = inventoryItem.item + + val amount get() = inventoryItem.amount + + fun canMagnetTo(rectangle: Rectangle): Boolean { + val magnetArea = getMagnetArea() + return Intersector.overlaps(magnetArea, rectangle) + } + + private fun getMagnetArea(): Rectangle = Rectangle( + /* x = */ x - MAGNET_DISTANCE, + /* y = */ y - MAGNET_DISTANCE, + /* width = */ width + MAGNET_DISTANCE * 2, + /* height = */ height + MAGNET_DISTANCE * 2, + ) + + companion object { + private fun getInitialVelocity(): Vector2 = Vector2(MathUtils.random(-100f, 100f), -100f) + + private const val MAGNET_DISTANCE = 8f + + const val MAGNET_VELOCITY = 256f + const val DROP_SIZE = BLOCK_SIZE_PX / 2 + } +} diff --git a/core/entity/mob/build.gradle.kts b/core/entity/mob/build.gradle.kts new file mode 100644 index 0000000..fee0959 --- /dev/null +++ b/core/entity/mob/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useModule(":core:domain:items") + useModule(":core:domain:assets") +} diff --git a/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/MobBehavior.kt b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/MobBehavior.kt new file mode 100644 index 0000000..fde74da --- /dev/null +++ b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/MobBehavior.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.entity.mob.abstraction + +import ru.fredboy.cavedroid.entity.mob.model.Mob + +interface MobBehavior { + + fun update(mob: Mob, worldAdapter: MobWorldAdapter, delta: Float) +} diff --git a/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/MobWorldAdapter.kt b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/MobWorldAdapter.kt new file mode 100644 index 0000000..e774e25 --- /dev/null +++ b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/MobWorldAdapter.kt @@ -0,0 +1,25 @@ +package ru.fredboy.cavedroid.entity.mob.abstraction + +import com.badlogic.gdx.math.Vector2 +import ru.fredboy.cavedroid.domain.items.model.block.Block + +interface MobWorldAdapter { + + val height: Int + + val width: Int + + fun setForegroundBlock(x: Int, y: Int, block: Block) + + fun setBackgroundBlock(x: Int, y: Int, block: Block) + + fun getForegroundBlock(x: Int, y: Int): Block + + fun getBackgroundBlock(x: Int, y: Int): Block + + fun destroyForegroundBlock(x: Int, y: Int, shouldDrop: Boolean) + + fun destroyBackgroundBlock(x: Int, y: Int, shouldDrop: Boolean) + + fun findSpawnPoint(): Vector2 +} diff --git a/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/PlayerAdapter.kt b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/PlayerAdapter.kt new file mode 100644 index 0000000..ec8dcb3 --- /dev/null +++ b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/abstraction/PlayerAdapter.kt @@ -0,0 +1,9 @@ +package ru.fredboy.cavedroid.entity.mob.abstraction + +interface PlayerAdapter { + val x: Float + val y: Float + + val width: Float + val height: Float +} diff --git a/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Direction.kt b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Direction.kt new file mode 100644 index 0000000..ed8204f --- /dev/null +++ b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Direction.kt @@ -0,0 +1,16 @@ +package ru.fredboy.cavedroid.entity.mob.model + +import com.badlogic.gdx.math.MathUtils + +enum class Direction( + val index: Int, + val basis: Int, +) { + LEFT(0, -1), + RIGHT(1, 1), + ; + + companion object { + fun random() = if (MathUtils.randomBoolean()) LEFT else RIGHT + } +} diff --git a/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/FallingBlock.kt b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/FallingBlock.kt new file mode 100644 index 0000000..d35c95d --- /dev/null +++ b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/FallingBlock.kt @@ -0,0 +1,33 @@ +package ru.fredboy.cavedroid.entity.mob.model + +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.entity.mob.abstraction.MobBehavior + +class FallingBlock( + val block: Block, + x: Float, + y: Float, + behavior: MobBehavior, +) : Mob(x, y, 1.px, 1.px, Direction.RIGHT, Int.MAX_VALUE, behavior) { + + init { + velocity.y = 1f + } + + override val speed get() = 0f + + override fun changeDir() = Unit + + override fun jump() = Unit + + override fun draw( + spriteBatch: SpriteBatch, + x: Float, + y: Float, + delta: Float, + ) { + block.draw(spriteBatch, x, y) + } +} diff --git a/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Mob.kt b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Mob.kt new file mode 100644 index 0000000..fda88e7 --- /dev/null +++ b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Mob.kt @@ -0,0 +1,198 @@ +package ru.fredboy.cavedroid.entity.mob.model + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.math.MathUtils +import com.badlogic.gdx.math.Rectangle +import com.badlogic.gdx.math.Vector2 +import com.badlogic.gdx.utils.Timer +import ru.fredboy.cavedroid.common.utils.bl +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.entity.mob.abstraction.MobBehavior +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import kotlin.math.abs + +abstract class Mob( + x: Float, + y: Float, + width: Float, + height: Float, + var direction: Direction, + val maxHealth: Int, + val behavior: MobBehavior, +) : Rectangle(x, y, width, height) { + + private var resetTakeDamageTask: ResetTakeDamageTask? = null + + var velocity = Vector2() + protected set + + val mapX get() = (x + width / 2).bl + val upperMapY get() = y.bl + val middleMapY get() = (y + height / 2).bl + val lowerMapY get() = (y + height).bl + + var animDelta = ANIMATION_SPEED + var anim = 0f + + var isDead = false + protected set + + var canJump = false + + var isFlyMode = false + + var health = maxHealth + + var takingDamage = false + set(value) { + field = value + + if (value) { + var resetTask = resetTakeDamageTask + if (resetTask != null && resetTask.isScheduled) { + resetTask.cancel() + } else { + resetTask = ResetTakeDamageTask() + } + Timer.schedule(resetTask, DAMAGE_TINT_TIMEOUT_S) + resetTakeDamageTask = resetTask + } + } + + protected val tintColor: Color + get() = if (takingDamage) { + DAMAGE_TINT_COLOR + } else { + Color.WHITE + } + + abstract val speed: Float + + private fun isAnimationIncreasing(): Boolean = anim > 0 && animDelta > 0 || anim < 0 && animDelta < 0 + + private fun checkHealth() { + health = MathUtils.clamp(health, 0, maxHealth) + + if (health <= 0) { + kill() + } + } + + protected fun updateAnimation(delta: Float) { + val velocityMultiplier = abs(velocity.x) / speed + val animMultiplier = (if (velocityMultiplier == 0f) 1f else velocityMultiplier) * delta + val maxAnim = ANIMATION_RANGE * (if (velocityMultiplier == 0f) 1f else velocityMultiplier) + + if (velocity.x != 0f || abs(anim) > animDelta * animMultiplier) { + anim += animDelta * animMultiplier + } else { + anim = 0f + } + + if (anim > maxAnim) { + anim = maxAnim + animDelta = -ANIMATION_SPEED + } else if (anim < -maxAnim) { + anim = -maxAnim + animDelta = ANIMATION_SPEED + } + + if (velocity.x == 0f && isAnimationIncreasing()) { + animDelta = -animDelta + } + } + + protected fun switchDir() { + direction = if (looksLeft()) { + Direction.RIGHT + } else { + Direction.LEFT + } + } + + fun looksLeft() = direction == Direction.LEFT + + fun looksRight() = direction == Direction.RIGHT + + fun kill() { + isDead = true + } + + open fun damage(damage: Int) { + if (damage == 0) { + return + } + + if (damage < 0) { + Gdx.app.error(TAG, "Damage can't be negative!") + return + } + + if (health <= Int.MIN_VALUE + damage) { + health = Int.MIN_VALUE + damage + } + + health -= damage + checkHealth() + + takingDamage = true + } + + fun heal(heal: Int) { + if (heal < 0) { + Gdx.app.error(TAG, "Heal can't be negative") + return + } + + if (health >= Int.MAX_VALUE - heal) { + health = Int.MAX_VALUE - heal + } + + health += heal + checkHealth() + } + + fun getHitBox(): Rectangle = Rectangle( + /* x = */ x - HIT_RANGE, + /* y = */ y + HIT_RANGE, + /* width = */ width + HIT_RANGE * 2f, + /* height = */ height + HIT_RANGE * 2f, + ) + + fun update(mobWorldAdapter: MobWorldAdapter, delta: Float) { + behavior.update(this, mobWorldAdapter, delta) + } + + open fun getDropItems( + itemByKey: GetItemByKeyUseCase, + ): List = emptyList() + + abstract fun draw(spriteBatch: SpriteBatch, x: Float, y: Float, delta: Float) + + abstract fun changeDir() + + abstract fun jump() + + private inner class ResetTakeDamageTask : Timer.Task() { + override fun run() { + takingDamage = false + } + } + + companion object { + private const val TAG = "Mob" + + @JvmStatic + protected val ANIMATION_SPEED = 360f + + protected const val ANIMATION_RANGE = 60f + + private const val HIT_RANGE = 8f + + private const val DAMAGE_TINT_TIMEOUT_S = 0.5f + private val DAMAGE_TINT_COLOR = Color((0xff8080 shl 8) or 0xFF) + } +} diff --git a/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Pig.kt b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Pig.kt new file mode 100644 index 0000000..4a03b4d --- /dev/null +++ b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Pig.kt @@ -0,0 +1,74 @@ +package ru.fredboy.cavedroid.entity.mob.model + +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.math.Vector2 +import ru.fredboy.cavedroid.common.utils.drawSprite +import ru.fredboy.cavedroid.domain.assets.model.MobSprite +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.entity.mob.abstraction.MobBehavior + +class Pig( + private val sprite: MobSprite.Pig, + x: Float, + y: Float, + behavior: MobBehavior, +) : Mob(x, y, WIDTH, HEIGHT, Direction.random(), MAX_HEALTH, behavior) { + + override val speed get() = SPEED + + override fun changeDir() { + switchDir() + velocity = Vector2(direction.basis * speed, 0f) + } + + override fun jump() { + velocity.y = JUMP_VELOCITY + } + + override fun damage(damage: Int) { + super.damage(damage) + + if (damage > 0) { + if (canJump) { + jump() + } + } + } + + override fun getDropItems(itemByKey: GetItemByKeyUseCase): List = listOf(itemByKey["porkchop_raw"].toInventoryItem()) + + override fun draw( + spriteBatch: SpriteBatch, + x: Float, + y: Float, + delta: Float, + ) { + updateAnimation(delta) + + val leftLegX = x + sprite.getLeftLegRelativeX(direction.index) + val rightLegX = x + sprite.getRightLegRelativeX(direction.index) + val legY = y + sprite.getLegsRelativeY() + + sprite.leg.setOrigin(sprite.leg.width / 2, 0f) + sprite.leg.setFlip(looksRight(), sprite.leg.isFlipY) + sprite.headAndBody.setFlip(looksRight(), sprite.leg.isFlipY) + + val backgroundTintColor = tintColor.cpy().sub(Color(0xAAAAAA shl 8)) + + spriteBatch.drawSprite(sprite.leg, leftLegX, legY, -anim, tint = backgroundTintColor) + spriteBatch.drawSprite(sprite.leg, rightLegX, legY, -anim, tint = backgroundTintColor) + spriteBatch.drawSprite(sprite.headAndBody, x, y, tint = tintColor) + spriteBatch.drawSprite(sprite.leg, leftLegX, legY, anim, tint = tintColor) + spriteBatch.drawSprite(sprite.leg, rightLegX, legY, anim, tint = tintColor) + } + + companion object { + private const val WIDTH = 25f + private const val HEIGHT = 18f + private const val SPEED = 48f + private const val JUMP_VELOCITY = -133.332f + private const val MAX_HEALTH = 10 + } +} diff --git a/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Player.kt b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Player.kt new file mode 100644 index 0000000..46b0355 --- /dev/null +++ b/core/entity/mob/src/main/kotlin/ru/fredboy/cavedroid/entity/mob/model/Player.kt @@ -0,0 +1,278 @@ +package ru.fredboy.cavedroid.entity.mob.model + +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.math.MathUtils +import com.badlogic.gdx.math.Vector2 +import ru.fredboy.cavedroid.common.utils.applyOrigin +import ru.fredboy.cavedroid.common.utils.drawSprite +import ru.fredboy.cavedroid.domain.assets.model.MobSprite +import ru.fredboy.cavedroid.domain.items.model.inventory.Inventory +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.entity.mob.abstraction.MobBehavior + +class Player( + private val sprite: MobSprite.Player, + private val getFallbackItem: GetFallbackItemUseCase, + x: Float, + y: Float, + behavior: MobBehavior, +) : Mob(x, y, WIDTH, HEIGHT, Direction.random(), MAX_HEALTH, behavior) { + + var spawnPoint: Vector2? = null + + var inventory = Inventory( + size = INVENTORY_SIZE, + fallbackItem = getFallbackItem(), + ) + + var gameMode = 0 + var swim = false + + var cursorX = 0 + var cursorY = 0 + + var controlMode = ControlMode.WALK + var blockDamage = 0f + + var isHitting = false + var isHittingWithDamage = false + + var hitAnim = 0f + var hitAnimDelta = ANIMATION_SPEED + + var headRotation = 0f + + private var _activeSlot = 0 + + var activeSlot + get() = _activeSlot + set(value) { + if (value in 0.. 0 && canJump) { + jump() + } + } + + override fun getDropItems(itemByKey: GetItemByKeyUseCase): List = inventory.items + + override fun draw(spriteBatch: SpriteBatch, x: Float, y: Float, delta: Float) { + updateAnimation(delta) + + with(sprite) { + hand.setFlip(looksRight(), hand.isFlipY) + leg.setFlip(looksRight(), leg.isFlipY) + head.setFlip(looksRight(), head.isFlipY) + body.setFlip(looksRight(), body.isFlipY) + + hand.setOrigin(hand.width / 2f, 0f) + leg.setOrigin(leg.width / 2f, 0f) + head.setOrigin(head.width / 2, head.height) + + var backHandAnim: Float + var frontHandAnim: Float + + val rightHandAnim = getRightHandAnim(delta) + + if (looksLeft()) { + backHandAnim = rightHandAnim + frontHandAnim = anim + } else { + backHandAnim = -anim + frontHandAnim = -rightHandAnim + } + + val backgroundTintColor = tintColor.cpy().sub(Color(0xAAAAAA shl 8)) + + hand.color = backgroundTintColor + spriteBatch.drawSprite(hand, x + getBodyRelativeX(), y + getBodyRelativeY(), backHandAnim) + + if (looksLeft()) { + drawItem(spriteBatch, x, y, -backHandAnim) + } + + leg.color = backgroundTintColor + spriteBatch.drawSprite(leg, x + getBodyRelativeX(), y + getLegsRelativeY(), anim) + + leg.color = tintColor + spriteBatch.drawSprite(leg, x + getBodyRelativeX(), y + getLegsRelativeY(), -anim) + + head.color = tintColor + spriteBatch.drawSprite(head, x, y, headRotation) + + body.color = tintColor + spriteBatch.drawSprite(body, x + getBodyRelativeX(), y + getBodyRelativeY()) + + if (looksRight()) { + drawItem(spriteBatch, x, y, frontHandAnim) + } + + hand.color = tintColor + spriteBatch.drawSprite(hand, x + getBodyRelativeX(), y + getBodyRelativeY(), frontHandAnim) + } + } + + fun respawn(spawnPoint: Vector2) { + this.spawnPoint = spawnPoint + + x = spawnPoint.x + y = spawnPoint.y + + velocity.setZero() + isDead = false + heal(maxHealth) + } + + fun startHitting(withDamage: Boolean = true) { + if (isHitting) { + return + } + + isHitting = true + isHittingWithDamage = withDamage + hitAnim = HIT_ANIMATION_RANGE.endInclusive + hitAnimDelta = ANIMATION_SPEED + } + + fun stopHitting() { + blockDamage = 0f + isHitting = false + } + + fun decreaseCurrentItemCount(amount: Int = 1) { + if (gameMode == 1) { + return + } + + activeItem.subtract(amount) + + if (activeItem.amount <= 0) { + setCurrentInventorySlotItem(getFallbackItem()) + } + } + + fun setCurrentInventorySlotItem(item: Item) { + inventory.items[activeSlot] = item.toInventoryItem() + } + + private fun drawItem(spriteBatch: SpriteBatch, x: Float, y: Float, handAnim: Float) { + val item = activeItem.item.takeIf { !it.isNone() } ?: return + val itemSprite = item.sprite + val isSmallSprite = !item.isTool() || item.isShears() + val originalWidth = itemSprite.width + val originalHeight = itemSprite.height + val handLength = sprite.hand.height + + if (isSmallSprite) { + itemSprite.setSize(SMALL_ITEM_SIZE, SMALL_ITEM_SIZE) + } + + val spriteOrigin = item.params.inHandSpriteOrigin + val handMultiplier = -direction.basis + val xOffset = (-1 + direction.index) * itemSprite.width + SMALL_ITEM_SIZE / 2 + + handMultiplier * itemSprite.width * spriteOrigin.x + val yOffset = if (!isSmallSprite) -itemSprite.height / 2 else 0f + + val rotate = handAnim + HAND_ITEM_ANGLE_DEG + + if (item.isTool()) { + itemSprite.rotate90(looksLeft()) + } + + val itemX = x + handLength * MathUtils.sin(handMultiplier * handAnim * MathUtils.degRad) + xOffset + val itemY = y + handLength * MathUtils.cos(handMultiplier * handAnim * MathUtils.degRad) + yOffset + + if (looksLeft()) { + itemSprite.setFlip(!item.isTool(), itemSprite.isFlipY) + itemSprite.applyOrigin(spriteOrigin.getFlipped(flipX = true, flipY = false)) + } else { + itemSprite.setFlip(item.isTool(), itemSprite.isFlipY) + itemSprite.applyOrigin(spriteOrigin) + } + + itemSprite.rotation = -handMultiplier * rotate + itemSprite.setPosition(itemX, itemY) + itemSprite.draw(spriteBatch) + + // dont forget to reset + itemSprite.setFlip(false, itemSprite.isFlipY) + itemSprite.rotation = 0f + itemSprite.setOriginCenter() + itemSprite.setSize(originalWidth, originalHeight) + if (item.isTool()) { + itemSprite.rotate90(looksRight()) + } + } + + private fun getRightHandAnim(delta: Float): Float { + hitAnim -= hitAnimDelta * delta + + if (hitAnim !in HIT_ANIMATION_RANGE) { + if (isHitting) { + hitAnim = MathUtils.clamp(hitAnim, HIT_ANIMATION_RANGE.start, HIT_ANIMATION_RANGE.endInclusive) + hitAnimDelta = -hitAnimDelta + } else { + hitAnimDelta = ANIMATION_SPEED + } + } + + if (!isHitting) { + if (hitAnim < hitAnimDelta * delta) { + hitAnim = 0f + hitAnimDelta = 0f + return -anim + } + } + + return hitAnim + } + + enum class ControlMode { + WALK, + CURSOR, + } + + companion object { + const val HOTBAR_SIZE = 9 + const val INVENTORY_SIZE = 36 + + const val MAX_HEALTH = 20 + + const val WIDTH = 4f + const val HEIGHT = 30f + + const val SPEED = 69.072f + private const val JUMP_VELOCITY = -133.332f + + private val HIT_ANIMATION_RANGE = 30f..90f + + private val SMALL_ITEM_SIZE = 8f + private val HAND_ITEM_ANGLE_DEG = 30f + } +} diff --git a/core/game/controller/container/build.gradle.kts b/core/game/controller/container/build.gradle.kts new file mode 100644 index 0000000..43efa9b --- /dev/null +++ b/core/game/controller/container/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useModule(":core:domain:items") + useModule(":core:domain:world") + useModule(":core:entity:container") + useModule(":core:entity:drop") +} diff --git a/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/ContainerController.kt b/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/ContainerController.kt new file mode 100644 index 0000000..d0a6a50 --- /dev/null +++ b/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/ContainerController.kt @@ -0,0 +1,102 @@ +package ru.fredboy.cavedroid.game.controller.container + +import com.badlogic.gdx.Gdx +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.domain.world.listener.OnBlockDestroyedListener +import ru.fredboy.cavedroid.domain.world.listener.OnBlockPlacedListener +import ru.fredboy.cavedroid.domain.world.model.Layer +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerFactory +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerWorldAdapter +import ru.fredboy.cavedroid.entity.container.model.Container +import ru.fredboy.cavedroid.entity.container.model.ContainerCoordinates +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter +import javax.inject.Inject + +@GameScope +class ContainerController @Inject constructor( + private val getItemByKeyUseCase: GetItemByKeyUseCase, + private val containerWorldAdapter: ContainerWorldAdapter, + private val containerFactory: ContainerFactory, + private val dropAdapter: DropAdapter, +) : OnBlockPlacedListener, + OnBlockDestroyedListener { + + val containerMap = mutableMapOf() + + val size get() = containerMap.size + + init { + containerWorldAdapter.addOnBlockPlacedListener(this) + containerWorldAdapter.addOnBlockDestroyedListener(this) + } + + private fun getContainerKey(x: Int, y: Int, z: Int): ContainerCoordinates { + return ContainerCoordinates(x, y, z) + } + + fun getContainer(x: Int, y: Int, z: Int): Container? { + return containerMap[getContainerKey(x, y, z)] + } + + fun addContainer(x: Int, y: Int, z: Int, container: Container) { + val key = getContainerKey(x, y, z) + if (containerMap.containsKey(key)) { + resetContainer(x, y, z) + } + + containerMap[key] = container + } + + private fun retrieveContainer(x: Int, y: Int, z: Int): Container? { + return containerMap.remove(getContainerKey(x, y, z)) + } + + fun resetContainer(x: Int, y: Int, z: Int) { + retrieveContainer(x, y, z) + } + + fun destroyContainer(x: Int, y: Int, z: Int) { + retrieveContainer(x, y, z)?.let { container -> + dropAdapter.dropInventory(x.px, y.px, container.inventory) + } + } + + @Suppress("unused") + fun update(delta: Float) { + val iterator = containerMap.iterator() + while (iterator.hasNext()) { + val (coordinates, container) = iterator.next() + + if (!containerWorldAdapter.checkContainerAtCoordinates(coordinates, container.type)) { + Gdx.app.log(TAG, "Removing orphaned ${container::class.simpleName} at $coordinates") + iterator.remove() + } else { + container.update(getItemByKeyUseCase) + } + } + } + + fun dispose() { + containerMap.clear() + containerWorldAdapter.removeOnBlockPlacedListener(this) + containerWorldAdapter.removeOnBlockDestroyedListener(this) + } + + override fun onBlockDestroyed(block: Block, x: Int, y: Int, layer: Layer, withDrop: Boolean) { + destroyContainer(x, y, layer.z) + } + + override fun onBlockPlaced(block: Block, x: Int, y: Int, layer: Layer) { + if (block is Block.Container) { + val container = containerFactory.createContainer(block) + addContainer(x, y, layer.z, container) + } + } + + companion object { + private const val TAG = "ContainerControllerImpl" + } +} diff --git a/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/di/ControllerContainerModule.kt b/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/di/ControllerContainerModule.kt new file mode 100644 index 0000000..3080fad --- /dev/null +++ b/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/di/ControllerContainerModule.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.game.controller.container.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerFactory +import ru.fredboy.cavedroid.game.controller.container.impl.ContainerFactoryImpl + +@Module +abstract class ControllerContainerModule { + + @Binds + internal abstract fun bindContainerFactory(impl: ContainerFactoryImpl): ContainerFactory +} diff --git a/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/impl/ContainerFactoryImpl.kt b/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/impl/ContainerFactoryImpl.kt new file mode 100644 index 0000000..cced707 --- /dev/null +++ b/core/game/controller/container/src/main/kotlin/ru/fredboy/cavedroid/game/controller/container/impl/ContainerFactoryImpl.kt @@ -0,0 +1,35 @@ +package ru.fredboy.cavedroid.game.controller.container.impl + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerFactory +import ru.fredboy.cavedroid.entity.container.model.Chest +import ru.fredboy.cavedroid.entity.container.model.Container +import ru.fredboy.cavedroid.entity.container.model.Furnace +import javax.inject.Inject + +@GameScope +internal class ContainerFactoryImpl @Inject constructor( + private val getFallbackItemUseCase: GetFallbackItemUseCase, +) : ContainerFactory { + + override fun createContainer(type: Block.Container): Container { + return when (type) { + is Block.Furnace -> createFurnace() + is Block.Chest -> createChest() + } + } + + private fun createFurnace(): Furnace { + return Furnace( + fallbackItem = getFallbackItemUseCase(), + ) + } + + private fun createChest(): Chest { + return Chest( + fallbackItem = getFallbackItemUseCase(), + ) + } +} diff --git a/core/game/controller/drop/build.gradle.kts b/core/game/controller/drop/build.gradle.kts new file mode 100644 index 0000000..d58c8e5 --- /dev/null +++ b/core/game/controller/drop/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useModule(":core:domain:items") + useModule(":core:domain:world") + useModule(":core:entity:drop") +} diff --git a/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/DropController.kt b/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/DropController.kt new file mode 100644 index 0000000..a694f52 --- /dev/null +++ b/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/DropController.kt @@ -0,0 +1,93 @@ +package ru.fredboy.cavedroid.game.controller.drop + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.blockCenterPx +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.domain.world.listener.OnBlockDestroyedListener +import ru.fredboy.cavedroid.domain.world.model.Layer +import ru.fredboy.cavedroid.entity.drop.abstraction.DropWorldAdapter +import ru.fredboy.cavedroid.entity.drop.model.Drop +import java.util.* +import javax.inject.Inject + +@GameScope +class DropController @Inject constructor( + private val itemsRepository: ItemsRepository, + private val dropWorldAdapter: DropWorldAdapter, +) : OnBlockDestroyedListener { + + private val drops = LinkedList() + + constructor( + itemsRepository: ItemsRepository, + dropWorldAdapter: DropWorldAdapter, + initialDrop: Collection, + ) : this(itemsRepository, dropWorldAdapter) { + drops.addAll(initialDrop.filterNot { drop -> drop.item.isNone() }) + } + + val size get() = drops.size + + init { + dropWorldAdapter.addOnBlockDestroyedListener(this) + } + + fun getAllDrop(): Collection { + return drops + } + + fun addDrop(drop: Drop) { + if (drop.item.isNone()) { + return + } + + drops.add(drop) + } + + fun addDrop(x: Float, y: Float, item: Item, count: Int) { + addDrop(Drop(x, y, item, count)) + } + + fun addDrop(x: Float, y: Float, inventoryItem: InventoryItem) { + addDrop(x, y, inventoryItem.item, inventoryItem.amount) + } + + fun forEach(action: (Drop) -> Unit) { + drops.forEach(action) + } + + @Suppress("unused") + fun update(delta: Float) { + val iterator = drops.iterator() + + while (iterator.hasNext()) { + val drop = iterator.next() + if (drop.isPickedUp) { + iterator.remove() + } + } + } + + fun dispose() { + drops.clear() + } + + override fun onBlockDestroyed(block: Block, x: Int, y: Int, layer: Layer, withDrop: Boolean) { + if (!withDrop) { + return + } + + val dropInfo = block.params.dropInfo ?: return + val item = itemsRepository.getItemByKey(dropInfo.itemKey).takeIf { !it.isNone() } ?: return + + addDrop( + x = x.blockCenterPx() - Drop.DROP_SIZE / 2, + y = y.blockCenterPx() - Drop.DROP_SIZE / 2, + item = item, + count = dropInfo.count, + ) + } +} diff --git a/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/di/ControllerDropModule.kt b/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/di/ControllerDropModule.kt new file mode 100644 index 0000000..3511325 --- /dev/null +++ b/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/di/ControllerDropModule.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.game.controller.drop.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter +import ru.fredboy.cavedroid.game.controller.drop.impl.DropAdapterImpl + +@Module +abstract class ControllerDropModule { + + @Binds + internal abstract fun bindDropAdapter(impl: DropAdapterImpl): DropAdapter +} diff --git a/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/impl/DropAdapterImpl.kt b/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/impl/DropAdapterImpl.kt new file mode 100644 index 0000000..beb790f --- /dev/null +++ b/core/game/controller/drop/src/main/kotlin/ru/fredboy/cavedroid/game/controller/drop/impl/DropAdapterImpl.kt @@ -0,0 +1,23 @@ +package ru.fredboy.cavedroid.game.controller.drop.impl + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.inventory.Inventory +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter +import ru.fredboy.cavedroid.game.controller.drop.DropController +import javax.inject.Inject + +@GameScope +internal class DropAdapterImpl @Inject constructor( + private val dropController: DropController, +) : DropAdapter { + + override fun dropInventory(x: Float, y: Float, inventory: Inventory) { + dropItems(x, y, inventory.items) + inventory.clear() + } + + override fun dropItems(x: Float, y: Float, items: List) { + items.forEach { item -> dropController.addDrop(x, y, item) } + } +} diff --git a/core/game/controller/mob/build.gradle.kts b/core/game/controller/mob/build.gradle.kts new file mode 100644 index 0000000..ac2a092 --- /dev/null +++ b/core/game/controller/mob/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useModule(":core:domain:assets") + useModule(":core:domain:items") + useModule(":core:domain:world:") + useModule(":core:entity:mob") +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/MobController.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/MobController.kt new file mode 100644 index 0000000..9ba2636 --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/MobController.kt @@ -0,0 +1,76 @@ +package ru.fredboy.cavedroid.game.controller.mob + +import com.badlogic.gdx.math.MathUtils +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.repository.MobAssetsRepository +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.entity.mob.model.Mob +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.behavior.PlayerMobBehavior +import java.util.* +import javax.inject.Inject + +@GameScope +class MobController @Inject constructor( + mobAssetsRepository: MobAssetsRepository, + getFallbackItemUseCase: GetFallbackItemUseCase, + private val mobWorldAdapter: MobWorldAdapter, +) { + + private val _mobs = LinkedList() + + val mobs: List get() = _mobs + + var player = Player( + sprite = mobAssetsRepository.getPlayerSprites(), + getFallbackItem = getFallbackItemUseCase, + x = 0f, + y = 0f, + behavior = PlayerMobBehavior(), + ) + + init { + respawnPlayer() + } + + fun addMob(mob: Mob) { + // TODO: Probably shouldn't add if already in the list + _mobs.add(mob) + } + + fun removeMob(mob: Mob) { + _mobs.remove(mob) + } + + fun update(delta: Float) { + mobs.forEach { mob -> + mob.update(mobWorldAdapter, delta) + } + player.update(mobWorldAdapter, delta) + } + + fun checkPlayerCursorBounds() { + with(player) { + if (gameMode == 0) { + val minCursorX = mapX - SURVIVAL_CURSOR_RANGE + val maxCursorX = mapX + SURVIVAL_CURSOR_RANGE + val minCursorY = middleMapY - SURVIVAL_CURSOR_RANGE + val maxCursorY = middleMapY + SURVIVAL_CURSOR_RANGE + + cursorX = MathUtils.clamp(cursorX, minCursorX, maxCursorX) + cursorY = MathUtils.clamp(cursorY, minCursorY, maxCursorY) + } + + cursorY = MathUtils.clamp(cursorY, 0, mobWorldAdapter.height) + } + } + + fun respawnPlayer() { + player.respawn(player.spawnPoint ?: mobWorldAdapter.findSpawnPoint()) + } + + companion object { + private const val SURVIVAL_CURSOR_RANGE = 4 + } +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/BaseMobBehavior.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/BaseMobBehavior.kt new file mode 100644 index 0000000..d1e3013 --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/BaseMobBehavior.kt @@ -0,0 +1,30 @@ +package ru.fredboy.cavedroid.game.controller.mob.behavior + +import ru.fredboy.cavedroid.entity.mob.abstraction.MobBehavior +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.entity.mob.model.Mob +import kotlin.reflect.KClass + +abstract class BaseMobBehavior( + val mobType: KClass, +) : MobBehavior { + + @Suppress("UNCHECKED_CAST") + final override fun update(mob: Mob, worldAdapter: MobWorldAdapter, delta: Float) { + if (mob::class == mobType) { + with(mob as MOB) { + updateMob(worldAdapter, delta) + } + } else { + throw IllegalArgumentException( + "Trying to update mob of type ${mob::class.simpleName} with behavior of ${mobType.simpleName}", + ) + } + } + + abstract fun MOB.updateMob(worldAdapter: MobWorldAdapter, delta: Float) + + companion object { + private const val TAG = "BaseMobBehavior" + } +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/FallingBlockMobBehavior.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/FallingBlockMobBehavior.kt new file mode 100644 index 0000000..7c7e66a --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/FallingBlockMobBehavior.kt @@ -0,0 +1,17 @@ +package ru.fredboy.cavedroid.game.controller.mob.behavior + +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.entity.mob.model.FallingBlock + +class FallingBlockMobBehavior : + BaseMobBehavior( + mobType = FallingBlock::class, + ) { + + override fun FallingBlock.updateMob(worldAdapter: MobWorldAdapter, delta: Float) { + if (velocity.isZero) { + worldAdapter.setForegroundBlock(mapX, middleMapY, block) + kill() + } + } +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/PigMobBehavior.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/PigMobBehavior.kt new file mode 100644 index 0000000..2a28dbf --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/PigMobBehavior.kt @@ -0,0 +1,21 @@ +package ru.fredboy.cavedroid.game.controller.mob.behavior + +import com.badlogic.gdx.math.MathUtils +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.entity.mob.model.Pig + +class PigMobBehavior : + BaseMobBehavior( + mobType = Pig::class, + ) { + + override fun Pig.updateMob(worldAdapter: MobWorldAdapter, delta: Float) { + if (MathUtils.randomBoolean(delta)) { + if (velocity.x != 0f) { + velocity.x = 0f + } else { + changeDir() + } + } + } +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/PlayerMobBehavior.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/PlayerMobBehavior.kt new file mode 100644 index 0000000..4db0590 --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/behavior/PlayerMobBehavior.kt @@ -0,0 +1,95 @@ +package ru.fredboy.cavedroid.game.controller.mob.behavior + +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.world.model.Layer +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.entity.mob.model.Player + +class PlayerMobBehavior : + BaseMobBehavior( + mobType = Player::class, + ) { + + private fun Block?.isHittable() = this != null && !isNone() && params.hitPoints >= 0 + + private fun MobWorldAdapter.getTargetBlockWithLayer(x: Int, y: Int): Pair? { + val foregroundBlock = getForegroundBlock(x, y) + val backgroundBlock = getBackgroundBlock(x, y) + + return when { + foregroundBlock.isHittable() -> foregroundBlock to Layer.FOREGROUND + backgroundBlock.isHittable() -> backgroundBlock to Layer.BACKGROUND + else -> null + } + } + + private fun Player.hitBlock(worldAdapter: MobWorldAdapter) { + if (!isHitting || !isHittingWithDamage) { + return + } + + val (targetBlock, targetLayer) = worldAdapter.getTargetBlockWithLayer(cursorX, cursorY) + ?: run { + stopHitting() + return + } + + if (gameMode == 0) { + if (blockDamage >= targetBlock.params.hitPoints) { + val shouldDrop = activeItem.item.let { itemInHand -> + val toolLevel = (itemInHand as? Item.Tool)?.level?.takeIf { + targetBlock.params.toolType == itemInHand.javaClass + } ?: 0 + + toolLevel >= targetBlock.params.toolLevel + } + + if (activeItem.item.isTool()) { + decreaseCurrentItemCount() + } + + when (targetLayer) { + Layer.FOREGROUND -> worldAdapter.destroyForegroundBlock(cursorX, cursorY, shouldDrop) + Layer.BACKGROUND -> worldAdapter.destroyBackgroundBlock(cursorX, cursorY, shouldDrop) + } + blockDamage = 0f + } + } else { + when (targetLayer) { + Layer.FOREGROUND -> worldAdapter.destroyForegroundBlock(cursorX, cursorY, false) + Layer.BACKGROUND -> worldAdapter.destroyBackgroundBlock(cursorX, cursorY, false) + } + stopHitting() + } + } + + override fun Player.updateMob(worldAdapter: MobWorldAdapter, delta: Float) { + hitBlock(worldAdapter) + + if (gameMode == 1) { + return + } + + val (targetBlock, _) = worldAdapter.getTargetBlockWithLayer(cursorX, cursorY) + ?.takeIf { isHitting && isHittingWithDamage } + ?: run { + blockDamage = 0f + return@updateMob + } + + var blockDamageMultiplier = 1f + (activeItem.item as? Item.Tool)?.let { currentTool -> + if (currentTool.javaClass == targetBlock.params.toolType && + currentTool.level >= targetBlock.params.toolLevel + ) { + blockDamageMultiplier = 2f * currentTool.level + } + blockDamageMultiplier *= currentTool.blockDamageMultiplier + } + + if (isHitting && isHittingWithDamage) { + blockDamage += 60f * delta * blockDamageMultiplier + } + } +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/di/MobControllerModule.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/di/MobControllerModule.kt new file mode 100644 index 0000000..9f02bff --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/di/MobControllerModule.kt @@ -0,0 +1,13 @@ +package ru.fredboy.cavedroid.game.controller.mob.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.entity.mob.abstraction.PlayerAdapter +import ru.fredboy.cavedroid.game.controller.mob.impl.PlayerAdapterImpl + +@Module +abstract class MobControllerModule { + + @Binds + internal abstract fun bindPlayerAdapter(impl: PlayerAdapterImpl): PlayerAdapter +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/factory/FallingBlockFactory.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/factory/FallingBlockFactory.kt new file mode 100644 index 0000000..2a642be --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/factory/FallingBlockFactory.kt @@ -0,0 +1,28 @@ +package ru.fredboy.cavedroid.game.controller.mob.factory + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.entity.mob.model.FallingBlock +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.controller.mob.behavior.FallingBlockMobBehavior +import javax.inject.Inject + +@GameScope +class FallingBlockFactory @Inject constructor( + private val mobController: MobController, +) { + + fun create(x: Int, y: Int, block: Block): FallingBlock { + val fallingBlock = FallingBlock( + block = block, + x = x.px, + y = y.px, + behavior = FallingBlockMobBehavior(), + ) + + mobController.addMob(fallingBlock) + + return fallingBlock + } +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/factory/PigFactory.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/factory/PigFactory.kt new file mode 100644 index 0000000..18ad1a3 --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/factory/PigFactory.kt @@ -0,0 +1,28 @@ +package ru.fredboy.cavedroid.game.controller.mob.factory + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.GameAssetsHolder +import ru.fredboy.cavedroid.entity.mob.model.Pig +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.controller.mob.behavior.PigMobBehavior +import javax.inject.Inject + +@GameScope +class PigFactory @Inject constructor( + private val mobController: MobController, + private val gameAssetsHolder: GameAssetsHolder, +) { + + fun create(x: Float, y: Float): Pig { + val pig = Pig( + x = x, + y = y, + behavior = PigMobBehavior(), + sprite = gameAssetsHolder.getPigSprites(), + ) + + mobController.addMob(pig) + + return pig + } +} diff --git a/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/impl/PlayerAdapterImpl.kt b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/impl/PlayerAdapterImpl.kt new file mode 100644 index 0000000..f1e2652 --- /dev/null +++ b/core/game/controller/mob/src/main/kotlin/ru/fredboy/cavedroid/game/controller/mob/impl/PlayerAdapterImpl.kt @@ -0,0 +1,26 @@ +package ru.fredboy.cavedroid.game.controller.mob.impl + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.entity.mob.abstraction.PlayerAdapter +import ru.fredboy.cavedroid.game.controller.mob.MobController +import javax.inject.Inject + +@GameScope +class PlayerAdapterImpl @Inject constructor( + private val mobController: MobController, +) : PlayerAdapter { + + private val player get() = mobController.player + + override val x: Float + get() = player.x + + override val y: Float + get() = player.y + + override val width: Float + get() = player.width + + override val height: Float + get() = player.height +} diff --git a/core/game/window/build.gradle.kts b/core/game/window/build.gradle.kts new file mode 100644 index 0000000..d84f920 --- /dev/null +++ b/core/game/window/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useEntityModules() + useDomainModules() +} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameUiWindow.java b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowType.kt similarity index 52% rename from core/src/ru/deadsoftware/cavedroid/game/GameUiWindow.java rename to core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowType.kt index c75e96b..efa694c 100644 --- a/core/src/ru/deadsoftware/cavedroid/game/GameUiWindow.java +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowType.kt @@ -1,10 +1,10 @@ -package ru.deadsoftware.cavedroid.game; +package ru.fredboy.cavedroid.game.window -public enum GameUiWindow { +enum class GameWindowType { NONE, CREATIVE_INVENTORY, SURVIVAL_INVENTORY, CHEST, CRAFTING_TABLE, - FURNACE + FURNACE, } diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowsConfigs.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowsConfigs.kt new file mode 100644 index 0000000..36b1550 --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowsConfigs.kt @@ -0,0 +1,124 @@ +package ru.fredboy.cavedroid.game.window + +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 = 124f + const val craftResultOffsetY = 36f + } + + data object Furnace { + const val itemsGridMarginLeft = 8f + const val itemsGridMarginTop = 84f + + const val itemsGridRowHeight = 18f + const val itemsGridColWidth = 18f + + const val itemsInRow = 9 + const val itemsInCol = 5 + + const val hotbarOffsetFromBottom = 24f + const val hotbarCells = 9 + + const val smeltInputMarginLeft = 56f + const val smeltInputMarginTop = 18f + + const val smeltFuelMarginLeft = 56f + const val smeltFuelMarginTop = 54f + + const val smeltResultOffsetX = 116f + const val smeltResultOffsetY = 36f + + const val fuelBurnMarginLeft = 56f + const val fuelBurnMarginTop = 36f + const val fuelBurnHeight = 14f + + const val progressMarginLeft = 79f + const val progressMarginTop = 34f + const val progressWidth = 24f + } + + data object Chest { + const val itemsGridMarginLeft = 8f + const val itemsGridMarginTop = 86f + + const val itemsGridRowHeight = 18f + const val itemsGridColWidth = 18f + + const val hotbarCells = 9 + const val hotbarOffsetFromBottom = 24f + + const val itemsInRow = 9 + const val itemsInCol = 5 + + const val contentsMarginLeft = 8f + const val contentsMarginTop = 18f + + const val contentsInRow = 9 + const val contentsInCol = 3 + } +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowsManager.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowsManager.kt new file mode 100644 index 0000000..5809b5e --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/GameWindowsManager.kt @@ -0,0 +1,61 @@ +package ru.fredboy.cavedroid.game.window + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.entity.container.model.Chest +import ru.fredboy.cavedroid.entity.container.model.Furnace +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter +import ru.fredboy.cavedroid.entity.mob.abstraction.PlayerAdapter +import ru.fredboy.cavedroid.game.window.inventory.AbstractInventoryWindow +import ru.fredboy.cavedroid.game.window.inventory.AbstractInventoryWindowWithCraftGrid +import ru.fredboy.cavedroid.game.window.inventory.ChestInventoryWindow +import ru.fredboy.cavedroid.game.window.inventory.CraftingInventoryWindow +import ru.fredboy.cavedroid.game.window.inventory.CreativeInventoryWindow +import ru.fredboy.cavedroid.game.window.inventory.FurnaceInventoryWindow +import ru.fredboy.cavedroid.game.window.inventory.SurvivalInventoryWindow +import javax.inject.Inject + +@GameScope +class GameWindowsManager @Inject constructor( + private val tooltipManager: TooltipManager, + private val itemsRepository: ItemsRepository, + private val dropAdapter: DropAdapter, + private val playerAdapter: PlayerAdapter, +) { + + var creativeScrollAmount = 0 + var isDragging = false + + var currentWindow: AbstractInventoryWindow? = null + + val currentWindowType: GameWindowType + get() = currentWindow?.type ?: GameWindowType.NONE + + fun openSurvivalInventory() { + currentWindow = SurvivalInventoryWindow(itemsRepository) + } + + fun openCreativeInventory() { + currentWindow = CreativeInventoryWindow() + } + + fun openFurnace(furnace: Furnace) { + currentWindow = FurnaceInventoryWindow(furnace) + } + + fun openChest(chest: Chest) { + currentWindow = ChestInventoryWindow(chest) + } + + fun openCrafting() { + currentWindow = CraftingInventoryWindow(itemsRepository) + } + + fun closeWindow() { + (currentWindow as? AbstractInventoryWindowWithCraftGrid)?.let { window -> + dropAdapter.dropItems(playerAdapter.x, playerAdapter.y, window.craftingItems) + } + currentWindow = null + tooltipManager.showMouseTooltip("") + } +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/TooltipManager.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/TooltipManager.kt new file mode 100644 index 0000000..7a6ed54 --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/TooltipManager.kt @@ -0,0 +1,37 @@ +package ru.fredboy.cavedroid.game.window + +import com.badlogic.gdx.utils.Timer +import ru.fredboy.cavedroid.common.di.GameScope +import javax.inject.Inject + +@GameScope +class TooltipManager @Inject constructor() { + + private val resetTask = object : Timer.Task() { + override fun run() { + currentHotbarTooltip = "" + } + } + + var currentHotbarTooltip: String = "" + private set + + var currentMouseTooltip: String = "" + private set + + fun showHotbarTooltip(tooltip: String) { + currentHotbarTooltip = tooltip + if (resetTask.isScheduled) { + resetTask.cancel() + } + Timer.schedule(resetTask, TOOLTIP_TIME_S) + } + + fun showMouseTooltip(tooltip: String) { + currentMouseTooltip = tooltip + } + + companion object { + private const val TOOLTIP_TIME_S = 2f + } +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/AbstractInventoryWindow.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/AbstractInventoryWindow.kt new file mode 100644 index 0000000..633d4b7 --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/AbstractInventoryWindow.kt @@ -0,0 +1,83 @@ +package ru.fredboy.cavedroid.game.window.inventory + +import com.badlogic.gdx.math.MathUtils +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem.Companion.isNoneOrNull +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.window.GameWindowType + +abstract class AbstractInventoryWindow { + + abstract val type: GameWindowType + + abstract var selectedItem: InventoryItem? + + var selectItemPointer: Int = -1 + + fun onLeftCLick( + items: MutableList, + itemsRepository: ItemsRepository, + index: Int, + pointer: Int = -1, + ) { + if (selectedItem != null && + selectedItem?.item?.isNone() != true && + pointer >= 0 && + selectItemPointer >= 0 && + pointer != selectItemPointer + ) { + return + } + + val clickedItem = items[index] + + selectedItem?.let { selectedItem -> + if (!clickedItem.isNoneOrNull() && + items[index].item == selectedItem.item && + items[index].amount + selectedItem.amount <= selectedItem.item.params.maxStack + ) { + items[index].amount += selectedItem.amount + this@AbstractInventoryWindow.selectedItem = null + selectItemPointer = -1 + return + } + } + + val item = items[index] + items[index] = selectedItem ?: itemsRepository.fallbackItem.toInventoryItem() + selectedItem = item + selectItemPointer = pointer + } + + fun onRightClick(items: MutableList, itemsRepository: ItemsRepository, index: Int) { + val clickedItem = items[index] + val selectedItem = selectedItem + + if (selectedItem.isNoneOrNull() && !clickedItem.isNoneOrNull()) { + val half = InventoryItem(clickedItem.item, MathUtils.ceil(clickedItem.amount.toFloat() / 2f)) + this.selectedItem = half + clickedItem.subtract(half.amount) + if (clickedItem.amount == 0) { + items[index] = itemsRepository.fallbackItem.toInventoryItem() + } + return + } + + if (selectedItem == null || + (!clickedItem.isNoneOrNull() && selectedItem.item != clickedItem.item) || + !clickedItem.canBeAdded() + ) { + return + } + + val newItem = selectedItem.item.toInventoryItem( + (clickedItem.takeIf { !it.item.isNone() }?.amount ?: 0) + 1, + ) + items[index] = newItem + selectedItem.amount-- + + if (selectedItem.amount <= 0) { + this@AbstractInventoryWindow.selectedItem = null + } + } +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/AbstractInventoryWindowWithCraftGrid.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/AbstractInventoryWindowWithCraftGrid.kt new file mode 100644 index 0000000..1e0fbe9 --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/AbstractInventoryWindowWithCraftGrid.kt @@ -0,0 +1,23 @@ +package ru.fredboy.cavedroid.game.window.inventory + +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository + +abstract class AbstractInventoryWindowWithCraftGrid( + itemsRepository: ItemsRepository, +) : AbstractInventoryWindow() { + + private val _items = Array(10) { itemsRepository.fallbackItem.toInventoryItem() } + + val items get() = _items.asList() + + val craftingItems get() = items.subList(0, 9) as MutableList + + val craftResultList get() = items.subList(9, 10) as MutableList + + var craftResult: InventoryItem + get() = craftResultList[0] + set(value) { + craftResultList[0] = value + } +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/ChestInventoryWindow.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/ChestInventoryWindow.kt new file mode 100644 index 0000000..9de51be --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/ChestInventoryWindow.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.game.window.inventory + +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.entity.container.model.Chest +import ru.fredboy.cavedroid.game.window.GameWindowType + +class ChestInventoryWindow(val chest: Chest) : AbstractInventoryWindow() { + + override val type = GameWindowType.CHEST + + override var selectedItem: InventoryItem? = null +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/CraftingInventoryWindow.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/CraftingInventoryWindow.kt new file mode 100644 index 0000000..8a17019 --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/CraftingInventoryWindow.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.game.window.inventory + +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.window.GameWindowType + +class CraftingInventoryWindow( + itemsRepository: ItemsRepository, +) : AbstractInventoryWindowWithCraftGrid(itemsRepository) { + + override val type = GameWindowType.CRAFTING_TABLE + + override var selectedItem: InventoryItem? = null +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/CreativeInventoryWindow.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/CreativeInventoryWindow.kt new file mode 100644 index 0000000..f07f036 --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/CreativeInventoryWindow.kt @@ -0,0 +1,15 @@ +package ru.fredboy.cavedroid.game.window.inventory + +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs + +class CreativeInventoryWindow : AbstractInventoryWindow() { + + override val type = GameWindowType.CREATIVE_INVENTORY + + override var selectedItem: InventoryItem? = null + + fun getMaxScroll(itemsRepository: ItemsRepository): Int = itemsRepository.getAllItems().size / GameWindowsConfigs.Creative.itemsInRow +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/FurnaceInventoryWindow.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/FurnaceInventoryWindow.kt new file mode 100644 index 0000000..e3bcef8 --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/FurnaceInventoryWindow.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.game.window.inventory + +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.entity.container.model.Furnace +import ru.fredboy.cavedroid.game.window.GameWindowType + +class FurnaceInventoryWindow( + val furnace: Furnace, +) : AbstractInventoryWindow() { + + override val type = GameWindowType.FURNACE + + override var selectedItem: InventoryItem? = null +} diff --git a/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/SurvivalInventoryWindow.kt b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/SurvivalInventoryWindow.kt new file mode 100644 index 0000000..959e127 --- /dev/null +++ b/core/game/window/src/main/kotlin/ru/fredboy/cavedroid/game/window/inventory/SurvivalInventoryWindow.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.game.window.inventory + +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.window.GameWindowType + +class SurvivalInventoryWindow( + itemsRepository: ItemsRepository, +) : AbstractInventoryWindowWithCraftGrid(itemsRepository) { + + override val type = GameWindowType.SURVIVAL_INVENTORY + + override var selectedItem: InventoryItem? = null +} diff --git a/core/game/world/build.gradle.kts b/core/game/world/build.gradle.kts new file mode 100644 index 0000000..317d76b --- /dev/null +++ b/core/game/world/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + + useCommonModule() + useModule(":core:domain:items") + useModule(":core:domain:world") + useModule(":core:entity:container") + useModule(":core:entity:drop") + useModule(":core:entity:mob") +} diff --git a/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/GameWorld.kt b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/GameWorld.kt new file mode 100644 index 0000000..1f69ac2 --- /dev/null +++ b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/GameWorld.kt @@ -0,0 +1,193 @@ +package ru.fredboy.cavedroid.game.world + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.removeFirst +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.domain.world.listener.OnBlockDestroyedListener +import ru.fredboy.cavedroid.domain.world.listener.OnBlockPlacedListener +import ru.fredboy.cavedroid.domain.world.model.Layer +import ru.fredboy.cavedroid.game.world.generator.GameWorldGenerator +import ru.fredboy.cavedroid.game.world.generator.WorldGeneratorConfig +import java.lang.ref.WeakReference +import java.util.LinkedList +import javax.inject.Inject + +@GameScope +class GameWorld @Inject constructor( + private val itemsRepository: ItemsRepository, + initialForeMap: Array>?, + initialBackMap: Array>?, +) { + val foreMap: Array> + val backMap: Array> + + val width: Int + val height: Int + + val generatorConfig = WorldGeneratorConfig.getDefault() + + private val onBlockPlacedListeners = LinkedList>() + private val onBlockDestroyedListeners = LinkedList>() + + init { + width = generatorConfig.width + height = generatorConfig.height + + if (initialForeMap != null && initialBackMap != null) { + foreMap = initialForeMap + backMap = initialBackMap + } else { + val (generatedFore, generatedBack) = GameWorldGenerator(generatorConfig, itemsRepository).generate() + foreMap = generatedFore + backMap = generatedBack + } + } + + fun addBlockPlacedListener(listener: OnBlockPlacedListener) { + onBlockPlacedListeners.add(WeakReference(listener)) + } + + fun addBlockDestroyedListener(listener: OnBlockDestroyedListener) { + onBlockDestroyedListeners.add(WeakReference(listener)) + } + + fun removeBlockPlacedListener(listener: OnBlockPlacedListener) { + onBlockPlacedListeners.removeFirst { it.get() == listener } + } + + fun removeBlockDestroyedListener(listener: OnBlockDestroyedListener) { + onBlockDestroyedListeners.removeFirst { it.get() == listener } + } + + private fun transformX(x: Int): Int { + var transformed = x % width + if (transformed < 0) { + transformed = width + x + } + return transformed + } + + private fun getMap(x: Int, y: Int, layer: Layer): Block { + val fallback = itemsRepository.fallbackBlock + + if (y !in 0.. foreMap[transformedX][y] + Layer.BACKGROUND -> backMap[transformedX][y] + } + } + + private fun notifyBlockPlaced(x: Int, y: Int, layer: Layer, value: Block) { + onBlockPlacedListeners.forEach { listener -> + listener.get()?.onBlockPlaced(value, x, y, layer) + } + } + + private fun notifyBlockDestroyed(x: Int, y: Int, layer: Layer, value: Block, withDrop: Boolean) { + onBlockDestroyedListeners.forEach { listener -> + listener.get()?.onBlockDestroyed(value, x, y, layer, withDrop) + } + } + + private fun setMap(x: Int, y: Int, layer: Layer, value: Block, dropOld: Boolean) { + if (y !in 0.. + notifyBlockDestroyed(x, y, layer, currentBlock, dropOld) + } + + when (layer) { + Layer.FOREGROUND -> foreMap[transformedX][y] = value + Layer.BACKGROUND -> backMap[transformedX][y] = value + } + + notifyBlockPlaced(x, y, layer, value) + } + + private fun isSameSlab(slab1: Block, slab2: Block): Boolean = slab1 is Block.Slab && + slab2 is Block.Slab && + (slab1.params.key == slab2.otherPartBlockKey || slab1.otherPartBlockKey == slab2.params.key) + + fun hasForeAt(x: Int, y: Int): Boolean = !getMap(x, y, Layer.FOREGROUND).isNone() + + fun hasBackAt(x: Int, y: Int): Boolean = !getMap(x, y, Layer.BACKGROUND).isNone() + + fun getForeMap(x: Int, y: Int): Block = getMap(x, y, Layer.FOREGROUND) + + fun setForeMap(x: Int, y: Int, block: Block, dropOld: Boolean = false) { + setMap(x, y, Layer.FOREGROUND, block, dropOld) + } + + fun resetForeMap(x: Int, y: Int) { + setForeMap(x, y, itemsRepository.fallbackBlock) + } + + fun getBackMap(x: Int, y: Int): Block = getMap(x, y, Layer.BACKGROUND) + + fun setBackMap(x: Int, y: Int, block: Block, dropOld: Boolean = false) { + setMap(x, y, Layer.BACKGROUND, block, dropOld) + } + + fun canPlaceToForeground(x: Int, y: Int, value: Block): Boolean = !hasForeAt(x, y) || value.isNone() || !getForeMap(x, y).params.hasCollision + + fun placeToForeground(x: Int, y: Int, value: Block, dropOld: Boolean = false): Boolean { + val wasPlaced = if (canPlaceToForeground(x, y, value)) { + setForeMap(x, y, value, dropOld) + true + } else if (value is Block.Slab && isSameSlab(value, getForeMap(x, y))) { + setForeMap(x, y, itemsRepository.getBlockByKey(value.otherPartBlockKey), dropOld) + true + } else { + false + } + + return wasPlaced + } + + fun placeToBackground(x: Int, y: Int, value: Block, dropOld: Boolean = false): Boolean { + val wasPlaced = if (value.isNone() || + getBackMap(x, y).isNone() && + value.params.hasCollision && + (!value.params.isTransparent || value.params.key == "glass" || value.isChest() || value.isSlab()) + ) { + setBackMap(x, y, value, dropOld) + true + } else { + false + } + + return wasPlaced + } + + fun destroyForeMap(x: Int, y: Int, shouldDrop: Boolean) { + placeToForeground(x, y, itemsRepository.fallbackBlock, shouldDrop) + } + + fun destroyBackMap(x: Int, y: Int, shouldDrop: Boolean) { + placeToBackground(x, y, itemsRepository.fallbackBlock, shouldDrop) + } + + companion object { + private const val TAG = "GameWorld" + } +} diff --git a/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/di/GameWorldModule.kt b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/di/GameWorldModule.kt new file mode 100644 index 0000000..f739ce8 --- /dev/null +++ b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/di/GameWorldModule.kt @@ -0,0 +1,21 @@ +package ru.fredboy.cavedroid.game.world.di + +import dagger.Binds +import dagger.Module +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerWorldAdapter +import ru.fredboy.cavedroid.entity.drop.abstraction.DropWorldAdapter +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.game.world.impl.WorldAdapterImpl + +@Module +abstract class GameWorldModule { + + @Binds + internal abstract fun bindMobWorldAdapter(impl: WorldAdapterImpl): MobWorldAdapter + + @Binds + internal abstract fun bindContainerWorldAdapter(impl: WorldAdapterImpl): ContainerWorldAdapter + + @Binds + internal abstract fun bindDropWorldAdapter(impl: WorldAdapterImpl): DropWorldAdapter +} diff --git a/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/Biome.kt b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/Biome.kt new file mode 100644 index 0000000..8ff59a4 --- /dev/null +++ b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/Biome.kt @@ -0,0 +1,7 @@ +package ru.fredboy.cavedroid.game.world.generator + +enum class Biome { + PLAINS, + DESERT, + WINTER, +} diff --git a/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/GameWorldGenerator.kt b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/GameWorldGenerator.kt new file mode 100644 index 0000000..cd873b7 --- /dev/null +++ b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/GameWorldGenerator.kt @@ -0,0 +1,332 @@ +package ru.fredboy.cavedroid.game.world.generator + +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import kotlin.math.abs +import kotlin.math.max +import kotlin.math.min +import kotlin.random.Random + +class GameWorldGenerator( + private val config: WorldGeneratorConfig, + private val itemsRepository: ItemsRepository, +) { + + private val random = Random(config.seed) + + private val foreMap by lazy { Array(config.width) { Array(config.height) { itemsRepository.fallbackBlock } } } + private val backMap by lazy { Array(config.width) { Array(config.height) { itemsRepository.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.. { + 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.. dirt + else -> stone + } + backMap[x][y] = foreMap[x][y] + } + + val plant = random.nextInt(100) + if (surfaceHeight < config.seaLevel) { + if (plant < 10) { + generateSpruce(x) + } + } + } + + private fun plainsBiome(x: Int) { + assert(x in 0.. 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.. 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 = itemsRepository.getBlockByKey("water") + + for (x in 0..= 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] = itemsRepository.getBlockByKey(mushrooms.random(random)) + } + } + + for (y in h downTo height) { + backMap[x][y] = log + } + } + + private fun generateSpruce(x: Int) { + val log = itemsRepository.getBlockByKey("log_spruce") + val leaves = itemsRepository.getBlockByKey("leaves_spruce") + val h = heights[x] - 1 + val treeH = random.nextInt(7, 9) + val height = max(0, h - treeH) + + val top = height - 1 + if (top >= 0) { + foreMap[x][top] = leaves + backMap[x][top] = leaves + } + + for (x1 in max(0, x - 1)..min(config.width - 1, x + 1)) { + val y = height + foreMap[x1][y] = leaves + backMap[x1][y] = leaves + } + + for (y in 1..2) { + for (x1 in max(0, x - y)..min(config.width - 1, x + y)) { + foreMap[x1][height + 1 + y] = leaves + backMap[x1][height + 1 + y] = leaves + } + } + + for (y in h downTo height) { + backMap[x][y] = log + } + } + + private fun generateTallGrass(x: Int) { + val tallGrass = itemsRepository.getBlockByKey(plainsPlants.random(random)) + val h = heights[x] - 1 + if (h > 0) { + foreMap[x][h] = tallGrass + } + } + + private fun generateDeadBush(x: Int) { + val bush = itemsRepository.getBlockByKey("deadbush") + val h = heights[x] - 1 + if (h > 0) { + foreMap[x][h] = bush + } + } + + private fun generateOres(x: Int) { + val stone = itemsRepository.getBlockByKey("stone") + val coal = itemsRepository.getBlockByKey("coal_ore") + val iron = itemsRepository.getBlockByKey("iron_ore") + val gold = itemsRepository.getBlockByKey("gold_ore") + val diamond = itemsRepository.getBlockByKey("diamond_ore") + val lapis = itemsRepository.getBlockByKey("lapis_ore") + + for (y in heights[x].. 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>> { + 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/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/WorldGeneratorConfig.kt b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/WorldGeneratorConfig.kt new file mode 100644 index 0000000..f8c6643 --- /dev/null +++ b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/generator/WorldGeneratorConfig.kt @@ -0,0 +1,29 @@ +package ru.fredboy.cavedroid.game.world.generator + +import com.badlogic.gdx.utils.TimeUtils + +data class WorldGeneratorConfig( + val width: Int, + val height: Int, + val seed: Long, + val minSurfaceHeight: Int, + val maxSurfaceHeight: Int, + val biomes: List, + val minBiomeSize: Int, + val seaLevel: Int, +) { + + companion object { + + fun getDefault(): WorldGeneratorConfig = WorldGeneratorConfig( + width = 1024, + height = 256, + seed = TimeUtils.millis(), + maxSurfaceHeight = 208, + minSurfaceHeight = 128, + biomes = Biome.entries.toList(), + minBiomeSize = 64, + seaLevel = 192, + ) + } +} diff --git a/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/impl/WorldAdapterImpl.kt b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/impl/WorldAdapterImpl.kt new file mode 100644 index 0000000..a24bf77 --- /dev/null +++ b/core/game/world/src/main/kotlin/ru/fredboy/cavedroid/game/world/impl/WorldAdapterImpl.kt @@ -0,0 +1,106 @@ +package ru.fredboy.cavedroid.game.world.impl + +import com.badlogic.gdx.math.Vector2 +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.domain.world.listener.OnBlockDestroyedListener +import ru.fredboy.cavedroid.domain.world.listener.OnBlockPlacedListener +import ru.fredboy.cavedroid.domain.world.model.Layer +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerWorldAdapter +import ru.fredboy.cavedroid.entity.container.model.ContainerCoordinates +import ru.fredboy.cavedroid.entity.drop.abstraction.DropWorldAdapter +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.world.GameWorld +import javax.inject.Inject +import kotlin.reflect.KClass + +@GameScope +internal class WorldAdapterImpl @Inject constructor( + private val gameWorld: GameWorld, + private val itemsRepository: ItemsRepository, +) : DropWorldAdapter, + ContainerWorldAdapter, + MobWorldAdapter { + + override val height: Int + get() = gameWorld.height + + override val width: Int + get() = gameWorld.width + + override fun setForegroundBlock(x: Int, y: Int, block: Block) { + gameWorld.setForeMap(x, y, block) + } + + override fun setBackgroundBlock(x: Int, y: Int, block: Block) { + gameWorld.setBackMap(x, y, block) + } + + override fun getForegroundBlock(x: Int, y: Int): Block = gameWorld.getForeMap(x, y) + + override fun getBackgroundBlock(x: Int, y: Int): Block = gameWorld.getBackMap(x, y) + + override fun destroyForegroundBlock(x: Int, y: Int, shouldDrop: Boolean) { + gameWorld.destroyForeMap(x, y, shouldDrop) + } + + override fun destroyBackgroundBlock(x: Int, y: Int, shouldDrop: Boolean) { + gameWorld.destroyBackMap(x, y, shouldDrop) + } + + override fun findSpawnPoint(): Vector2 { + var x = width / 2 + var y = 0 + + while (y++ in 0..gameWorld.generatorConfig.seaLevel) { + if (y == gameWorld.generatorConfig.seaLevel) { + while (x++ in 0.., + ): Boolean { + val block = when (coordinates.z) { + Layer.FOREGROUND.z -> gameWorld.getForeMap(coordinates.x, coordinates.y) + Layer.BACKGROUND.z -> gameWorld.getBackMap(coordinates.x, coordinates.y) + else -> itemsRepository.fallbackBlock + } + + return block.isContainer() && block::class == requiredType + } + + override fun addOnBlockDestroyedListener(listener: OnBlockDestroyedListener) { + gameWorld.addBlockDestroyedListener(listener) + } + + override fun addOnBlockPlacedListener(listener: OnBlockPlacedListener) { + gameWorld.addBlockPlacedListener(listener) + } + + override fun removeOnBlockDestroyedListener(listener: OnBlockDestroyedListener) { + gameWorld.removeBlockDestroyedListener(listener) + } + + override fun removeOnBlockPlacedListener(listener: OnBlockPlacedListener) { + gameWorld.removeBlockPlacedListener(listener) + } +} diff --git a/core/src/ru/deadsoftware/cavedroid/CaveGame.java b/core/src/ru/deadsoftware/cavedroid/CaveGame.java deleted file mode 100644 index 031b613..0000000 --- a/core/src/ru/deadsoftware/cavedroid/CaveGame.java +++ /dev/null @@ -1,75 +0,0 @@ -package ru.deadsoftware.cavedroid; - -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; - -public class CaveGame extends Game { - - private static final String TAG = "CaveGame"; - - public static final String VERSION = "alpha 0.4"; - - private final MainConfig mMainConfig; - private final MainComponent mMainComponent; - - private final String mGameFolder; - private final boolean mTouch; - private boolean mDebug; - - public CaveGame(String gameFolder, boolean touch) { - mGameFolder = gameFolder; - mTouch = touch; - - mMainComponent = DaggerMainComponent.builder().caveGame(this).build(); - mMainConfig = mMainComponent.getMainConfig(); - } - - public void setDebug(boolean debug) { - mDebug = debug; - } - - private void initConfig() { - int width = mTouch ? 320 : 480; - int height = (int) (width * ((float) Gdx.graphics.getHeight() / Gdx.graphics.getWidth())); - - mMainConfig.setMainComponent(mMainComponent); - mMainConfig.setGameFolder(mGameFolder); - mMainConfig.setTouch(mTouch); - mMainConfig.setWidth(width); - mMainConfig.setHeight(height); - mMainConfig.setShowInfo(mDebug); - } - - public void newGame() { - GameScreen gameScreen = mMainComponent.getGameScreen(); - gameScreen.newGame(); - setScreen(gameScreen); - } - - public void loadGame() { - GameScreen gameScreen = mMainComponent.getGameScreen(); - gameScreen.loadGame(); - setScreen(gameScreen); - } - - public void quitGame() { - setScreen(mMainComponent.getMenuScreen()); - } - - @Override - public void create() { - Gdx.app.log(TAG, mGameFolder); - Gdx.files.absolute(mGameFolder).mkdirs(); - - Assets.load(); - GameItems.load(); - - initConfig(); - - setScreen(mMainComponent.getMenuScreen()); - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/MainComponent.java b/core/src/ru/deadsoftware/cavedroid/MainComponent.java deleted file mode 100644 index d8a05da..0000000 --- a/core/src/ru/deadsoftware/cavedroid/MainComponent.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.deadsoftware.cavedroid; - -import dagger.Component; -import ru.deadsoftware.cavedroid.game.GameScreen; -import ru.deadsoftware.cavedroid.menu.MenuScreen; - -import javax.inject.Singleton; - -@Singleton -@Component(dependencies = CaveGame.class) -public interface MainComponent { - GameScreen getGameScreen(); - - MenuScreen getMenuScreen(); - - MainConfig getMainConfig(); -} diff --git a/core/src/ru/deadsoftware/cavedroid/MainConfig.java b/core/src/ru/deadsoftware/cavedroid/MainConfig.java deleted file mode 100644 index 8cd0ffd..0000000 --- a/core/src/ru/deadsoftware/cavedroid/MainConfig.java +++ /dev/null @@ -1,107 +0,0 @@ -package ru.deadsoftware.cavedroid; - -import ru.deadsoftware.cavedroid.game.GameUiWindow; - -import javax.annotation.CheckForNull; -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class MainConfig { - - private final CaveGame mCaveGame; - - @CheckForNull - private MainComponent mMainComponent; - - private GameUiWindow mGameUiWindow; - private String mGameFolder; - - private boolean mTouch; - private boolean mShowInfo; - private boolean mShowMap; - - private float mWidth; - private float mHeight; - - @Inject - public MainConfig(CaveGame caveGame) { - mCaveGame = caveGame; - - mGameUiWindow = GameUiWindow.NONE; - mGameFolder = ""; - } - - public CaveGame getCaveGame() { - return mCaveGame; - } - - public MainComponent getMainComponent() { - assert mMainComponent != null; - return mMainComponent; - } - - public void setMainComponent(MainComponent mainComponent) { - mMainComponent = mainComponent; - } - - public boolean checkGameUiWindow(GameUiWindow gameUiWindow) { - return mGameUiWindow == gameUiWindow; - } - - public GameUiWindow getGameUiWindow() { - return mGameUiWindow; - } - - public void setGameUiWindow(GameUiWindow gameUiWindow) { - mGameUiWindow = gameUiWindow; - } - - public String getGameFolder() { - return mGameFolder; - } - - public void setGameFolder(String gameFolder) { - mGameFolder = gameFolder; - } - - public boolean isTouch() { - return mTouch; - } - - public void setTouch(boolean touch) { - mTouch = touch; - } - - public float getWidth() { - return mWidth; - } - - public void setWidth(float width) { - mWidth = width; - } - - public float getHeight() { - return mHeight; - } - - public void setHeight(float height) { - mHeight = height; - } - - public boolean isShowInfo() { - return mShowInfo; - } - - public void setShowInfo(boolean showInfo) { - mShowInfo = showInfo; - } - - public boolean isShowMap() { - return mShowMap; - } - - public void setShowMap(boolean showMap) { - mShowMap = showMap; - } -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameComponent.java b/core/src/ru/deadsoftware/cavedroid/game/GameComponent.java deleted file mode 100644 index 25779f0..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/GameComponent.java +++ /dev/null @@ -1,12 +0,0 @@ -package ru.deadsoftware.cavedroid.game; - -import dagger.Component; -import ru.deadsoftware.cavedroid.MainComponent; - -@GameScope -@Component(dependencies = MainComponent.class, modules = GameModule.class) -public interface GameComponent { - GameProc getGameProc(); - - GameInputProcessor getGameInputProcessor(); -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameFluidsThread.java b/core/src/ru/deadsoftware/cavedroid/game/GameFluidsThread.java deleted file mode 100644 index c3cdd47..0000000 --- a/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 deleted file mode 100644 index ca60317..0000000 --- a/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 deleted file mode 100644 index 51cc897..0000000 --- a/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 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 deleted file mode 100644 index 85f4b9c..0000000 --- a/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 blocksIds = new HashMap<>(); - private static final HashMap itemsIds = new HashMap<>(); - - private static final ArrayMap blocks = new ArrayMap<>(); - private static final ArrayMap 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/GameModule.java b/core/src/ru/deadsoftware/cavedroid/game/GameModule.java deleted file mode 100644 index d7ccc2d..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/GameModule.java +++ /dev/null @@ -1,52 +0,0 @@ -package ru.deadsoftware.cavedroid.game; - -import dagger.Module; -import dagger.Provides; -import ru.deadsoftware.cavedroid.MainConfig; -import ru.deadsoftware.cavedroid.game.mobs.MobsController; -import ru.deadsoftware.cavedroid.game.objects.DropController; - -import javax.annotation.CheckForNull; - -@Module -public class GameModule { - - @CheckForNull - private static GameSaver.Data data; - - public static void load(MainConfig mainConfig) { - data = GameSaver.load(mainConfig); - } - - private static void makeDataNullIfEmpty() { - if (data != null && data.isEmpty()) { - data = null; - } - } - - @Provides - @GameScope - public static DropController provideDropController() { - DropController controller = data != null ? data.retrieveDropController() : new DropController(); - makeDataNullIfEmpty(); - return controller; - } - - @Provides - @GameScope - public static MobsController provideMobsController() { - MobsController controller = data != null ? data.retrieveMobsController() : new MobsController(); - makeDataNullIfEmpty(); - 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; - makeDataNullIfEmpty(); - return new GameWorld(dropController, mobsController, fm, bm); - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java b/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java deleted file mode 100644 index d6b5a9a..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java +++ /dev/null @@ -1,288 +0,0 @@ -package ru.deadsoftware.cavedroid.game; - -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.MainConfig; -import ru.deadsoftware.cavedroid.game.mobs.Mob; -import ru.deadsoftware.cavedroid.game.mobs.MobsController; -import ru.deadsoftware.cavedroid.game.mobs.Player; -import ru.deadsoftware.cavedroid.game.objects.Drop; -import ru.deadsoftware.cavedroid.game.objects.DropController; - -import javax.inject.Inject; -import java.util.Iterator; - - -@GameScope -class GamePhysics { - - static final int PL_SPEED = 2; - - private final Vector2 gravity = new Vector2(0, .9f); - - private final GameWorld mGameWorld; - private final MainConfig mMainConfig; - private final MobsController mMobsController; - private final DropController mDropController; - - @Inject - public GamePhysics(GameWorld gameWorld, - MainConfig mainConfig, - MobsController mobsController, - DropController dropController) { - mGameWorld = gameWorld; - mMainConfig = mainConfig; - mMobsController = mobsController; - mDropController = dropController; - } - - /** - * Checks if mob should jump - * - * @return true if mob should jump - */ - private boolean checkJump(Mob mob) { - 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); - - if (checkColl(new Rectangle(blX, mob.getY() - 18, mob.getWidth(), mob.getHeight()))) { - block = 0; - } - - return (block > 0 && GameItems.getBlock(block).toJump() && - (mob.getY() + mob.getHeight()) - GameItems.getBlock(block).getRectangle(blX / 16, blY / 16).y > 8); - } - - private boolean checkColl(Rectangle rect) { - int minX = (int) ((rect.x + rect.width / 2) / 16) - 4; - int minY = (int) ((rect.y + rect.height / 2) / 16) - 4; - int maxX = (int) ((rect.x + rect.width / 2) / 16) + 4; - int maxY = (int) ((rect.y + rect.height / 2) / 16) + 4; - - if (minY < 0) { - minY = 0; - } - - if (maxY > mGameWorld.getHeight()) { - maxY = mGameWorld.getHeight(); - } - - int block; - for (int y = minY; y < maxY; y++) { - for (int x = minX; x < maxX; x++) { - block = mGameWorld.getForeMap(x, y); - if (block > 0 && GameItems.getBlock(block).hasCollision()) { - if (Intersector.overlaps(rect, GameItems.getBlock(block).getRectangle(x, y))) { - return true; - } - } - } - } - - return false; - } - - private int 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); - } 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; - } - } - drop.move(); - - - if (checkColl(drop)) { - drop.getMove().set(0, -1); - do { - drop.move(); - } while (checkColl(drop)); - drop.getMove().setZero(); - } - } - - private void mobXColl(Mob mob) { - if (checkColl(mob)) { - if (mob.canJump() && !mob.isFlyMode()) { - mob.y -= 8; - } - - if (checkColl(mob)) { - if (mob.canJump() && !mob.isFlyMode()) { - mob.y += 8; - } - - int d = 0; - - if (mob.getMove().x < 0) { - d = 1; - } else if (mob.getMove().x > 0) { - d = -1; - } - - mob.x = MathUtils.round(mob.getX()); - - while (checkColl(mob)) { - mob.x += d; - } - - if (mob.canJump()) { - mob.changeDir(); - } - } - } - - mob.checkWorldBounds(mGameWorld); - } - - private void mobYColl(Mob mob) { - if (checkColl(mob)) { - int d = -1; - - if (mob.getMove().y < 0) { - d = 1; - } - - if (d == -1) { - mob.setCanJump(true); - mob.setFlyMode(false); - } - - mob.y = MathUtils.round(mob.getY()); - - while (checkColl(mob)) { - mob.y += d; - } - - mob.getMove().y = 0; - - } else { - mob.setCanJump(false); - } - - if (mob.getY() > mGameWorld.getHeightPx()) { - mob.kill(); - } - } - - private void playerPhy(Player player) { - player.y += player.getMove().y; - mobYColl(player); - - if (player.isDead()) { - return; - } - - if (GameItems.isFluid(getBlock(player))) { - if (mMainConfig.isTouch() && player.getMove().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.getMove().y > 4.5f) { - player.getMove().add(0, -1f); - } - } else { - player.getMove().add(0, -.5f); - if (player.getMove().y < -3) { - player.getMove().y = -3; - } - } - } else { - if (!player.isFlyMode() && player.getMove().y < 18) { - player.getMove().add(gravity); - } - } - - player.x += player.getMove().x * (player.isFlyMode() ? 1.5f : 1) * - (GameItems.isFluid(getBlock(player)) && !player.isFlyMode() ? .8f : 1); - - mobXColl(player); - - if (mMainConfig.isTouch() && !player.isFlyMode() && player.canJump() && player.getMove().x != 0 && checkJump(player)) { - player.getMove().add(0, -8); - 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); - } - - mob.getMove().add(0, -.5f); - - if (mob.getMove().y < -3) { - mob.getMove().y = -3; - } - } else if (!mob.isFlyMode() && mob.getMove().y < 18) { - mob.getMove().add(gravity); - } - - mob.y += mob.getMove().y; - mobYColl(mob); - - if (mob.isDead()) { - return; - } - - mob.x += mob.getMove().x; - mobXColl(mob); - - if (mob.canJump() && mob.getMove().x != 0 && checkJump(mob)) { - mob.getMove().add(0, -8); - mob.setCanJump(false); - } - } - - void update() { - Player player = mMobsController.getPlayer(); - - for (Iterator it = mDropController.getIterator(); it.hasNext(); ) { - Drop drop = it.next(); - dropPhy(drop); - if (Intersector.overlaps(drop, player)) { - drop.pickUpDrop(player); - } - if (drop.isPickedUp()) { - it.remove(); - } - } - - for (Iterator it = mMobsController.getIterator(); it.hasNext(); ) { - Mob mob = it.next(); - mob.ai(mGameWorld); - mobPhy(mob); - if (mob.isDead()) { - it.remove(); - } - } - - playerPhy(player); - if (player.isDead()) { - player.respawn(mGameWorld); - } - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameProc.java b/core/src/ru/deadsoftware/cavedroid/game/GameProc.java deleted file mode 100644 index 44a4687..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/GameProc.java +++ /dev/null @@ -1,41 +0,0 @@ -package ru.deadsoftware.cavedroid.game; - -import com.badlogic.gdx.utils.Disposable; - -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; - - @Inject - public GameProc(GameWorld gameWorld, - GamePhysics gamePhysics, - GameInput gameInput, - GameRenderer gameRenderer) { - mGameWorld = gameWorld; - mGamePhysics = gamePhysics; - mGameInput = gameInput; - mGameRenderer = gameRenderer; - - mGameWorld.startFluidsThread(); - } - - public void update(float delta) { - mGamePhysics.update(); - mGameInput.update(); - mGameWorld.update(); - mGameRenderer.render(delta); - } - - @Override - public void dispose() { - mGameWorld.dispose(); - } -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java b/core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java deleted file mode 100644 index 19f2681..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java +++ /dev/null @@ -1,303 +0,0 @@ -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 ru.deadsoftware.cavedroid.MainConfig; -import ru.deadsoftware.cavedroid.game.mobs.Mob; -import ru.deadsoftware.cavedroid.game.mobs.MobsController; -import ru.deadsoftware.cavedroid.game.mobs.Player; -import ru.deadsoftware.cavedroid.game.objects.Drop; -import ru.deadsoftware.cavedroid.game.objects.DropController; -import ru.deadsoftware.cavedroid.misc.ControlMode; -import ru.deadsoftware.cavedroid.misc.Renderer; - -import javax.inject.Inject; - -import static ru.deadsoftware.cavedroid.misc.Assets.guiMap; -import static ru.deadsoftware.cavedroid.misc.Assets.textureRegions; - -@GameScope -public class GameRenderer extends Renderer { - - private final MainConfig mMainConfig; - private final GameInput mGameInput; - private final GameWorld mGameWorld; - private final MobsController mMobsController; - private final DropController mDropController; - - @Inject - GameRenderer(MainConfig mainConfig, - GameInput gameInput, - GameWorld gameWorld, - MobsController mobsController, - DropController dropController) { - super(mainConfig.getWidth(), mainConfig.getHeight()); - - mMainConfig = mainConfig; - mGameInput = gameInput; - mGameWorld = gameWorld; - mMobsController = mobsController; - mDropController = dropController; - - Gdx.gl.glClearColor(0f, .6f, .6f, 1f); - } - - private float drawX(int x) { - return x * 16 - getCamX(); - } - - private float drawY(int y) { - return y * 16 - getCamY(); - } - - 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 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 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 void drawMob(Mob mob) { - float mobDrawX = mob.getX() - getCamX(); - float mobDrawY = mob.getY() - getCamY(); - - 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; - } - - mob.draw(spriter, mobDrawX, mobDrawY); - } - - private void drawDrop(Drop drop) { - } - - @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); - } - } - } - - } - - 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()); - } - 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); - } - } - } - spriter.draw(hotbarSelector, - getWidth() / 2 - (float) hotbar.getRegionWidth() / 2 - 1 + 20 * mMobsController.getPlayer().slot, - -1); - } - - 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); - } - } - - private void drawGamePlay() { - Player player = mMobsController.getPlayer(); - - 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(); - } - - private void updateCameraPosition() { - Player player = mMobsController.getPlayer(); - setCamPos(player.getX() + player.getWidth() / 2 - getWidth() / 2, - player.getY() + player.getHeight() / 2 - getHeight() / 2); - } - - @Override - public void render(float delta) { - int fps = (int) (1 / delta); - updateCameraPosition(); - mGameInput.moveCursor(this); - - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - - spriter.begin(); - - drawGamePlay(); - - switch (mMainConfig.getGameUiWindow()) { - case CREATIVE_INVENTORY: - drawCreative(); - break; - //TODO draw other ui windows - } - - - if (mMainConfig.isTouch()) { - drawTouchGui(); - } - - spriter.end(); - - 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(); - //================= - } - - 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(); - } - - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java b/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java deleted file mode 100644 index 1ff2bad..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/GameSaver.java +++ /dev/null @@ -1,194 +0,0 @@ -package ru.deadsoftware.cavedroid.game; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import ru.deadsoftware.cavedroid.MainConfig; -import ru.deadsoftware.cavedroid.game.mobs.MobsController; -import ru.deadsoftware.cavedroid.game.objects.DropController; - -import javax.annotation.CheckForNull; -import java.io.*; -import java.nio.ByteBuffer; - -public class GameSaver { - - public static class Data { - @CheckForNull - private MobsController mMobsController; - @CheckForNull - private DropController mDropController; - @CheckForNull - private int[][] mForeMap, mBackMap; - - public Data(MobsController mobsController, DropController dropController, int[][] foreMap, int[][] backMap) { - mMobsController = mobsController; - mDropController = dropController; - mForeMap = foreMap; - mBackMap = backMap; - } - - public MobsController retrieveMobsController() { - assert mMobsController != null; - MobsController mobsController = mMobsController; - mMobsController = null; - return mobsController; - } - - public DropController retrieveDropController() { - assert mDropController != null; - DropController dropController = mDropController; - mDropController = null; - return dropController; - } - - public int[][] retrieveForeMap() { - assert mForeMap != null; - int[][] foreMap = mForeMap; - mForeMap = null; - return foreMap; - } - - public int[][] retrieveBackMap() { - assert mBackMap != null; - int[][] backMap = mBackMap; - mBackMap = null; - return backMap; - } - - public boolean isEmpty() { - return mMobsController == null && mDropController == null && mForeMap == null && mBackMap == null; - } - } - - private static final int SAVE_VERSION = 1; - - private static byte[] intToBytes(int i) { - return ByteBuffer.allocate(4).putInt(i).array(); - } - - private static void saveMap(FileHandle file, int[][] map) 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(intToBytes(width)); - out.write(intToBytes(height)); - - for (int y = 0; y < height; y++) { - block = map[0][y]; - run = 0; - for (int[] ints : map) { - if (ints[y] != block) { - out.write(intToBytes(run)); - out.write(intToBytes(block)); - run = 0; - block = ints[y]; - } - run++; - } - out.write(intToBytes(run)); - out.write(intToBytes(block)); - } - - out.flush(); - out.close(); - } - - private static int[][] loadMap(FileHandle file) throws Exception { - int[][] map; - int version, width, height; - int run, block; - - DataInputStream in = new DataInputStream(file.read()); - - version = in.readInt(); - - if (SAVE_VERSION == version) { - width = in.readInt(); - height = in.readInt(); - map = new int[width][height]; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x += run) { - run = in.readInt(); - block = in.readInt(); - for (int i = x; i < x + run; i++) { - map[i][y] = block; - } - } - } - } else { - throw new Exception("version mismatch"); - } - - in.close(); - return map; - } - - @CheckForNull - public static Data load(MainConfig mainConfig) { - String folder = mainConfig.getGameFolder(); - FileHandle file = Gdx.files.absolute(folder + "/saves/game.sav"); - - try { - ObjectInputStream in = new ObjectInputStream(file.read()); - int version = in.readInt(); - DropController dropController; - MobsController mobsController; - - if (SAVE_VERSION == version) { - dropController = (DropController) in.readObject(); - mobsController = (MobsController) in.readObject(); - } else { - throw new Exception("version mismatch"); - } - - in.close(); - - int[][] foreMap = loadMap(Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/foremap.sav")); - int[][] backMap = loadMap(Gdx.files.absolute(mainConfig.getGameFolder() + "/saves/backmap.sav")); - - if (dropController == null || mobsController == null) { - throw new Exception("couldn't load"); - } - - return new Data(mobsController, dropController, foreMap, backMap); - } catch (Exception e) { - Gdx.app.error("GameSaver", e.getMessage()); - } - - return null; - } - - public static void save(MainConfig mainConfig, - DropController dropController, - MobsController mobsController, - GameWorld gameWorld) { - - String folder = mainConfig.getGameFolder(); - FileHandle file = Gdx.files.absolute(folder + "/saves/"); - file.mkdirs(); - file = Gdx.files.absolute(folder + "/saves/game.sav"); - - 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()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static boolean exists(MainConfig mainConfig) { - String folder = mainConfig.getGameFolder(); - return (Gdx.files.absolute(folder + "/saves/game.sav").exists() && - Gdx.files.absolute(folder + "/saves/foremap.sav").exists() && - Gdx.files.absolute(folder + "/saves/backmap.sav").exists()); - } -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameScope.java b/core/src/ru/deadsoftware/cavedroid/game/GameScope.java deleted file mode 100644 index cfe440f..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/GameScope.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.deadsoftware.cavedroid.game; - -import javax.inject.Scope; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Scope -@Retention(RetentionPolicy.RUNTIME) -public @interface GameScope { -} \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameScreen.java b/core/src/ru/deadsoftware/cavedroid/game/GameScreen.java deleted file mode 100644 index 370d8bd..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/GameScreen.java +++ /dev/null @@ -1,90 +0,0 @@ -package ru.deadsoftware.cavedroid.game; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Screen; -import ru.deadsoftware.cavedroid.MainConfig; - -import javax.annotation.CheckForNull; -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class GameScreen implements Screen { - - private final MainConfig mMainConfig; - - @CheckForNull - private GameProc mGameProc; - @CheckForNull - private GameInputProcessor mGameInputProcessor; - - @Inject - public GameScreen(MainConfig mainConfig) { - mMainConfig = mainConfig; - } - - public void newGame() { - if (mGameProc != null) { - mGameProc.dispose(); - } - - GameComponent gameComponent = DaggerGameComponent.builder() - .mainComponent(mMainConfig.getMainComponent()).build(); - - mGameProc = gameComponent.getGameProc(); - mGameInputProcessor = gameComponent.getGameInputProcessor(); - - Gdx.input.setInputProcessor(gameComponent.getGameInputProcessor()); - } - - public void loadGame() { - if (mGameProc != null) { - mGameProc.dispose(); - } - - GameModule.load(mMainConfig); - - GameComponent gameComponent = DaggerGameComponent.builder() - .mainComponent(mMainConfig.getMainComponent()).build(); - - mGameProc = gameComponent.getGameProc(); - mGameInputProcessor = gameComponent.getGameInputProcessor(); - - Gdx.input.setInputProcessor(gameComponent.getGameInputProcessor()); - } - - @Override - public void render(float delta) { - mGameProc.update(delta); - } - - @Override - public void show() { - Gdx.input.setInputProcessor(mGameInputProcessor); - } - - @Override - public void resize(int width, int height) { - - } - - @Override - public void pause() { - - } - - @Override - public void resume() { - - } - - @Override - public void hide() { - - } - - @Override - public void dispose() { - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/GameWorld.java b/core/src/ru/deadsoftware/cavedroid/game/GameWorld.java deleted file mode 100644 index 3bf4981..0000000 --- a/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 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 deleted file mode 100644 index c157a4e..0000000 --- a/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> { - 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/mobs/FallingGravel.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingGravel.java deleted file mode 100644 index 23cf1dc..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingGravel.java +++ /dev/null @@ -1,42 +0,0 @@ -package ru.deadsoftware.cavedroid.game.mobs; - -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; - -/** - * Falling gravel is actually a mob, that spawns in place of gravel when there is no block under it, - * falls down to the next block and becomes a block of gravel again. - */ -public class FallingGravel extends Mob { - - /** - * Creates a FallingGravel mob at coordinates - * - * @param x X in pixels - * @param y Y in pixels - */ - public FallingGravel(float x, float y) { - super(x, y, 16, 16, Direction.LEFT, Type.GRAVEL); - mMove = new Vector2(0, 1); - } - - @Override - public void ai(GameWorld gameWorld) { - if (mMove.isZero()) { - gameWorld.setForeMap(getMapX(), getMiddleMapY(), 11); - kill(); - } - } - - @Override - public void changeDir() { - } - - @Override - public void draw(SpriteBatch spriteBatch, float x, float y) { - spriteBatch.draw(GameItems.getBlockTex(11), x, y); - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingSand.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingSand.java deleted file mode 100644 index 9a383a2..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/mobs/FallingSand.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.deadsoftware.cavedroid.game.mobs; - -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; - - -/** - * Falling sand is actually a mob, that spawns in place of gravel when there is no block under it, - * falls down to the next block and becomes a block of sand again. - */ -public class FallingSand extends Mob { - - /** - * Creates a FallingSand mob at coordinates - * - * @param x X in pixels - * @param y Y in pixels - */ - public FallingSand(float x, float y) { - super(x, y, 16, 16, Direction.LEFT, Type.SAND); - mMove = new Vector2(0, 1); - } - - @Override - public void ai(GameWorld gameWorld) { - if (mMove.isZero()) { - gameWorld.setForeMap(getMapX(), getMiddleMapY(), 10); - kill(); - } - } - - @Override - public void changeDir() { - } - - @Override - public void draw(SpriteBatch spriteBatch, float x, float y) { - spriteBatch.draw(GameItems.getBlockTex(10), x, y); - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java deleted file mode 100644 index 0f76592..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java +++ /dev/null @@ -1,177 +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.Rectangle; -import com.badlogic.gdx.math.Vector2; -import ru.deadsoftware.cavedroid.game.GameWorld; - -import java.io.Serializable; - -/** - * Mob class. - */ -public abstract class Mob extends Rectangle implements Serializable { - - public enum Type { - MOB, - SAND, - GRAVEL - } - - public enum Direction { - LEFT, - RIGHT - } - - protected Vector2 mMove; - protected Type mType; - protected int mAnimDelta = 6; - protected int mAnim; - - private Direction mDirection; - private boolean mDead; - private boolean mCanJump; - private boolean mFlyMode; - - /** - * @param x in pixels - * @param y in pixels - * @param width 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) { - super(x, y, width, height); - mMove = new Vector2(0, 0); - mCanJump = false; - mDead = false; - this.mDirection = mDirection; - this.mType = type; - } - - protected static Direction randomDir() { - return MathUtils.randomBoolean(.5f) ? Direction.LEFT : Direction.RIGHT; - } - - /** - * @return The X coordinate of a mob in blocks - */ - public final int getMapX() { - return (int) (x + (getWidth() / 2)) / 16; - } - - /** - * @return The Y coordinate of mob's upper edge in blocks - */ - public final int getUpperMapY() { - return (int) (y / 16); - } - - /** - * @return The Y coordinate if mob's vertical center in blocks - */ - public final int getMiddleMapY() { - return (int) (y + (getHeight() / 2)) / 16; - } - - /** - * @return The Y coordinate of mob's legs in blocks - */ - public final int getLowerMapY() { - return (int) (y + getHeight()) / 16; - } - - public final float getWidth() { - return width; - } - - public final float getHeight() { - return height; - } - - /** - * @return Integer representing a direction in which mob is looking, where 0 is left and 1 is right - */ - public final Direction getDirection() { - return mDirection; - } - - public final boolean looksLeft() { - return mDirection == Direction.LEFT; - } - - public final boolean looksRight() { - return mDirection == Direction.RIGHT; - } - - /** - * Switches direction in which mob is looking - */ - protected final void switchDir() { - mDirection = looksLeft() ? Direction.RIGHT : Direction.LEFT; - } - - protected final int dirMultiplier() { - return looksLeft() ? 0 : 1; - } - - public final boolean isDead() { - return mDead; - } - - public final int getAnim() { - return mAnim; - } - - /** - * Set's mob's dead variable to true and nothing else. It doesn't delete the - */ - public final void kill() { - mDead = true; - } - - public final void move() { - x += mMove.x; - y += mMove.y; - } - - public final Vector2 getMove() { - return mMove; - } - - public final boolean canJump() { - return mCanJump; - } - - public final void setCanJump(boolean canJump) { - this.mCanJump = canJump; - } - - public final boolean isFlyMode() { - return mFlyMode; - } - - public final void setFlyMode(boolean flyMode) { - this.mFlyMode = flyMode; - } - - public final Type getType() { - return mType; - } - - public void checkWorldBounds(GameWorld gameWorld) { - if (x + width / 2 < 0) { - x += gameWorld.getWidthPx(); - } - if (x + width / 2 > gameWorld.getWidthPx()) { - x -= gameWorld.getWidthPx(); - } - } - - public abstract void draw(SpriteBatch spriteBatch, float x, float y); - - public abstract void ai(GameWorld gameWorld); - - public abstract void changeDir(); -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/MobsController.java deleted file mode 100644 index cda54ee..0000000 --- a/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 mMobs = new LinkedList<>(); - - @Inject - public MobsController() { - mPlayer = new Player(); - } - - public Player getPlayer() { - return mPlayer; - } - - public void addMob(Class 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 getIterator() { - return mMobs.iterator(); - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.java deleted file mode 100644 index b362da7..0000000 --- a/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/Player.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/Player.java deleted file mode 100644 index 0b7ab8b..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/mobs/Player.java +++ /dev/null @@ -1,93 +0,0 @@ -package ru.deadsoftware.cavedroid.game.mobs; - -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.math.Vector2; -import ru.deadsoftware.cavedroid.game.GameWorld; -import ru.deadsoftware.cavedroid.misc.Assets; - -public class Player extends Mob { - - public final int[] inventory; - public int slot; - public final int gameMode; - public boolean swim; - - public Player() { - super(0, 0, 4, 30, randomDir(), Type.MOB); - this.gameMode = 1; - inventory = new int[9]; - swim = false; - } - - public void respawn(GameWorld gameWorld) { - Vector2 pos = getSpawnPoint(gameWorld); - this.x = pos.x; - this.y = pos.y; - mMove.setZero(); - } - - 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); - break; - } - if (gameWorld.hasForeAt(0, y) && gameWorld.getForeMapBlock(0, y).hasCollision()) { - break; - } - } - return new Vector2(8 - getWidth() / 2, (float) y * 16 - getHeight()); - } - - public void setDir(Direction dir) { - if (dir != getDirection()) { - switchDir(); - } - } - - @Override - public void ai(GameWorld gameWorld) { - } - - @Override - public void changeDir() { - } - - @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); - } 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); - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/mobs/package-info.java b/core/src/ru/deadsoftware/cavedroid/game/mobs/package-info.java deleted file mode 100644 index e76bf1b..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/mobs/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonnullByDefault -package ru.deadsoftware.cavedroid.game.mobs; - -import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault; \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/Block.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/Block.kt deleted file mode 100644 index d44f017..0000000 --- a/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 - - 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 deleted file mode 100644 index 3b5c760..0000000 --- a/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/DropController.java b/core/src/ru/deadsoftware/cavedroid/game/objects/DropController.java deleted file mode 100644 index 0e4c659..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/objects/DropController.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.deadsoftware.cavedroid.game.objects; - -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 DropController implements Serializable { - - public interface Callback { - void run(Drop drop); - } - - private final LinkedList mDrops = new LinkedList<>(); - - @Inject - public DropController() { - } - - public void addDrop(float x, float y, int id) { - mDrops.add(new Drop(x, y, id)); - } - - public int getSize() { - return mDrops.size(); - } - - public void forEach(Callback callback) { - mDrops.forEach(callback::run); - } - - public Iterator getIterator() { - return mDrops.iterator(); - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/Item.kt b/core/src/ru/deadsoftware/cavedroid/game/objects/Item.kt deleted file mode 100644 index 59d231d..0000000 --- a/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/objects/TouchButton.java b/core/src/ru/deadsoftware/cavedroid/game/objects/TouchButton.java deleted file mode 100644 index c33fcb5..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/objects/TouchButton.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.deadsoftware.cavedroid.game.objects; - -import com.badlogic.gdx.math.Rectangle; - -public class TouchButton { - - private final Rectangle rect; - private final int code; - private final boolean mouse; - - public TouchButton(Rectangle rect, int code, boolean mouse) { - this.rect = rect; - this.code = code; - this.mouse = mouse; - } - - public Rectangle getRect() { - return rect; - } - - public int getCode() { - return code; - } - - public boolean isMouse() { - return mouse; - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/package-info.java b/core/src/ru/deadsoftware/cavedroid/game/objects/package-info.java deleted file mode 100644 index c5856a0..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/objects/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonnullByDefault -package ru.deadsoftware.cavedroid.game.objects; - -import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault; \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/game/package-info.java b/core/src/ru/deadsoftware/cavedroid/game/package-info.java deleted file mode 100644 index 6290746..0000000 --- a/core/src/ru/deadsoftware/cavedroid/game/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonnullByDefault -package ru.deadsoftware.cavedroid.game; - -import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault; \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/menu/MenuComponent.java b/core/src/ru/deadsoftware/cavedroid/menu/MenuComponent.java deleted file mode 100644 index 5674368..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/MenuComponent.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.deadsoftware.cavedroid.menu; - -import dagger.Component; -import ru.deadsoftware.cavedroid.MainComponent; - -@MenuScope -@Component(dependencies = MainComponent.class) -public interface MenuComponent { - MenuProc getMenuProc(); -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/MenuProc.java b/core/src/ru/deadsoftware/cavedroid/menu/MenuProc.java deleted file mode 100644 index f96d4d4..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/MenuProc.java +++ /dev/null @@ -1,107 +0,0 @@ -package ru.deadsoftware.cavedroid.menu; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.utils.ObjectMap; -import ru.deadsoftware.cavedroid.CaveGame; -import ru.deadsoftware.cavedroid.MainConfig; -import ru.deadsoftware.cavedroid.menu.objects.Button; -import ru.deadsoftware.cavedroid.menu.submenus.Menu; -import ru.deadsoftware.cavedroid.menu.submenus.MenuMain; -import ru.deadsoftware.cavedroid.menu.submenus.MenuNewGame; -import ru.deadsoftware.cavedroid.misc.Renderer; - -import javax.inject.Inject; - -import static ru.deadsoftware.cavedroid.misc.Assets.*; - -@MenuScope -public class MenuProc extends Renderer { - - public class Input { - private void startNewGame(int gameMode) { - mMainConfig.getCaveGame().newGame(); - } - - public void newGameClicked() { - mCurrentMenu = mMenuNewGame; - } - - public void loadGameClicked() { - mMainConfig.getCaveGame().loadGame(); - } - - public void quitClicked() { - Gdx.app.exit(); - } - - public void survivalClicked() { - startNewGame(0); - } - - public void creativeClicked() { - startNewGame(1); - } - - public void backClicked() { - mCurrentMenu = mMenuMain; - } - } - - private final MainConfig mMainConfig; - - private final MenuMain mMenuMain; - private final MenuNewGame mMenuNewGame; - - private Menu mCurrentMenu; - - @Inject - public MenuProc(MainConfig mainConfig) { - 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); - - mCurrentMenu = mMenuMain; - } - - private void drawButton(Button button) { - spriter.draw(textureRegions.get("button_" + button.getType()), button.getX(), button.getY()); - setFontColor(255, 255, 255); - drawString(button.getLabel(), - (button.getX() + button.getWidth() / 2) - (float) getStringWidth(button.getLabel()) / 2, - (button.getY() + button.getHeight() / 2) - (float) getStringHeight(button.getLabel()) / 2); - } - - @Override - public boolean touchUp(int screenX, int screenY, int pointer, int mb) { - screenX *= getWidth() / Gdx.graphics.getWidth(); - screenY *= getHeight() / Gdx.graphics.getHeight(); - for (ObjectMap.Entry entry : mCurrentMenu.getButtons()) { - Button button = entry.value; - if (button.getRect().contains(screenX, screenY)) { - if (button.getType() > 0) { - button.clicked(); - } - break; - } - } - return false; - } - - @Override - public void render(float delta) { - spriter.begin(); - mCurrentMenu.draw(spriter); - drawString("CaveDroid " + CaveGame.VERSION, 0, - getHeight() - getStringHeight("CaveDroid " + CaveGame.VERSION) * 1.5f); - spriter.end(); - } - - public void reset() { - mCurrentMenu = mMenuMain; - } -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/MenuScope.java b/core/src/ru/deadsoftware/cavedroid/menu/MenuScope.java deleted file mode 100644 index 3198af6..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/MenuScope.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.deadsoftware.cavedroid.menu; - -import javax.inject.Scope; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Scope -@Retention(RetentionPolicy.RUNTIME) -public @interface MenuScope { -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/MenuScreen.java b/core/src/ru/deadsoftware/cavedroid/menu/MenuScreen.java deleted file mode 100644 index 4b9c5e3..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/MenuScreen.java +++ /dev/null @@ -1,57 +0,0 @@ -package ru.deadsoftware.cavedroid.menu; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Screen; -import ru.deadsoftware.cavedroid.MainConfig; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class MenuScreen implements Screen { - - private final MenuProc mMenuProc; - - @Inject - public MenuScreen(MainConfig mainConfig) { - MenuComponent menuComponent = DaggerMenuComponent.builder() - .mainComponent(mainConfig.getMainComponent()).build(); - mMenuProc = menuComponent.getMenuProc(); - } - - @Override - public void show() { - mMenuProc.reset(); - Gdx.input.setInputProcessor(mMenuProc); - } - - @Override - public void render(float delta) { - mMenuProc.render(delta); - } - - @Override - public void resize(int width, int height) { - - } - - @Override - public void pause() { - - } - - @Override - public void resume() { - - } - - @Override - public void hide() { - - } - - @Override - public void dispose() { - - } -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/objects/Button.java b/core/src/ru/deadsoftware/cavedroid/menu/objects/Button.java deleted file mode 100644 index bc78f21..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/objects/Button.java +++ /dev/null @@ -1,72 +0,0 @@ -package ru.deadsoftware.cavedroid.menu.objects; - -import com.badlogic.gdx.math.Rectangle; - -public class Button { - - public static final int WIDTH = 200; - public static final int HEIGHT = 20; - - public static final int - DISABLED = 0, - NORMAL = 1, - SELECTED = 2; - private final Rectangle rect; - private final String label; - private ButtonEventListener listener; - private int type; - - /** - * @param label Label to be shown on button - * @param type Type of button where 0 - disabled, 1 - normal, 2 - selected. - * You should use these constants - * {@link #DISABLED} {@link #NORMAL} {@link #SELECTED} - */ - public Button(String label, int x, int y, int type, ButtonEventListener listener) { - this.label = label; - rect = new Rectangle(x, y, WIDTH, HEIGHT); - this.type = type; - this.listener = listener; - } - - public Rectangle getRect() { - return rect; - } - - public String getLabel() { - return label; - } - - public float getX() { - return rect.x; - } - - public float getY() { - return rect.y; - } - - public float getWidth() { - return rect.width; - } - - public float getHeight() { - return rect.height; - } - - public int getType() { - return type; - } - - public void setType(int type) { - this.type = type; - } - - public void draw(ButtonRenderer drawer) { - drawer.draw(this); - } - - public void clicked() { - listener.buttonClicked(); - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/objects/ButtonEventListener.java b/core/src/ru/deadsoftware/cavedroid/menu/objects/ButtonEventListener.java deleted file mode 100644 index 3797295..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/objects/ButtonEventListener.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.deadsoftware.cavedroid.menu.objects; - -/** - * A {@link Button} event listener. Should be sent as lambda to Button's constructor. - */ -public interface ButtonEventListener { - - /** - * Will be called by {@link Button} when clicked - */ - void buttonClicked(); - -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/objects/ButtonRenderer.java b/core/src/ru/deadsoftware/cavedroid/menu/objects/ButtonRenderer.java deleted file mode 100644 index 6a429f2..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/objects/ButtonRenderer.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.deadsoftware.cavedroid.menu.objects; - -public interface ButtonRenderer { - - void draw(Button button); - -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/objects/package-info.java b/core/src/ru/deadsoftware/cavedroid/menu/objects/package-info.java deleted file mode 100644 index b132187..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/objects/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonnullByDefault -package ru.deadsoftware.cavedroid.menu.objects; - -import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault; \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/menu/package-info.java b/core/src/ru/deadsoftware/cavedroid/menu/package-info.java deleted file mode 100644 index 304c211..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonnullByDefault -package ru.deadsoftware.cavedroid.menu; - -import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault; \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/Menu.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/Menu.java deleted file mode 100644 index 9f8df4c..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/submenus/Menu.java +++ /dev/null @@ -1,120 +0,0 @@ -package ru.deadsoftware.cavedroid.menu.submenus; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.utils.ArrayMap; -import com.badlogic.gdx.utils.JsonValue; -import ru.deadsoftware.cavedroid.MainConfig; -import ru.deadsoftware.cavedroid.menu.MenuProc; -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.Assets; - -import java.util.HashMap; - -public abstract class Menu { - - protected final MainConfig mMainConfig; - protected final MenuProc.Input mMenuInput; - - private final ButtonRenderer mButtonRenderer; - - private final float mWidth; - private final float mHeight; - - /** - * {@link ArrayMap} of {@link Button Buttons} of this menu screen - */ - private ArrayMap buttons; - - /** - * @param width Viewport width - * @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) { - mWidth = width; - mHeight = height; - mButtonRenderer = buttonRenderer; - mMainConfig = mainConfig; - mMenuInput = menuInput; - initButtons(); - } - - /** - * If you are loading buttons from json, - * override this method and create a HashMap with buttons' keys from json as keys - * and {@link ButtonEventListener ButtonEventListeners} as values. - * - * @return empty HashMap if not overridden - */ - protected HashMap getButtonEventListeners() { - return new HashMap<>(); - } - - /** - * You can call this from {@link #initButtons()} to load buttons from json - * - * @param jsonFile A {@link FileHandle} to json file - */ - void loadButtonsFromJson(FileHandle jsonFile) { - if (buttons == null) { - buttons = new ArrayMap<>(); - } - HashMap eventListeners = getButtonEventListeners(); - JsonValue json = Assets.jsonReader.parse(jsonFile); - int y = (int) mHeight / 4; - for (JsonValue key = json.child(); key != null; key = key.next(), y += Button.HEIGHT + 10) { - buttons.put(key.name(), - new Button(Assets.getStringFromJson(key, "label", ""), - (int) mWidth / 2 - Button.WIDTH / 2, - Assets.getIntFromJson(key, "y", y), - Assets.getIntFromJson(key, "type", Button.NORMAL), - eventListeners.containsKey(key.name()) ? eventListeners.get(key.name()) : () -> { - })); - } - } - - /** - * Draws the menu with background, logo and it's buttons - * - * @param spriter {@link SpriteBatch} that will draw it. Should be already started. - */ - public void draw(SpriteBatch spriter) { - TextureRegion background = Assets.textureRegions.get("background"); - TextureRegion gamelogo = Assets.textureRegions.get("gamelogo"); - - for (int x = 0; x <= mWidth / 16; x++) { - for (int y = 0; y <= mHeight / 16; y++) { - spriter.draw(background, x * 16, y * 16); - } - } - spriter.draw(gamelogo, mWidth / 2 - (float) gamelogo.getRegionWidth() / 2, 8); - - float inputX = Gdx.input.getX() * mWidth / Gdx.graphics.getWidth(); - float inputY = Gdx.input.getY() * mHeight / Gdx.graphics.getHeight(); - for (Button button : buttons.values()) { - if (button.getType() > 0) { - if (button.getRect().contains(inputX, inputY) && (/*!CaveGame.TOUCH || */Gdx.input.isTouched())) { - button.setType(2); - } else { - button.setType(1); - } - } - button.draw(mButtonRenderer); - } - } - - public ArrayMap getButtons() { - return buttons; - } - - /** - * This method is called from constructor and should initialize {@link #buttons}
- * You can run {@link #loadButtonsFromJson(FileHandle)} from it - */ - protected abstract void initButtons(); -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuMain.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuMain.java deleted file mode 100644 index d2dedd2..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuMain.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.deadsoftware.cavedroid.menu.submenus; - -import com.badlogic.gdx.Gdx; -import ru.deadsoftware.cavedroid.MainConfig; -import ru.deadsoftware.cavedroid.game.GameSaver; -import ru.deadsoftware.cavedroid.menu.MenuProc; -import ru.deadsoftware.cavedroid.menu.objects.Button; -import ru.deadsoftware.cavedroid.menu.objects.ButtonEventListener; -import ru.deadsoftware.cavedroid.menu.objects.ButtonRenderer; - -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); - } - - @Override - protected HashMap getButtonEventListeners() { - HashMap map = new HashMap<>(); - map.put("new_game", mMenuInput::newGameClicked); - map.put("load_game", mMenuInput::loadGameClicked); - map.put("quit", mMenuInput::quitClicked); - return map; - } - - @Override - protected void initButtons() { - loadButtonsFromJson(Gdx.files.internal("json/menu_main_buttons.json")); - if (GameSaver.exists(mMainConfig)) { - getButtons().get("load_game").setType(Button.NORMAL); - } - } -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuNewGame.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuNewGame.java deleted file mode 100644 index 2c3b38b..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/submenus/MenuNewGame.java +++ /dev/null @@ -1,30 +0,0 @@ -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 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); - } - - @Override - protected HashMap getButtonEventListeners() { - HashMap map = new HashMap<>(); - map.put("survival", mMenuInput::survivalClicked); - map.put("creative", mMenuInput::creativeClicked); - map.put("back", mMenuInput::backClicked); - return map; - } - - @Override - protected void initButtons() { - loadButtonsFromJson(Gdx.files.internal("json/menu_new_game_buttons.json")); - } -} diff --git a/core/src/ru/deadsoftware/cavedroid/menu/submenus/package-info.java b/core/src/ru/deadsoftware/cavedroid/menu/submenus/package-info.java deleted file mode 100644 index 6c28493..0000000 --- a/core/src/ru/deadsoftware/cavedroid/menu/submenus/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonnullByDefault -package ru.deadsoftware.cavedroid.menu.submenus; - -import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault; \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/misc/Assets.java b/core/src/ru/deadsoftware/cavedroid/misc/Assets.java deleted file mode 100644 index 335e3c0..0000000 --- a/core/src/ru/deadsoftware/cavedroid/misc/Assets.java +++ /dev/null @@ -1,113 +0,0 @@ -package ru.deadsoftware.cavedroid.misc; - -import com.badlogic.gdx.Gdx; -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.utils.ArrayMap; -import com.badlogic.gdx.utils.JsonReader; -import com.badlogic.gdx.utils.JsonValue; -import ru.deadsoftware.cavedroid.game.objects.TouchButton; - -import java.util.HashMap; - -public class Assets { - - public static final JsonReader jsonReader = new JsonReader(); - public static final Sprite[][] playerSprite = new Sprite[2][4]; - public static final Sprite[][] pigSprite = new Sprite[2][2]; - public static final HashMap textureRegions = new HashMap<>(); - public static final ArrayMap guiMap = new ArrayMap<>(); - private static final GlyphLayout glyphLayout = new GlyphLayout(); - static BitmapFont minecraftFont; - - private static TextureRegion flippedRegion(Texture texture, int x, int y, int width, int height) { - return new TextureRegion(texture, x, y + height, width, -height); - } - - private static Sprite flippedSprite(Texture texture) { - Sprite sprite = new Sprite(texture); - sprite.flip(false, true); - return sprite; - } - - private static Sprite flippedSprite(TextureRegion texture) { - Sprite sprite = new Sprite(texture); - sprite.flip(false, true); - return sprite; - } - - private static void loadMob(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].setOrigin(sprite[i][j].getWidth() / 2, 0); - } - } - } - - /** - * Loads texture names and sizes from json/texture_regions.json, 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")); - for (JsonValue file = json.child(); file != null; file = file.next()) { - Texture texture = new Texture(Gdx.files.internal(file.name() + ".png")); - if (file.size == 0) { - textureRegions.put(file.name(), - flippedRegion(texture, 0, 0, texture.getWidth(), texture.getHeight())); - } else { - for (JsonValue key = file.child(); key != null; key = key.next()) { - int x = getIntFromJson(key, "x", 0); - int y = getIntFromJson(key, "y", 0); - int w = getIntFromJson(key, "w", texture.getWidth()); - int h = getIntFromJson(key, "h", texture.getHeight()); - textureRegions.put(key.name(), flippedRegion(texture, x, y, w, h)); - } - } - } - } - - public static void load() { - loadMob(playerSprite, "char"); - loadMob(pigSprite, "pig"); - loadJSON(); - minecraftFont = new BitmapFont(Gdx.files.internal("font.fnt"), true); - minecraftFont.getData().setScale(.375f); - } - - /** - * @param s string whose width you want to know - * @return A width of string written in {@link #minecraftFont} in pixels - */ - public static int getStringWidth(String s) { - glyphLayout.setText(minecraftFont, s); - return (int) glyphLayout.width; - } - - /** - * @param s string whose height you want to know - * @return A height of string written in {@link #minecraftFont} in pixels - */ - public static int getStringHeight(String s) { - glyphLayout.setText(minecraftFont, s); - return (int) glyphLayout.height; - } - - public static int getIntFromJson(JsonValue json, String name, int defaultValue) { - return json.has(name) ? json.getInt(name) : defaultValue; - } - - public static String getStringFromJson(JsonValue json, String name, String defaultValue) { - return json.has(name) ? json.getString(name) : defaultValue; - } - - public static boolean getBooleanFromJson(JsonValue json, String name, boolean defaultValue) { - return json.has(name) ? json.getBoolean(name) : defaultValue; - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/misc/ControlMode.java b/core/src/ru/deadsoftware/cavedroid/misc/ControlMode.java deleted file mode 100644 index dab3ac3..0000000 --- a/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 deleted file mode 100644 index b59e977..0000000 --- a/core/src/ru/deadsoftware/cavedroid/misc/Renderer.java +++ /dev/null @@ -1,108 +0,0 @@ -package ru.deadsoftware.cavedroid.misc; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.InputProcessor; -import com.badlogic.gdx.graphics.OrthographicCamera; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer; - -public abstract class Renderer implements InputProcessor { - - protected final ShapeRenderer shaper; - protected final SpriteBatch spriter; - private final OrthographicCamera camera; - - protected Renderer() { - this(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); - } - - protected Renderer(float width, float height) { - camera = new OrthographicCamera(); - camera.setToOrtho(true, width, height); - shaper = new ShapeRenderer(); - shaper.setProjectionMatrix(camera.combined); - spriter = new SpriteBatch(); - spriter.setProjectionMatrix(camera.combined); - } - - public float getWidth() { - return camera.viewportWidth; - } - - public float getHeight() { - return camera.viewportHeight; - } - - public float getCamX() { - return camera.position.x; - } - - public float getCamY() { - return camera.position.y; - } - - public void setCamPos(float x, float y) { - camera.position.set(x, y, 0); - } - - public void setFontScale(float scale) { - Assets.minecraftFont.getData().setScale(scale); - } - - protected void setFontColor(int r, int g, int b) { - Assets.minecraftFont.setColor(r / 255f, g / 255f, b / 255f, 1f); - } - - protected void drawString(String str, float x, float y) { - Assets.minecraftFont.draw(spriter, str, x, y); - } - - protected void drawString(String str) { - Assets.minecraftFont.draw(spriter, str, - getWidth() / 2 - (float) Assets.getStringWidth(str) / 2, - getHeight() / 2 - (float) Assets.getStringHeight(str) / 2); - } - - public abstract void render(float delta); - - @Override - public boolean keyDown(int keycode) { - return false; - } - - @Override - public boolean keyUp(int keycode) { - return false; - } - - @Override - public boolean keyTyped(char character) { - return false; - } - - @Override - public boolean touchDown(int screenX, int screenY, int pointer, int button) { - return false; - } - - @Override - public boolean touchUp(int screenX, int screenY, int pointer, int button) { - return false; - } - - @Override - public boolean touchDragged(int screenX, int screenY, int pointer) { - return false; - } - - @Override - public boolean mouseMoved(int screenX, int screenY) { - return false; - } - - @Override - public boolean scrolled(int amount) { - return false; - } - -} diff --git a/core/src/ru/deadsoftware/cavedroid/misc/annotations/NonnullByDefault.java b/core/src/ru/deadsoftware/cavedroid/misc/annotations/NonnullByDefault.java deleted file mode 100644 index bee4070..0000000 --- a/core/src/ru/deadsoftware/cavedroid/misc/annotations/NonnullByDefault.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.deadsoftware.cavedroid.misc.annotations; - -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import static java.lang.annotation.ElementType.*; - -@Nonnull -@TypeQualifierDefault({METHOD, FIELD, PARAMETER}) -@Retention(RetentionPolicy.RUNTIME) -public @interface NonnullByDefault { -} diff --git a/core/src/ru/deadsoftware/cavedroid/misc/package-info.java b/core/src/ru/deadsoftware/cavedroid/misc/package-info.java deleted file mode 100644 index acdf3e1..0000000 --- a/core/src/ru/deadsoftware/cavedroid/misc/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonnullByDefault -package ru.deadsoftware.cavedroid.misc; - -import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault; \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/package-info.java b/core/src/ru/deadsoftware/cavedroid/package-info.java deleted file mode 100644 index a566cb5..0000000 --- a/core/src/ru/deadsoftware/cavedroid/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonnullByDefault -package ru.deadsoftware.cavedroid; - -import ru.deadsoftware.cavedroid.misc.annotations.NonnullByDefault; \ No newline at end of file diff --git a/core/ux/controls/build.gradle.kts b/core/ux/controls/build.gradle.kts new file mode 100644 index 0000000..68aca5e --- /dev/null +++ b/core/ux/controls/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useAutomultibind() + useLibgdx() + useDagger() + + useCommonModule() + useDomainModules() + useEntityModules() + useGameModules() +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/GameInputProcessor.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/GameInputProcessor.kt new file mode 100644 index 0000000..80715c6 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/GameInputProcessor.kt @@ -0,0 +1,287 @@ +package ru.fredboy.cavedroid.ux.controls + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.Input +import com.badlogic.gdx.InputProcessor +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.model.Joystick +import ru.fredboy.cavedroid.domain.assets.model.TouchButton +import ru.fredboy.cavedroid.domain.assets.usecase.GetTouchButtonsUseCase +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.handler.mouse.CursorMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.mapper.KeyboardInputActionMapper +import ru.fredboy.cavedroid.ux.controls.input.mapper.MouseInputActionMapper +import javax.inject.Inject +import kotlin.math.abs + +@GameScope +class GameInputProcessor @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val gameContextRepository: GameContextRepository, + private val mobController: MobController, + private val getTouchButtonsUseCase: GetTouchButtonsUseCase, + private val cursorMouseInputHandler: CursorMouseInputHandler, + private val mouseInputActionMapper: MouseInputActionMapper, + private val keyboardInputActionMapper: KeyboardInputActionMapper, + private val mouseInputHandlers: Set<@JvmSuppressWildcards IMouseInputHandler>, + private val keyboardInputHandlers: Set<@JvmSuppressWildcards IKeyboardInputHandler>, + private val gameWindowsManager: GameWindowsManager, +) : InputProcessor { + + private val mouseLeftTouchButton = TouchButton( + Rectangle( + /* x = */ applicationContextRepository.getWidth() / 2, + /* y = */ 0f, + /* width = */ applicationContextRepository.getWidth() / 2, + /* height = */ applicationContextRepository.getHeight() / 2, + ), + Input.Buttons.LEFT, + true, + ) + + private val mouseRightTouchButton = TouchButton( + Rectangle( + /* x = */ applicationContextRepository.getWidth() / 2, + /* y = */ applicationContextRepository.getHeight() / 2, + /* width = */ applicationContextRepository.getWidth() / 2, + /* height = */ applicationContextRepository.getHeight() / 2, + ), + Input.Buttons.RIGHT, + true, + ) + + private var touchDownX = 0f + private var touchDownY = 0f + + override fun keyDown(keycode: Int): Boolean = handleKeyboardAction(keycode, true) + + override fun keyUp(keycode: Int): Boolean = handleKeyboardAction(keycode, false) + + override fun keyTyped(p0: Char): Boolean = false + + override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { + val (touchX, touchY) = gameContextRepository.getCameraContext().getViewportCoordinates(screenX, screenY) + + touchDownX = touchX + touchDownY = touchY + + if (applicationContextRepository.isTouch()) { + val touchedKey = getTouchedKey(touchX, touchY) + return if (touchedKey.isMouse) { + onMouseActionEvent( + mouseX = screenX, + mouseY = screenY, + button = touchedKey.code, + touchUp = false, + pointer = pointer, + ) + } else { + keyDown(touchedKey.code) + } + } + + return onMouseActionEvent(screenX, screenY, button, false, pointer) + } + + override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { + val (touchX, touchY) = gameContextRepository.getCameraContext().getViewportCoordinates(screenX, screenY) + + val joy: Joystick? = gameContextRepository.getJoystick() + + if (applicationContextRepository.isTouch()) { + if (joy != null && joy.active && joy.pointer == pointer) { + return onMouseActionEvent( + mouseX = screenX, + mouseY = screenY, + button = nullButton.code, + touchUp = true, + pointer = pointer, + ) + } + + val touchedKey: TouchButton = getTouchedKey(touchX, touchY) + + return if (touchedKey.isMouse) { + onMouseActionEvent( + mouseX = screenX, + mouseY = screenY, + button = touchedKey.code, + touchUp = true, + pointer = pointer, + ) + } else { + keyUp(touchedKey.code) + } + } + + return onMouseActionEvent( + mouseX = screenX, + mouseY = screenY, + button = button, + touchUp = true, + pointer = pointer, + ) + } + + override fun touchCancelled(p0: Int, p1: Int, p2: Int, p3: Int): Boolean = false + + override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean { + val (touchX, touchY) = gameContextRepository.getCameraContext().getViewportCoordinates(screenX, screenY) + + if (abs(touchX - touchDownX) < 16 && abs(touchY - touchDownY) < DRAG_THRESHOLD) { + return false + } + + val action = mouseInputActionMapper.mapDragged( + mouseX = screenX.toFloat(), + mouseY = screenY.toFloat(), + cameraViewport = gameContextRepository.getCameraContext().viewport, + pointer = pointer, + ) + + return handleMouseAction(action) + } + + override fun mouseMoved(p0: Int, p1: Int): Boolean = false + + override fun scrolled(amountX: Float, amountY: Float): Boolean { + val action: MouseInputAction? = mouseInputActionMapper + .mapScrolled( + mouseX = Gdx.input.x.toFloat(), + mouseY = Gdx.input.y.toFloat(), + amountX = amountX, + amountY = amountY, + cameraViewport = gameContextRepository.getCameraContext().viewport, + ) + return handleMouseAction(action) + } + + @Suppress("unused") + fun update(delta: Float) { + handleMousePosition() + } + + private val TouchButton.rectangleOnScreen + get() = Rectangle( + /* x = */ if (rectangle.x < 0f) { + applicationContextRepository.getWidth() + rectangle.x + } else { + rectangle.x + }, + /* y = */ if (rectangle.y < 0f) { + applicationContextRepository.getHeight() + rectangle.y + } else { + rectangle.y + }, + /* width = */ rectangle.width, + /* height = */ rectangle.height, + ) + + private fun getTouchedKey(touchX: Float, touchY: Float): TouchButton { + if (gameWindowsManager.currentWindowType != GameWindowType.NONE) { + return nullButton + } + + for (entry in getTouchButtonsUseCase().entries) { + val button = entry.value + if (button.rectangleOnScreen.contains(touchX, touchY)) { + return button + } + } + + if (mouseLeftTouchButton.rectangle.contains(touchX, touchY)) { + return mouseLeftTouchButton + } + + if (mouseRightTouchButton.rectangle.contains(touchX, touchY)) { + return mouseRightTouchButton + } + + return nullButton + } + + private fun handleMouseAction(action: MouseInputAction?): Boolean { + if (action == null) { + return false + } + + var anyProcessed = false + + for (handler in mouseInputHandlers) { + val conditions: Boolean = handler.checkConditions(action) + if (conditions) { + anyProcessed = true + handler.handle(action) + break + } + } + + return anyProcessed + } + + private fun handleKeyboardAction(keycode: Int, isKeyDown: Boolean): Boolean { + val action = keyboardInputActionMapper.map(keycode, isKeyDown) + + if (action == null) { + return false + } + + var anyProcessed = false + + for (handler in keyboardInputHandlers) { + val conditions: Boolean = handler.checkConditions(action) + if (conditions) { + anyProcessed = true + handler.handle(action) + break + } + } + + return anyProcessed + } + + private fun onMouseActionEvent(mouseX: Int, mouseY: Int, button: Int, touchUp: Boolean, pointer: Int): Boolean { + val action: MouseInputAction? = mouseInputActionMapper.map( + mouseX = mouseX.toFloat(), + mouseY = mouseY.toFloat(), + cameraViewport = requireNotNull(gameContextRepository.getCameraContext().viewport), + button = button, + touchUp = touchUp, + pointer = pointer, + ) + return handleMouseAction(action) + } + + private fun handleMousePosition() { + val cameraContext = gameContextRepository.getCameraContext() + + val screenX = cameraContext.xOnViewport(Gdx.input.x) + val screenY = cameraContext.yOnViewport(Gdx.input.y) + + val action = MouseInputAction( + screenX = screenX, + screenY = screenY, + actionKey = MouseInputActionKey.None, + cameraViewport = cameraContext.viewport, + ) + + cursorMouseInputHandler.handle(action) + } + + companion object { + private const val TAG = "GameInputProcessor" + + private const val DRAG_THRESHOLD = 1f + + private val nullButton = TouchButton(Rectangle(), -1, true) + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/PlaceBlockActionUtils.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/PlaceBlockActionUtils.kt new file mode 100644 index 0000000..7351be5 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/PlaceBlockActionUtils.kt @@ -0,0 +1,19 @@ +package ru.fredboy.cavedroid.ux.controls.action + +import com.badlogic.gdx.Gdx +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.ux.controls.action.placeblock.IPlaceBlockAction +import ru.fredboy.cavedroid.ux.controls.action.placeblock.PlaceBlockItemToBackgroundAction +import ru.fredboy.cavedroid.ux.controls.action.placeblock.PlaceBlockItemToForegroundAction + +private const val TAG = "PlaceBlockActionUtils" + +fun Map.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.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") +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindPlaceBlockAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindPlaceBlockAction.kt new file mode 100644 index 0000000..12de70d --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindPlaceBlockAction.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.ux.controls.action.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.ux.controls.action.placeblock.IPlaceBlockAction + +@BindsIntoMapStringKey( + interfaceClass = IPlaceBlockAction::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "PlaceBlockActionsModule", +) +annotation class BindPlaceBlockAction(val stringKey: String) diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindUseBlockAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindUseBlockAction.kt new file mode 100644 index 0000000..a0adf2e --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindUseBlockAction.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.ux.controls.action.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.ux.controls.action.useblock.IUseBlockAction + +@BindsIntoMapStringKey( + interfaceClass = IUseBlockAction::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "UseBlockActionsModule", +) +annotation class BindUseBlockAction(val stringKey: String) diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindUseItemAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindUseItemAction.kt new file mode 100644 index 0000000..a82236b --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/annotation/BindUseItemAction.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.ux.controls.action.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.ux.controls.action.useitem.IUseItemAction + +@BindsIntoMapStringKey( + interfaceClass = IUseItemAction::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "UseItemActionsModule", +) +annotation class BindUseItemAction(val stringKey: String) diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/IPlaceBlockAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/IPlaceBlockAction.kt new file mode 100644 index 0000000..3d1c4b9 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/IPlaceBlockAction.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.ux.controls.action.placeblock + +import ru.fredboy.cavedroid.domain.items.model.item.Item + +interface IPlaceBlockAction { + + fun place(placeable: Item.Placeable, x: Int, y: Int) +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceBlockItemToBackgroundAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceBlockItemToBackgroundAction.kt new file mode 100644 index 0000000..b1df6e9 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceBlockItemToBackgroundAction.kt @@ -0,0 +1,26 @@ +package ru.fredboy.cavedroid.ux.controls.action.placeblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindPlaceBlockAction +import javax.inject.Inject + +@GameScope +@BindPlaceBlockAction(stringKey = PlaceBlockItemToBackgroundAction.ACTION_KEY) +class PlaceBlockItemToBackgroundAction @Inject constructor( + private val gameWorld: GameWorld, + private val mobController: MobController, +) : IPlaceBlockAction { + + override fun place(placeable: Item.Placeable, x: Int, y: Int) { + if (gameWorld.placeToBackground(x, y, placeable.block)) { + mobController.player.decreaseCurrentItemCount() + } + } + + companion object { + const val ACTION_KEY = "place_background_block" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceBlockItemToForegroundAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceBlockItemToForegroundAction.kt new file mode 100644 index 0000000..89441b0 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceBlockItemToForegroundAction.kt @@ -0,0 +1,31 @@ +package ru.fredboy.cavedroid.ux.controls.action.placeblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindPlaceBlockAction +import javax.inject.Inject + +@GameScope +@BindPlaceBlockAction(stringKey = PlaceBlockItemToForegroundAction.ACTION_KEY) +class PlaceBlockItemToForegroundAction @Inject constructor( + private val gameWorld: GameWorld, + private val placeSlabAction: PlaceSlabAction, + private val mobController: MobController, +) : 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)) { + mobController.player.decreaseCurrentItemCount() + } + } + } + + companion object { + const val ACTION_KEY = "place_foreground_block" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceSlabAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceSlabAction.kt new file mode 100644 index 0000000..907b757 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/placeblock/PlaceSlabAction.kt @@ -0,0 +1,45 @@ +package ru.fredboy.cavedroid.ux.controls.action.placeblock + +import com.badlogic.gdx.Gdx +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindPlaceBlockAction +import javax.inject.Inject + +@GameScope +@BindPlaceBlockAction(stringKey = PlaceSlabAction.ACTION_KEY) +class PlaceSlabAction @Inject constructor( + private val gameWorld: GameWorld, + private val mobController: MobController, +) : 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)) { + mobController.player.decreaseCurrentItemCount() + } + } + + companion object { + private const val TAG = "PlaceSlabAction" + const val ACTION_KEY = "place_slab" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/IUseBlockAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/IUseBlockAction.kt new file mode 100644 index 0000000..04b79d5 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/IUseBlockAction.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.ux.controls.action.useblock + +import ru.fredboy.cavedroid.domain.items.model.block.Block + +interface IUseBlockAction { + + fun perform(block: Block, x: Int, y: Int) +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseChestAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseChestAction.kt new file mode 100644 index 0000000..dae9fb4 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseChestAction.kt @@ -0,0 +1,30 @@ +package ru.fredboy.cavedroid.ux.controls.action.useblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.world.model.Layer +import ru.fredboy.cavedroid.entity.container.model.Chest +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindUseBlockAction +import javax.inject.Inject + +@GameScope +@BindUseBlockAction(stringKey = UseChestAction.KEY) +class UseChestAction @Inject constructor( + private val containerController: ContainerController, + private val gameWindowsManager: GameWindowsManager, +) : IUseBlockAction { + + override fun perform(block: Block, x: Int, y: Int) { + // TODO: transform x + val chest = (containerController.getContainer(x, y, Layer.FOREGROUND.z) as? Chest) + ?: (containerController.getContainer(x, y, Layer.BACKGROUND.z) as? Chest) + ?: return + gameWindowsManager.openChest(chest) + } + + companion object { + const val KEY = "chest" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseCraftingTableAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseCraftingTableAction.kt new file mode 100644 index 0000000..562f55d --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseCraftingTableAction.kt @@ -0,0 +1,22 @@ +package ru.fredboy.cavedroid.ux.controls.action.useblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindUseBlockAction +import javax.inject.Inject + +@GameScope +@BindUseBlockAction(stringKey = UseCraftingTableAction.KEY) +class UseCraftingTableAction @Inject constructor( + private val gameWindowsManager: GameWindowsManager, +) : IUseBlockAction { + + override fun perform(block: Block, x: Int, y: Int) { + gameWindowsManager.openCrafting() + } + + companion object { + const val KEY = "crafting_table" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseFurnaceAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseFurnaceAction.kt new file mode 100644 index 0000000..aae58d8 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useblock/UseFurnaceAction.kt @@ -0,0 +1,33 @@ +package ru.fredboy.cavedroid.ux.controls.action.useblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.world.model.Layer +import ru.fredboy.cavedroid.entity.container.model.Furnace +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindUseBlockAction +import javax.inject.Inject + +@GameScope +@BindUseBlockAction(stringKey = UseFurnaceAction.KEY) +class UseFurnaceAction @Inject constructor( + private val containerController: ContainerController, + private val gameWindowsManager: GameWindowsManager, +) : IUseBlockAction { + + override fun perform(block: Block, x: Int, y: Int) { + // TODO: transform x + val furnace = ( + containerController.getContainer(x, y, Layer.FOREGROUND.z) + ?: containerController.getContainer(x, y, Layer.FOREGROUND.z) + ) as? Furnace + furnace ?: return + + gameWindowsManager.openFurnace(furnace) + } + + companion object { + const val KEY = "furnace" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/IUseItemAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/IUseItemAction.kt new file mode 100644 index 0000000..08b9073 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/IUseItemAction.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.ux.controls.action.useitem + +import ru.fredboy.cavedroid.domain.items.model.item.Item + +interface IUseItemAction { + + fun perform(item: Item.Usable, x: Int, y: Int) +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseBedAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseBedAction.kt new file mode 100644 index 0000000..c45ae53 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseBedAction.kt @@ -0,0 +1,33 @@ +package ru.fredboy.cavedroid.ux.controls.action.useitem + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.usecase.GetBlockByKeyUseCase +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindUseItemAction +import javax.inject.Inject + +@GameScope +@BindUseItemAction(UseBedAction.ACTION_KEY) +class UseBedAction @Inject constructor( + private val gameWorld: GameWorld, + private val mobController: MobController, + private val getBlockByKeyUseCase: GetBlockByKeyUseCase, +) : IUseItemAction { + + override fun perform(item: Item.Usable, x: Int, y: Int) { + val bedLeft = getBlockByKeyUseCase["bed_l"] + val bedRight = getBlockByKeyUseCase["bed_r"] + + if (gameWorld.canPlaceToForeground(x, y, bedLeft) && gameWorld.canPlaceToForeground(x + 1, y, bedRight)) { + gameWorld.placeToForeground(x, y, bedLeft) + gameWorld.placeToForeground(x + 1, y, bedRight) + mobController.player.decreaseCurrentItemCount() + } + } + + companion object { + const val ACTION_KEY = "use_bed_action" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseEmptyBucketAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseEmptyBucketAction.kt new file mode 100644 index 0000000..48301c0 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseEmptyBucketAction.kt @@ -0,0 +1,39 @@ +package ru.fredboy.cavedroid.ux.controls.action.useitem + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindUseItemAction +import javax.inject.Inject + +@GameScope +@BindUseItemAction(UseEmptyBucketAction.ACTION_KEY) +class UseEmptyBucketAction @Inject constructor( + private val gameWorld: GameWorld, + private val mobController: MobController, + private val getItemByKeyUseCase: GetItemByKeyUseCase, +) : 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 -> getItemByKeyUseCase["bucket_lava"] + is Block.Water -> getItemByKeyUseCase["bucket_water"] + else -> throw IllegalStateException("unknown fluid") + } + + mobController.player.setCurrentInventorySlotItem(filled) + } + + companion object { + const val ACTION_KEY = "use_empty_bucket" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseLavaBucketAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseLavaBucketAction.kt new file mode 100644 index 0000000..faf1071 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseLavaBucketAction.kt @@ -0,0 +1,32 @@ +package ru.fredboy.cavedroid.ux.controls.action.useitem + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.usecase.GetBlockByKeyUseCase +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindUseItemAction +import javax.inject.Inject + +@GameScope +@BindUseItemAction(UseLavaBucketAction.ACTION_KEY) +class UseLavaBucketAction @Inject constructor( + private val gameWorld: GameWorld, + private val mobController: MobController, + private val getBlockByKeyUseCase: GetBlockByKeyUseCase, + private val getItemByKeyUseCase: GetItemByKeyUseCase, +) : IUseItemAction { + + override fun perform(item: Item.Usable, x: Int, y: Int) { + gameWorld.placeToForeground(x, y, getBlockByKeyUseCase["lava"]) + + if (mobController.player.gameMode != 1) { + mobController.player.setCurrentInventorySlotItem(getItemByKeyUseCase["bucket_empty"]) + } + } + + companion object { + const val ACTION_KEY = "use_lava_bucket" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UsePigSpawnEggAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UsePigSpawnEggAction.kt new file mode 100644 index 0000000..7c278a6 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UsePigSpawnEggAction.kt @@ -0,0 +1,26 @@ +package ru.fredboy.cavedroid.ux.controls.action.useitem + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.controller.mob.factory.PigFactory +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindUseItemAction +import javax.inject.Inject + +@GameScope +@BindUseItemAction(UsePigSpawnEggAction.ACTION_KEY) +class UsePigSpawnEggAction @Inject constructor( + private val mobController: MobController, + private val pigFactory: PigFactory, +) : IUseItemAction { + + override fun perform(item: Item.Usable, x: Int, y: Int) { + pigFactory.create(mobController.player.cursorX.px, mobController.player.cursorY.px) + mobController.player.decreaseCurrentItemCount() + } + + companion object { + const val ACTION_KEY = "use_spawn_egg_pig" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseWaterBucketAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseWaterBucketAction.kt new file mode 100644 index 0000000..c02f5e1 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/action/useitem/UseWaterBucketAction.kt @@ -0,0 +1,31 @@ +package ru.fredboy.cavedroid.ux.controls.action.useitem + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.domain.items.usecase.GetBlockByKeyUseCase +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.action.annotation.BindUseItemAction +import javax.inject.Inject + +@GameScope +@BindUseItemAction(UseWaterBucketAction.ACTION_KEY) +class UseWaterBucketAction @Inject constructor( + private val gameWorld: GameWorld, + private val mobsController: MobController, + private val getBlockByKeyUseCase: GetBlockByKeyUseCase, + private val getItemByKeyUseCase: GetItemByKeyUseCase, +) : IUseItemAction { + + override fun perform(item: Item.Usable, x: Int, y: Int) { + gameWorld.placeToForeground(x, y, getBlockByKeyUseCase["water"]) + if (mobsController.player.gameMode != 1) { + mobsController.player.setCurrentInventorySlotItem(getItemByKeyUseCase["bucket_empty"]) + } + } + + companion object { + const val ACTION_KEY = "use_water_bucket" + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/IGameInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/IGameInputHandler.kt new file mode 100644 index 0000000..5cccba3 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/IGameInputHandler.kt @@ -0,0 +1,24 @@ +package ru.fredboy.cavedroid.ux.controls.input + +import ru.fredboy.cavedroid.ux.controls.input.action.IGameInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction + +interface IKeyboardInputHandler : IGameInputHandler + +interface IMouseInputHandler : IGameInputHandler + +interface IGameInputHandler { + + /** + * 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) +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/InputUtils.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/InputUtils.kt new file mode 100644 index 0000000..4552af8 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/InputUtils.kt @@ -0,0 +1,18 @@ +package ru.fredboy.cavedroid.ux.controls.input + +import com.badlogic.gdx.graphics.g2d.TextureRegion +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction + +fun MouseInputAction.isInsideHotbar(getTextureRegionByNameUseCase: GetTextureRegionByNameUseCase): Boolean { + val hotbar = requireNotNull(getTextureRegionByNameUseCase["hotbar"]) + + return this.screenY <= hotbar.regionHeight && + this.screenX >= this.cameraViewport.width / 2 - hotbar.regionWidth / 2 && + this.screenX <= this.cameraViewport.width / 2 + hotbar.regionWidth / 2 +} + +fun isInsideWindow(action: MouseInputAction, windowTexture: TextureRegion): Boolean = 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/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/IGameInputAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/IGameInputAction.kt new file mode 100644 index 0000000..41b9f22 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/IGameInputAction.kt @@ -0,0 +1,3 @@ +package ru.fredboy.cavedroid.ux.controls.input.action + +interface IGameInputAction diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/KeyboardInputAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/KeyboardInputAction.kt new file mode 100644 index 0000000..bc6dac5 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/KeyboardInputAction.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.ux.controls.input.action + +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey + +data class KeyboardInputAction( + val actionKey: KeyboardInputActionKey, + val isKeyDown: Boolean, +) : IGameInputAction diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/MouseInputAction.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/MouseInputAction.kt new file mode 100644 index 0000000..620f029 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/MouseInputAction.kt @@ -0,0 +1,11 @@ +package ru.fredboy.cavedroid.ux.controls.input.action + +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey + +data class MouseInputAction( + val screenX: Float, + val screenY: Float, + val actionKey: MouseInputActionKey, + val cameraViewport: Rectangle, +) : IGameInputAction diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/keys/KeyboardInputActionKey.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/keys/KeyboardInputActionKey.kt new file mode 100644 index 0000000..bbbd3b9 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/keys/KeyboardInputActionKey.kt @@ -0,0 +1,28 @@ +package ru.fredboy.cavedroid.ux.controls.input.action.keys + +sealed interface KeyboardInputActionKey { + + data object Left : KeyboardInputActionKey + data object Right : KeyboardInputActionKey + data object Down : KeyboardInputActionKey + data object Up : KeyboardInputActionKey + + data object Crouch : KeyboardInputActionKey + + data object DropItem : 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 class SelectHotbarSlot( + val slot: Int, + ) : KeyboardInputActionKey +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/keys/MouseInputActionKey.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/keys/MouseInputActionKey.kt new file mode 100644 index 0000000..a7ffd01 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/action/keys/MouseInputActionKey.kt @@ -0,0 +1,47 @@ +package ru.fredboy.cavedroid.ux.controls.input.action.keys + +sealed interface MouseInputActionKey { + + val touchUp: Boolean + + sealed interface Touch : MouseInputActionKey { + val pointer: Int + } + + data object None : MouseInputActionKey { + override val touchUp: Boolean + get() = throw IllegalAccessException("not applicable for mouse move action") + } + + data class Dragged( + override val pointer: Int, + ) : Touch { + 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 Screen( + override val touchUp: Boolean, + override val pointer: Int, + ) : Touch + + data class Scroll( + val amountX: Float, + val amountY: Float, + ) : MouseInputActionKey { + override val touchUp: Boolean + get() = throw IllegalAccessException("not applicable for mouse scroll action") + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/annotation/BindKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/annotation/BindKeyboardInputHandler.kt new file mode 100644 index 0000000..30bc23c --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/annotation/BindKeyboardInputHandler.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.ux.controls.input.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoSet +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler + +@BindsIntoSet( + interfaceClass = IKeyboardInputHandler::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "KeyboardInputHandlersModule", +) +annotation class BindKeyboardInputHandler diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/annotation/BindMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/annotation/BindMouseInputHandler.kt new file mode 100644 index 0000000..bfd5d88 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/annotation/BindMouseInputHandler.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.ux.controls.input.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoSet +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler + +@BindsIntoSet( + interfaceClass = IMouseInputHandler::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "MouseInputHandlersModule", +) +annotation class BindMouseInputHandler diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/CloseGameWindowKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/CloseGameWindowKeyboardInputHandler.kt new file mode 100644 index 0000000..fea5ebc --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/CloseGameWindowKeyboardInputHandler.kt @@ -0,0 +1,40 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class CloseGameWindowKeyboardInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val dropController: DropController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.OpenInventory && + !action.isKeyDown && + gameWindowsManager.currentWindowType != GameWindowType.NONE + + override fun handle(action: KeyboardInputAction) { + val selectedItem = gameWindowsManager.currentWindow?.selectedItem + if (selectedItem != null) { + for (i in 1..selectedItem.amount) { + dropController.addDrop( + /* x = */ mobController.player.x + (32f * mobController.player.direction.basis), + /* y = */ mobController.player.y, + /* item = */ selectedItem, + ) + } + gameWindowsManager.currentWindow?.selectedItem = null + } + gameWindowsManager.closeWindow() + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/DropItemKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/DropItemKeyboardInputHandler.kt new file mode 100644 index 0000000..438780a --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/DropItemKeyboardInputHandler.kt @@ -0,0 +1,50 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.entity.drop.model.Drop +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class DropItemKeyboardInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val dropController: DropController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.DropItem && + action.isKeyDown && + gameWindowsManager.currentWindowType == GameWindowType.NONE && + !mobController.player.activeItem.item.isNone() + + private fun createDrop(item: Item, playerX: Float, playerY: Float, amount: Int) { + dropController.addDrop( + /* x = */ playerX + ((DROP_DISTANCE - Drop.DROP_SIZE / 2) * mobController.player.direction.basis), + /* y = */ playerY, + /* item = */ item, + /* count = */ amount, + ) + } + + override fun handle(action: KeyboardInputAction) { + val player = mobController.player + val currentItem = player.activeItem + val dropAmount = if (currentItem.item.isTool()) currentItem.amount else 1 + + createDrop(currentItem.item, player.x, player.y, dropAmount) + player.decreaseCurrentItemCount(dropAmount) + } + + companion object { + const val DROP_DISTANCE = 20f + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/FlyDownKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/FlyDownKeyboardInputHandler.kt new file mode 100644 index 0000000..f68c6b9 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/FlyDownKeyboardInputHandler.kt @@ -0,0 +1,31 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class FlyDownKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.Down && + mobController.player.isFlyMode && + (mobController.player.controlMode == Player.ControlMode.WALK || !applicationContextRepository.isTouch()) + + override fun handle(action: KeyboardInputAction) { + if (action.isKeyDown) { + mobController.player.velocity.y = mobController.player.speed + } else { + mobController.player.velocity.y = 0f + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/FlyUpKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/FlyUpKeyboardInputHandler.kt new file mode 100644 index 0000000..2fc4677 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/FlyUpKeyboardInputHandler.kt @@ -0,0 +1,32 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class FlyUpKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.Up && + !mobController.player.swim && + mobController.player.isFlyMode && + (mobController.player.controlMode == Player.ControlMode.WALK || !applicationContextRepository.isTouch()) + + override fun handle(action: KeyboardInputAction) { + if (action.isKeyDown) { + mobController.player.velocity.y = -mobController.player.speed + } else { + mobController.player.velocity.y = 0f + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/GoLeftKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/GoLeftKeyboardInputHandler.kt new file mode 100644 index 0000000..4db1e24 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/GoLeftKeyboardInputHandler.kt @@ -0,0 +1,36 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Direction +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class GoLeftKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean { + val isTouch = applicationContextRepository.isTouch() + return action.actionKey is KeyboardInputActionKey.Left && + (mobController.player.controlMode == Player.ControlMode.WALK || !isTouch) && + (mobController.player.controlMode == Player.ControlMode.WALK || !isTouch) + } + + override fun handle(action: KeyboardInputAction) { + if (action.isKeyDown) { + mobController.player.velocity.x = -mobController.player.speed + mobController.player.direction = Direction.LEFT + } else { + mobController.player.velocity.x = 0f + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/GoRightKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/GoRightKeyboardInputHandler.kt new file mode 100644 index 0000000..b36011a --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/GoRightKeyboardInputHandler.kt @@ -0,0 +1,32 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Direction +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class GoRightKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.Right && + (mobController.player.controlMode == Player.ControlMode.WALK || !applicationContextRepository.isTouch()) + + override fun handle(action: KeyboardInputAction) { + if (action.isKeyDown) { + mobController.player.velocity.x = mobController.player.speed + mobController.player.direction = Direction.RIGHT + } else { + mobController.player.velocity.x = 0f + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/JumpKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/JumpKeyboardInputHandler.kt new file mode 100644 index 0000000..22dc03b --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/JumpKeyboardInputHandler.kt @@ -0,0 +1,29 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class JumpKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.Up && + mobController.player.canJump && + !mobController.player.isFlyMode && + action.isKeyDown && + (mobController.player.controlMode == Player.ControlMode.WALK || !applicationContextRepository.isTouch()) + + override fun handle(action: KeyboardInputAction) { + mobController.player.jump() + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/MoveCursorControlsModeKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/MoveCursorControlsModeKeyboardInputHandler.kt new file mode 100644 index 0000000..ea09181 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/MoveCursorControlsModeKeyboardInputHandler.kt @@ -0,0 +1,43 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class MoveCursorControlsModeKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobsController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = applicationContextRepository.isTouch() && + mobsController.player.controlMode == Player.ControlMode.CURSOR && + action.isKeyDown && + ( + action.actionKey is KeyboardInputActionKey.Left || + action.actionKey is KeyboardInputActionKey.Right || + action.actionKey is KeyboardInputActionKey.Up || + action.actionKey is KeyboardInputActionKey.Down + ) + + override fun handle(action: KeyboardInputAction) { + val player = mobsController.player + + when (action.actionKey) { + KeyboardInputActionKey.Left -> player.cursorX-- + KeyboardInputActionKey.Right -> player.cursorX++ + KeyboardInputActionKey.Up -> player.cursorY-- + KeyboardInputActionKey.Down -> player.cursorY++ + else -> return + } + + mobsController.checkPlayerCursorBounds() + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/OpenInventoryKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/OpenInventoryKeyboardInputHandler.kt new file mode 100644 index 0000000..412fe1d --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/OpenInventoryKeyboardInputHandler.kt @@ -0,0 +1,31 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class OpenInventoryKeyboardInputHandler @Inject constructor( + private val mobController: MobController, + private val gameWindowsManager: GameWindowsManager, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.OpenInventory && + !action.isKeyDown && + gameWindowsManager.currentWindowType == GameWindowType.NONE + + override fun handle(action: KeyboardInputAction) { + if (mobController.player.gameMode == 1) { + gameWindowsManager.openCreativeInventory() + } else { + gameWindowsManager.openSurvivalInventory() + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/PauseGameKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/PauseGameKeyboardInputHandler.kt new file mode 100644 index 0000000..6d84228 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/PauseGameKeyboardInputHandler.kt @@ -0,0 +1,50 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.api.ApplicationController +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.save.repository.SaveDataRepository +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class PauseGameKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val gameController: ApplicationController, + private val dropController: DropController, + private val mobController: MobController, + private val gameWorld: GameWorld, + private val containerController: ContainerController, + private val gameWindowsManager: GameWindowsManager, + private val saveDataRepository: SaveDataRepository, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.Pause && action.isKeyDown + + override fun handle(action: KeyboardInputAction) { + if (gameWindowsManager.currentWindowType != GameWindowType.NONE) { + gameWindowsManager.closeWindow() + return + } + + saveDataRepository.save( + gameDataFolder = applicationContextRepository.getGameDirectory(), + dropController = dropController, + mobController = mobController, + containerController = containerController, + gameWorld = gameWorld, + ) + + gameController.quitGame() + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/SelectHotbarSlotKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/SelectHotbarSlotKeyboardInputHandler.kt new file mode 100644 index 0000000..63cc753 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/SelectHotbarSlotKeyboardInputHandler.kt @@ -0,0 +1,23 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class SelectHotbarSlotKeyboardInputHandler @Inject constructor( + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.SelectHotbarSlot && + action.isKeyDown + + override fun handle(action: KeyboardInputAction) { + mobController.player.activeSlot = (action.actionKey as KeyboardInputActionKey.SelectHotbarSlot).slot + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/StopSwimKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/StopSwimKeyboardInputHandler.kt new file mode 100644 index 0000000..5c4e5c0 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/StopSwimKeyboardInputHandler.kt @@ -0,0 +1,28 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class StopSwimKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.Up && + !action.isKeyDown && + mobController.player.swim && + (mobController.player.controlMode == Player.ControlMode.WALK || !applicationContextRepository.isTouch()) + + override fun handle(action: KeyboardInputAction) { + mobController.player.swim = false + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/SwimUpKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/SwimUpKeyboardInputHandler.kt new file mode 100644 index 0000000..1b544fa --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/SwimUpKeyboardInputHandler.kt @@ -0,0 +1,35 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class SwimUpKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, + private val gameWorld: GameWorld, +) : IKeyboardInputHandler { + + private fun checkSwim(): Boolean = gameWorld.getForeMap(mobController.player.mapX, mobController.player.lowerMapY).isFluid() + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.Up && + action.isKeyDown && + !mobController.player.swim && + !mobController.player.canJump && + checkSwim() && + !mobController.player.isFlyMode && + (mobController.player.controlMode == Player.ControlMode.WALK || !applicationContextRepository.isTouch()) + + override fun handle(action: KeyboardInputAction) { + mobController.player.swim = true + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleControlsModeKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleControlsModeKeyboardInputHandler.kt new file mode 100644 index 0000000..3d1ce40 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleControlsModeKeyboardInputHandler.kt @@ -0,0 +1,31 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class ToggleControlsModeKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.SwitchControlsMode && + !action.isKeyDown && + applicationContextRepository.isTouch() + + override fun handle(action: KeyboardInputAction) { + if (mobController.player.controlMode == Player.ControlMode.WALK) { + mobController.player.controlMode = Player.ControlMode.CURSOR + } else { + mobController.player.controlMode = Player.ControlMode.WALK + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleDebugInfoKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleDebugInfoKeyboardInputHandler.kt new file mode 100644 index 0000000..5988629 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleDebugInfoKeyboardInputHandler.kt @@ -0,0 +1,22 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class ToggleDebugInfoKeyboardInputHandler @Inject constructor( + private val gameContextRepository: GameContextRepository, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.ShowDebug && action.isKeyDown + + override fun handle(action: KeyboardInputAction) { + gameContextRepository.setShowInfo(!gameContextRepository.shouldShowInfo()) + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleGameModeKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleGameModeKeyboardInputHandler.kt new file mode 100644 index 0000000..4a2f624 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleGameModeKeyboardInputHandler.kt @@ -0,0 +1,26 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class ToggleGameModeKeyboardInputHandler @Inject constructor( + private val mobController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.SwitchGameMode && action.isKeyDown + + override fun handle(action: KeyboardInputAction) { + if (mobController.player.gameMode == 1) { + mobController.player.gameMode = 0 + } else if (mobController.player.gameMode == 0) { + mobController.player.gameMode = 1 + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleMinimapKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleMinimapKeyboardInputHandler.kt new file mode 100644 index 0000000..a7450a8 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/ToggleMinimapKeyboardInputHandler.kt @@ -0,0 +1,22 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class ToggleMinimapKeyboardInputHandler @Inject constructor( + private val gameContextRepository: GameContextRepository, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = action.actionKey is KeyboardInputActionKey.ShowMap && action.isKeyDown + + override fun handle(action: KeyboardInputAction) { + gameContextRepository.setShowMap(!gameContextRepository.shouldShowMap()) + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/TurnOnFlyModeKeyboardInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/TurnOnFlyModeKeyboardInputHandler.kt new file mode 100644 index 0000000..4fa80ec --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/keyboard/TurnOnFlyModeKeyboardInputHandler.kt @@ -0,0 +1,32 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.keyboard + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.ux.controls.input.IKeyboardInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.KeyboardInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindKeyboardInputHandler +import javax.inject.Inject + +@GameScope +@BindKeyboardInputHandler +class TurnOnFlyModeKeyboardInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobsController: MobController, +) : IKeyboardInputHandler { + + override fun checkConditions(action: KeyboardInputAction): Boolean = mobsController.player.gameMode == 1 && + action.actionKey is KeyboardInputActionKey.Up && + !mobsController.player.swim && + !mobsController.player.isFlyMode && + !mobsController.player.canJump && + action.isKeyDown && + (mobsController.player.controlMode == Player.ControlMode.WALK || !applicationContextRepository.isTouch()) + + override fun handle(action: KeyboardInputAction) { + mobsController.player.isFlyMode = true + mobsController.player.velocity.y = 0f + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/AbstractInventoryItemsMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/AbstractInventoryItemsMouseInputHandler.kt new file mode 100644 index 0000000..9c5db6c --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/AbstractInventoryItemsMouseInputHandler.kt @@ -0,0 +1,104 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import com.badlogic.gdx.graphics.g2d.TextureRegion +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem.Companion.isNoneOrNull +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.AbstractInventoryWindow +import ru.fredboy.cavedroid.game.window.inventory.AbstractInventoryWindowWithCraftGrid +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.isInsideWindow + +abstract class AbstractInventoryItemsMouseInputHandler( + private val itemsRepository: ItemsRepository, + private val gameWindowsManager: GameWindowsManager, + private val windowType: GameWindowType, +) : IMouseInputHandler { + + protected abstract val windowTexture: TextureRegion + + override fun checkConditions(action: MouseInputAction): Boolean = gameWindowsManager.currentWindowType == windowType && + isInsideWindow(action, windowTexture) && + ( + action.actionKey is MouseInputActionKey.Left || + action.actionKey is MouseInputActionKey.Right || + action.actionKey is MouseInputActionKey.Screen + ) && + (action.actionKey.touchUp || action.actionKey is MouseInputActionKey.Screen) + + protected fun updateCraftResult(window: AbstractInventoryWindowWithCraftGrid) { + window.craftResult = itemsRepository.getCraftingResult(window.craftingItems.map(InventoryItem::item)) + } + + private fun reduceCraftItems(window: AbstractInventoryWindowWithCraftGrid) { + for (i in window.craftingItems.indices) { + if (window.craftingItems[i].amount > 1) { + window.craftingItems[i].amount-- + } else { + window.craftingItems[i] = itemsRepository.fallbackItem.toInventoryItem() + } + } + } + + protected fun handleInsidePlaceableCell( + action: MouseInputAction, + items: MutableList, + window: AbstractInventoryWindow, + index: Int, + ) { + if (action.actionKey is MouseInputActionKey.Screen) { + if (!action.actionKey.touchUp) { + window.onLeftCLick(items, itemsRepository, index, action.actionKey.pointer) + } else { + if (action.actionKey.pointer == window.selectItemPointer) { + window.onLeftCLick(items, itemsRepository, index, action.actionKey.pointer) + } else { + window.onRightClick(items, itemsRepository, index) + } + } + } else if (action.actionKey is MouseInputActionKey.Left) { + window.onLeftCLick(items, itemsRepository, index) + } else { + window.onRightClick(items, itemsRepository, index) + } + } + + protected fun handleInsideCraftResultCell( + action: MouseInputAction, + items: MutableList, + window: AbstractInventoryWindow, + index: Int, + ) { + val selectedItem = window.selectedItem + + if (!selectedItem.isNoneOrNull() && + ( + selectedItem.item != items[index].item || + !selectedItem.canBeAdded(items[index].amount) + ) + ) { + return + } + + if (!selectedItem.isNoneOrNull()) { + selectedItem.amount += items[index].amount + items[index] = itemsRepository.fallbackItem.toInventoryItem() + } else { + if (action.actionKey is MouseInputActionKey.Screen) { + if (!action.actionKey.touchUp) { + window.onLeftCLick(items, itemsRepository, index, action.actionKey.pointer) + } + } else if (action.actionKey is MouseInputActionKey.Left) { + window.onLeftCLick(items, itemsRepository, index) + } + } + + if (window is AbstractInventoryWindowWithCraftGrid) { + reduceCraftItems(window) + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/AttackMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/AttackMouseInputHandler.kt new file mode 100644 index 0000000..f12b4fc --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/AttackMouseInputHandler.kt @@ -0,0 +1,34 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.isInsideHotbar +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class AttackMouseInputHandler @Inject constructor( + private val mobController: MobController, + private val gameWindowsManager: GameWindowsManager, + private val textureRegions: GetTextureRegionByNameUseCase, +) : IMouseInputHandler { + + override fun checkConditions(action: MouseInputAction): Boolean = gameWindowsManager.currentWindowType == GameWindowType.NONE && + !action.isInsideHotbar(textureRegions) && + action.actionKey is MouseInputActionKey.Left + + override fun handle(action: MouseInputAction) { + if (action.actionKey.touchUp) { + mobController.player.stopHitting() + } else { + mobController.player.startHitting() + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CloseGameWindowMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CloseGameWindowMouseInputHandler.kt new file mode 100644 index 0000000..c5a80da --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CloseGameWindowMouseInputHandler.kt @@ -0,0 +1,59 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import com.badlogic.gdx.graphics.g2d.TextureRegion +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.isInsideWindow +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class CloseGameWindowMouseInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val dropController: DropController, + private val textureRegions: GetTextureRegionByNameUseCase, +) : IMouseInputHandler { + + private val creativeInventoryTexture get() = requireNotNull(textureRegions["creative"]) + private val survivalInventoryTexture get() = requireNotNull(textureRegions["survival"]) + private val craftingInventoryTexture get() = requireNotNull(textureRegions["crafting_table"]) + private val furnaceInventoryTexture get() = requireNotNull(textureRegions["furnace"]) + private val chestInventoryTexture get() = requireNotNull(textureRegions["chest"]) + + override fun checkConditions(action: MouseInputAction): Boolean = gameWindowsManager.currentWindowType != GameWindowType.NONE && + (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Screen) && + action.actionKey.touchUp && + !isInsideWindow(action, getCurrentWindowTexture()) + + private fun getCurrentWindowTexture(): TextureRegion = when (val window = gameWindowsManager.currentWindowType) { + GameWindowType.CREATIVE_INVENTORY -> creativeInventoryTexture + GameWindowType.SURVIVAL_INVENTORY -> survivalInventoryTexture + GameWindowType.CRAFTING_TABLE -> craftingInventoryTexture + GameWindowType.FURNACE -> furnaceInventoryTexture + GameWindowType.CHEST -> chestInventoryTexture + else -> throw UnsupportedOperationException("Cant close window ${window.name}") + } + + override fun handle(action: MouseInputAction) { + val selectedItem = gameWindowsManager.currentWindow?.selectedItem + if (selectedItem != null) { + dropController.addDrop( + /* x = */ mobController.player.x + (32f * mobController.player.direction.basis), + /* y = */ mobController.player.y, + /* item = */ selectedItem.item, + /* count = */ selectedItem.amount, + ) + gameWindowsManager.currentWindow?.selectedItem = null + } + gameWindowsManager.closeWindow() + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CreativeInventoryScrollMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CreativeInventoryScrollMouseInputHandler.kt new file mode 100644 index 0000000..fd73e88 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CreativeInventoryScrollMouseInputHandler.kt @@ -0,0 +1,94 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import com.badlogic.gdx.math.MathUtils +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.CreativeInventoryWindow +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.isInsideWindow +import javax.inject.Inject +import kotlin.math.abs + +@GameScope +@BindMouseInputHandler +class CreativeInventoryScrollMouseInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val gameWindowsManager: GameWindowsManager, + private val itemsRepository: ItemsRepository, + private val textureRegions: GetTextureRegionByNameUseCase, +) : IMouseInputHandler { + + private val creativeInventoryTexture get() = requireNotNull(textureRegions["creative"]) + + private var dragStartY = 0f + + override fun checkConditions(action: MouseInputAction): Boolean = gameWindowsManager.currentWindowType == GameWindowType.CREATIVE_INVENTORY && + (gameWindowsManager.isDragging || isInsideWindow(action, creativeInventoryTexture)) && + ( + checkStartDragConditions(action) || + checkEndDragConditions(action) || + checkDragConditions(action) || + action.actionKey is MouseInputActionKey.Scroll + ) + + private fun checkStartDragConditions(action: MouseInputAction): Boolean = (action.actionKey is MouseInputActionKey.Screen) && + !action.actionKey.touchUp && + !gameWindowsManager.isDragging + + private fun checkEndDragConditions(action: MouseInputAction): Boolean = action.actionKey is MouseInputActionKey.Screen && + action.actionKey.touchUp && + gameWindowsManager.isDragging + + private fun checkDragConditions(action: MouseInputAction): Boolean = applicationContextRepository.isTouch() && + action.actionKey is MouseInputActionKey.Dragged && + abs(action.screenY - dragStartY) >= DRAG_SENSITIVITY + + private fun clampScrollAmount() { + gameWindowsManager.creativeScrollAmount = + MathUtils.clamp( + /* value = */ gameWindowsManager.creativeScrollAmount, + /* min = */ 0, + /* max = */ (gameWindowsManager.currentWindow as CreativeInventoryWindow).getMaxScroll(itemsRepository), + ) + } + + 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.Screen -> handleStartOrEndDrag(action) + is MouseInputActionKey.Dragged -> handleDrag(action) + is MouseInputActionKey.Scroll -> handleScroll(action) + else -> return + } + } + + companion object { + private const val DRAG_SENSITIVITY = 16f + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CursorMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CursorMouseInputHandler.kt new file mode 100644 index 0000000..5cd6a27 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/CursorMouseInputHandler.kt @@ -0,0 +1,160 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import com.badlogic.gdx.math.MathUtils +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.bl +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByIndexUseCase +import ru.fredboy.cavedroid.entity.mob.model.Direction +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.TooltipManager +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class CursorMouseInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val mobController: MobController, + private val gameWorld: GameWorld, + private val gameWindowsManager: GameWindowsManager, + private val tooltipManager: TooltipManager, + private val textureRegions: GetTextureRegionByNameUseCase, + private val getItemByIndexUseCase: GetItemByIndexUseCase, +) : IMouseInputHandler { + + private val player get() = mobController.player + + private val creativeInventoryTexture get() = requireNotNull(textureRegions["creative"]) + + private val Block.isAutoselectable + get() = !isNone() && params.hasCollision + + private fun GameWorld.isCurrentBlockAutoselectable() = getForeMap(player.cursorX, player.cursorY).isAutoselectable + + private fun setPlayerDirectionToCursor() { + if (player.controlMode != Player.ControlMode.CURSOR) { + return + } + + if (player.cursorX.px + 8 < player.x + player.width / 2) { + player.direction = Direction.LEFT + } else { + player.direction = Direction.RIGHT + } + } + + private fun handleWalkTouch() { + player.cursorX = player.mapX + player.direction.basis + player.cursorY = player.upperMapY + player.headRotation = 0f + + for (i in 1..2) { + if (gameWorld.isCurrentBlockAutoselectable()) { + break + } + player.cursorY++ + } + + if (!gameWorld.isCurrentBlockAutoselectable()) { + player.cursorX -= player.direction.basis + } + } + + private fun getPlayerHeadRotation(mouseWorldX: Float, mouseWorldY: Float): Float { + val h = mouseWorldX - (player.x + player.width / 2) + 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) + + if (worldX < player.x + player.width / 2) { + player.direction = Direction.LEFT + } else { + player.direction = Direction.RIGHT + } + } + + private fun getCreativeTooltip(action: MouseInputAction): String? { + val creativeTexture = creativeInventoryTexture + val xOnGrid = ( + action.screenX - ( + action.cameraViewport.width / 2 - creativeTexture.regionWidth / 2 + + GameWindowsConfigs.Creative.itemsGridMarginLeft + ) + ) / + GameWindowsConfigs.Creative.itemsGridColWidth + val yOnGrid = ( + action.screenY - ( + action.cameraViewport.height / 2 - creativeTexture.regionHeight / 2 + + GameWindowsConfigs.Creative.itemsGridMarginTop + ) + ) / + GameWindowsConfigs.Creative.itemsGridRowHeight + + if (xOnGrid < 0 || + xOnGrid >= GameWindowsConfigs.Creative.itemsInRow || + yOnGrid < 0 || + yOnGrid >= GameWindowsConfigs.Creative.itemsInCol + ) { + return null + } + + val itemIndex = ( + gameWindowsManager.creativeScrollAmount * GameWindowsConfigs.Creative.itemsInRow + + (xOnGrid.toInt() + yOnGrid.toInt() * GameWindowsConfigs.Creative.itemsInRow) + ) + val item = getItemByIndexUseCase[itemIndex] + + return item.params.name + } + + override fun checkConditions(action: MouseInputAction): Boolean = action.actionKey is MouseInputActionKey.None + + override fun handle(action: MouseInputAction) { + val pastCursorX = player.cursorX + val pastCursorY = player.cursorY + + when { + player.controlMode == Player.ControlMode.WALK && applicationContextRepository.isTouch() -> handleWalkTouch() + !applicationContextRepository.isTouch() -> handleMouse(action) + } + + mobController.checkPlayerCursorBounds() + + if (player.controlMode == Player.ControlMode.WALK && applicationContextRepository.isTouch()) { + setPlayerDirectionToCursor() + } + + if (player.cursorX != pastCursorX || player.cursorY != pastCursorY) { + player.blockDamage = 0f + } + + if (gameWindowsManager.currentWindowType == GameWindowType.CREATIVE_INVENTORY) { + tooltipManager.showMouseTooltip(getCreativeTooltip(action).orEmpty()) + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/HotbarMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/HotbarMouseInputHandler.kt new file mode 100644 index 0000000..48d3d31 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/HotbarMouseInputHandler.kt @@ -0,0 +1,130 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import com.badlogic.gdx.utils.Timer +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.entity.drop.model.Drop +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.handler.keyboard.DropItemKeyboardInputHandler.Companion.DROP_DISTANCE +import ru.fredboy.cavedroid.ux.controls.input.isInsideHotbar +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class HotbarMouseInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val dropController: DropController, + private val textureRegions: GetTextureRegionByNameUseCase, +) : IMouseInputHandler { + + private val hotbarTexture get() = requireNotNull(textureRegions["hotbar"]) + + private var buttonHoldTask: Timer.Task? = null + + override fun checkConditions(action: MouseInputAction): Boolean = buttonHoldTask?.isScheduled == true || + ( + (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Screen) && + action.isInsideHotbar(textureRegions) || + action.actionKey is MouseInputActionKey.Scroll + ) && + gameWindowsManager.currentWindowType == GameWindowType.NONE + + private fun cancelHold() { + buttonHoldTask?.cancel() + buttonHoldTask = null + } + + private fun createDrop(item: Item, playerX: Float, playerY: Float, amount: Int) { + dropController.addDrop( + /* x = */ playerX + ((DROP_DISTANCE - Drop.DROP_SIZE / 2) * mobController.player.direction.basis), + /* y = */ playerY, + /* item = */ item, + /* count = */ amount, + ) + } + + private fun getActionSlot(action: MouseInputAction): Int = ( + ( + action.screenX - + (action.cameraViewport.width / 2 - hotbarTexture.regionWidth / 2) + ) / + HOTBAR_CELL_WIDTH + ).toInt() + + private fun handleHold(action: MouseInputAction) { +// buttonHoldTask = null +// gameWindowsManager.openInventory() + val player = mobController.player + val actionSlot = getActionSlot(action) + val currentItem = player.inventory.items[actionSlot] + val dropAmount = if (currentItem.item.isTool()) currentItem.amount else 1 + + createDrop(currentItem.item, player.x, player.y, dropAmount) + player.inventory.decreaseItemAmount(actionSlot, dropAmount) + } + + private fun handleDown(action: MouseInputAction) { + buttonHoldTask = object : Timer.Task() { + override fun run() { + handleHold(action) + } + } + + Timer.schedule(buttonHoldTask, TOUCH_HOLD_TIME_SEC) + } + + private fun handleUp(action: MouseInputAction) { + mobController.player.activeSlot = getActionSlot(action) + } + + private fun handleScroll(action: MouseInputAction) { + if (action.actionKey !is MouseInputActionKey.Scroll) { + return + } + mobController.player.activeSlot += action.actionKey.amountY.toInt() + if (mobController.player.activeSlot < 0) { + mobController.player.activeSlot = Player.HOTBAR_SIZE - 1 + } else if (mobController.player.activeSlot >= Player.HOTBAR_SIZE) { + mobController.player.activeSlot = 0 + } + } + + override fun handle(action: MouseInputAction) { + if (buttonHoldTask != null && buttonHoldTask?.isScheduled == true) { + cancelHold() + } + + if (buttonHoldTask != null && buttonHoldTask?.isScheduled != true) { + buttonHoldTask = null + return + } + + if (action.actionKey !is MouseInputActionKey.Left && action.actionKey !is MouseInputActionKey.Screen) { + if (action.actionKey is MouseInputActionKey.Scroll) { + handleScroll(action) + } + 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 + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectChestInventoryItemMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectChestInventoryItemMouseInputHandler.kt new file mode 100644 index 0000000..fef64d4 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectChestInventoryItemMouseInputHandler.kt @@ -0,0 +1,78 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.ChestInventoryWindow +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class SelectChestInventoryItemMouseInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val textureRegions: GetTextureRegionByNameUseCase, + itemsRepository: ItemsRepository, +) : AbstractInventoryItemsMouseInputHandler(itemsRepository, gameWindowsManager, GameWindowType.CHEST) { + + override val windowTexture get() = requireNotNull(textureRegions["chest"]) + + private fun handleInsideContentGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) { + val window = gameWindowsManager.currentWindow as ChestInventoryWindow + val itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Chest.contentsInRow + + handleInsidePlaceableCell(action, window.chest.items, window, itemIndex) + } + + private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) { + val window = gameWindowsManager.currentWindow as ChestInventoryWindow + + var itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Chest.itemsInRow + itemIndex += GameWindowsConfigs.Chest.hotbarCells + + if (itemIndex >= mobController.player.inventory.size) { + itemIndex -= mobController.player.inventory.size + } + + handleInsidePlaceableCell(action, mobController.player.inventory.items, window, itemIndex) + } + + override fun handle(action: MouseInputAction) { + val texture = windowTexture + + val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - texture.regionWidth / 2) + val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - texture.regionHeight / 2) + + val xOnGrid = (xOnWindow - GameWindowsConfigs.Chest.itemsGridMarginLeft) / + GameWindowsConfigs.Chest.itemsGridColWidth + val yOnGrid = (yOnWindow - GameWindowsConfigs.Chest.itemsGridMarginTop) / + GameWindowsConfigs.Chest.itemsGridRowHeight + + val xOnContent = (xOnWindow - GameWindowsConfigs.Chest.contentsMarginLeft) / + GameWindowsConfigs.Chest.itemsGridColWidth + val yOnContent = (yOnWindow - GameWindowsConfigs.Chest.contentsMarginTop) / + GameWindowsConfigs.Chest.itemsGridRowHeight + + val isInsideInventoryGrid = xOnGrid >= 0 && + xOnGrid < GameWindowsConfigs.Chest.itemsInRow && + yOnGrid >= 0 && + yOnGrid < GameWindowsConfigs.Chest.itemsInCol + + val isInsideContentGrid = xOnContent >= 0 && + xOnContent < GameWindowsConfigs.Chest.contentsInRow && + yOnContent >= 0 && + yOnContent < GameWindowsConfigs.Chest.contentsInCol + + if (isInsideInventoryGrid) { + handleInsideInventoryGrid(action, xOnGrid.toInt(), yOnGrid.toInt()) + } else if (isInsideContentGrid) { + handleInsideContentGrid(action, xOnContent.toInt(), yOnContent.toInt()) + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectCraftingInventoryItemMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectCraftingInventoryItemMouseInputHandler.kt new file mode 100644 index 0000000..0f0dba1 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectCraftingInventoryItemMouseInputHandler.kt @@ -0,0 +1,95 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.CraftingInventoryWindow +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class SelectCraftingInventoryItemMouseInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val textureRegions: GetTextureRegionByNameUseCase, + itemsRepository: ItemsRepository, +) : AbstractInventoryItemsMouseInputHandler(itemsRepository, gameWindowsManager, GameWindowType.CRAFTING_TABLE) { + + override val windowTexture get() = requireNotNull(textureRegions["crafting_table"]) + + private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) { + val window = gameWindowsManager.currentWindow as CraftingInventoryWindow + + var itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Crafting.itemsInRow + itemIndex += GameWindowsConfigs.Crafting.hotbarCells + + if (itemIndex >= mobController.player.inventory.size) { + itemIndex -= mobController.player.inventory.size + } + + handleInsidePlaceableCell(action, mobController.player.inventory.items, window, itemIndex) + } + + private fun handleInsideCraft(action: MouseInputAction, xOnCraft: Int, yOnCraft: Int) { + val window = gameWindowsManager.currentWindow as CraftingInventoryWindow + val index = xOnCraft + yOnCraft * GameWindowsConfigs.Crafting.craftGridSize + + handleInsidePlaceableCell(action, window.craftingItems, window, index) + + updateCraftResult(window) + } + + private fun handleInsideCraftResult(action: MouseInputAction) { + val window = gameWindowsManager.currentWindow as CraftingInventoryWindow + + handleInsideCraftResultCell(action, window.craftResultList, window, 0) + + updateCraftResult(window) + } + + override fun handle(action: MouseInputAction) { + val texture = windowTexture + + val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - texture.regionWidth / 2) + val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - texture.regionHeight / 2) + + val xOnGrid = (xOnWindow - GameWindowsConfigs.Crafting.itemsGridMarginLeft) / + GameWindowsConfigs.Crafting.itemsGridColWidth + 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) { + handleInsideCraftResult(action) + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectCreativeInventoryItemMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectCreativeInventoryItemMouseInputHandler.kt new file mode 100644 index 0000000..c35ffa0 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectCreativeInventoryItemMouseInputHandler.kt @@ -0,0 +1,66 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByIndexUseCase +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.isInsideWindow +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class SelectCreativeInventoryItemMouseInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val textureRegions: GetTextureRegionByNameUseCase, + private val getItemByIndexUseCase: GetItemByIndexUseCase, +) : IMouseInputHandler { + + private val creativeInventoryTexture get() = requireNotNull(textureRegions["creative"]) + + override fun checkConditions(action: MouseInputAction): Boolean = gameWindowsManager.currentWindowType == GameWindowType.CREATIVE_INVENTORY && + !gameWindowsManager.isDragging && + (action.actionKey is MouseInputActionKey.Left || action.actionKey is MouseInputActionKey.Screen) && + 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 = getItemByIndexUseCase[itemIndex] + mobController.player.inventory.addItem(item) + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectFurnaceInventoryItemMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectFurnaceInventoryItemMouseInputHandler.kt new file mode 100644 index 0000000..89d6adc --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectFurnaceInventoryItemMouseInputHandler.kt @@ -0,0 +1,104 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem.Companion.isNoneOrNull +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.entity.container.model.Furnace +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.FurnaceInventoryWindow +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class SelectFurnaceInventoryItemMouseInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val textureRegions: GetTextureRegionByNameUseCase, + itemsRepository: ItemsRepository, +) : AbstractInventoryItemsMouseInputHandler(itemsRepository, gameWindowsManager, GameWindowType.FURNACE) { + + override val windowTexture get() = requireNotNull(textureRegions["furnace"]) + + private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) { + val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow + + var itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Furnace.itemsInRow + itemIndex += GameWindowsConfigs.Furnace.hotbarCells + + if (itemIndex >= mobController.player.inventory.size) { + itemIndex -= mobController.player.inventory.size + } + + handleInsidePlaceableCell(action, mobController.player.inventory.items, window, itemIndex) + } + + private fun handleInsideFuel(action: MouseInputAction) { + val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow + + if (!window.selectedItem.isNoneOrNull() && window.selectedItem?.item?.params?.burningTimeMs == null) { + return + } + + handleInsidePlaceableCell(action, window.furnace.items, window, Furnace.FUEL_INDEX) + } + + private fun handleInsideInput(action: MouseInputAction) { + val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow + + handleInsidePlaceableCell(action, window.furnace.items, window, Furnace.INPUT_INDEX) + } + + private fun handleInsideResult(action: MouseInputAction) { + val window = gameWindowsManager.currentWindow as FurnaceInventoryWindow + + handleInsideCraftResultCell(action, window.furnace.items, window, Furnace.RESULT_INDEX) + } + + override fun handle(action: MouseInputAction) { + val texture = windowTexture + + val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - texture.regionWidth / 2) + val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - texture.regionHeight / 2) + + val xOnGrid = (xOnWindow - GameWindowsConfigs.Furnace.itemsGridMarginLeft) / + GameWindowsConfigs.Furnace.itemsGridColWidth + val yOnGrid = (yOnWindow - GameWindowsConfigs.Furnace.itemsGridMarginTop) / + GameWindowsConfigs.Furnace.itemsGridRowHeight + + val isInsideInput = xOnWindow > GameWindowsConfigs.Furnace.smeltInputMarginLeft && + xOnWindow < GameWindowsConfigs.Furnace.smeltInputMarginLeft + GameWindowsConfigs.Furnace.itemsGridColWidth && + yOnWindow > GameWindowsConfigs.Furnace.smeltInputMarginTop && + yOnWindow < GameWindowsConfigs.Furnace.smeltInputMarginTop + GameWindowsConfigs.Furnace.itemsGridRowHeight + + val isInsideFuel = xOnWindow > GameWindowsConfigs.Furnace.smeltFuelMarginLeft && + xOnWindow < GameWindowsConfigs.Furnace.smeltFuelMarginLeft + GameWindowsConfigs.Furnace.itemsGridColWidth && + yOnWindow > GameWindowsConfigs.Furnace.smeltFuelMarginTop && + yOnWindow < GameWindowsConfigs.Furnace.smeltFuelMarginTop + GameWindowsConfigs.Furnace.itemsGridRowHeight + + val isInsideResult = xOnWindow > GameWindowsConfigs.Furnace.smeltResultOffsetX && + xOnWindow < GameWindowsConfigs.Furnace.smeltResultOffsetX + GameWindowsConfigs.Furnace.itemsGridColWidth && + yOnWindow > GameWindowsConfigs.Furnace.smeltResultOffsetY && + yOnWindow < GameWindowsConfigs.Furnace.smeltResultOffsetY + GameWindowsConfigs.Furnace.itemsGridRowHeight + + val isInsideInventoryGrid = xOnGrid >= 0 && + xOnGrid < GameWindowsConfigs.Furnace.itemsInRow && + yOnGrid >= 0 && + yOnGrid < GameWindowsConfigs.Furnace.itemsInCol + + if (isInsideInventoryGrid) { + handleInsideInventoryGrid(action, xOnGrid.toInt(), yOnGrid.toInt()) + } else if (isInsideFuel) { + handleInsideFuel(action) + } else if (isInsideInput) { + handleInsideInput(action) + } else if (isInsideResult) { + handleInsideResult(action) + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectSurvivalInventoryItemMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectSurvivalInventoryItemMouseInputHandler.kt new file mode 100644 index 0000000..015b37a --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/SelectSurvivalInventoryItemMouseInputHandler.kt @@ -0,0 +1,93 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.SurvivalInventoryWindow +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class SelectSurvivalInventoryItemMouseInputHandler @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val mobController: MobController, + private val textureRegions: GetTextureRegionByNameUseCase, + private val itemsRepository: ItemsRepository, +) : AbstractInventoryItemsMouseInputHandler(itemsRepository, gameWindowsManager, GameWindowType.SURVIVAL_INVENTORY) { + + override val windowTexture get() = requireNotNull(textureRegions["survival"]) + + private fun handleInsideInventoryGrid(action: MouseInputAction, xOnGrid: Int, yOnGrid: Int) { + val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow + + var itemIndex = xOnGrid + yOnGrid * GameWindowsConfigs.Survival.itemsInRow + itemIndex += GameWindowsConfigs.Survival.hotbarCells + + if (itemIndex >= mobController.player.inventory.size) { + itemIndex -= mobController.player.inventory.size + } + + handleInsidePlaceableCell(action, mobController.player.inventory.items, window, itemIndex) + } + + private fun handleInsideCraft(action: MouseInputAction, xOnCraft: Int, yOnCraft: Int) { + val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow + val index = xOnCraft + yOnCraft * GameWindowsConfigs.Crafting.craftGridSize // this is crafting on purpose!! + + handleInsidePlaceableCell(action, window.craftingItems, window, index) + + updateCraftResult(window) + } + + private fun handleInsideCraftResult(action: MouseInputAction) { + val window = gameWindowsManager.currentWindow as SurvivalInventoryWindow + + handleInsideCraftResultCell(action, window.craftResultList, window, 0) + + updateCraftResult(window) + } + + override fun handle(action: MouseInputAction) { + val xOnWindow = action.screenX - (action.cameraViewport.width / 2 - windowTexture.regionWidth / 2) + val yOnWindow = action.screenY - (action.cameraViewport.height / 2 - windowTexture.regionHeight / 2) + + val xOnGrid = (xOnWindow - GameWindowsConfigs.Survival.itemsGridMarginLeft) / + GameWindowsConfigs.Survival.itemsGridColWidth + 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) { + handleInsideCraftResult(action) + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/UseItemMouseInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/UseItemMouseInputHandler.kt new file mode 100644 index 0000000..e6b98be --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/mouse/UseItemMouseInputHandler.kt @@ -0,0 +1,133 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.mouse + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.utils.Timer +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.items.model.item.Item +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.controls.action.placeToBackgroundAction +import ru.fredboy.cavedroid.ux.controls.action.placeToForegroundAction +import ru.fredboy.cavedroid.ux.controls.action.placeblock.IPlaceBlockAction +import ru.fredboy.cavedroid.ux.controls.action.useblock.IUseBlockAction +import ru.fredboy.cavedroid.ux.controls.action.useitem.IUseItemAction +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.isInsideHotbar +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class UseItemMouseInputHandler @Inject constructor( + private val mobController: MobController, + private val useItemActionMap: Map, + private val placeBlockActionMap: Map, + private val useBlockActionMap: Map, + private val gameWindowsManager: GameWindowsManager, + private val gameWorld: GameWorld, + private val textureRegions: GetTextureRegionByNameUseCase, +) : IMouseInputHandler { + + private var buttonHoldTask: Timer.Task? = null + + override fun checkConditions(action: MouseInputAction): Boolean = buttonHoldTask?.isScheduled == true || + !action.isInsideHotbar(textureRegions) && + gameWindowsManager.currentWindowType == GameWindowType.NONE && + action.actionKey is MouseInputActionKey.Right + + private fun cancelHold() { + buttonHoldTask?.cancel() + buttonHoldTask = null + } + + private fun handleHold() { + cancelHold() + + val player = mobController.player + val item = player.activeItem.item + player.startHitting(false) + player.stopHitting() + + if (item is Item.Placeable) { + placeBlockActionMap.placeToBackgroundAction( + item = item, + x = player.cursorX, + y = player.cursorY, + ) + } + } + + private fun handleDown() { + cancelHold() + buttonHoldTask = object : Timer.Task() { + override fun run() { + handleHold() + } + } + Timer.schedule(buttonHoldTask, TOUCH_HOLD_TIME_SEC) + } + + private fun tryUseBlock() { + val block = gameWorld.getForeMap(mobController.player.cursorX, mobController.player.cursorY) + .takeIf { !it.isNone() } + ?: gameWorld.getBackMap(mobController.player.cursorX, mobController.player.cursorY) + .takeIf { !it.isNone() } + ?: return + + useBlockActionMap[block.params.key]?.perform( + block = block, + x = mobController.player.cursorX, + y = mobController.player.cursorY, + ) + } + + private fun handleUp() { + val player = mobController.player + val item = player.activeItem.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") + } else if (item is Item.Food && player.health < player.maxHealth) { + player.heal(item.heal) + player.decreaseCurrentItemCount() + } else { + tryUseBlock() + } + } + + 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() + } else if (!action.actionKey.touchUp) { + handleDown() + } + } + + companion object { + private const val TAG = "UseItemMouseInputActionHandler" + private const val TOUCH_HOLD_TIME_SEC = 0.5f + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/touch/JoystickInputHandler.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/touch/JoystickInputHandler.kt new file mode 100644 index 0000000..26437c9 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/handler/touch/JoystickInputHandler.kt @@ -0,0 +1,148 @@ +package ru.fredboy.cavedroid.ux.controls.input.handler.touch + +import com.badlogic.gdx.utils.TimeUtils +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.model.Joystick +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Direction +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.controls.input.IMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import ru.fredboy.cavedroid.ux.controls.input.annotation.BindMouseInputHandler +import ru.fredboy.cavedroid.ux.controls.input.isInsideHotbar +import javax.inject.Inject + +@GameScope +@BindMouseInputHandler +class JoystickInputHandler @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val gameContextRepository: GameContextRepository, + private val mobController: MobController, + private val gameWindowsManager: GameWindowsManager, + private val textureRegions: GetTextureRegionByNameUseCase, +) : IMouseInputHandler { + + private var activateTimeMs = 0L + private var cursorTimeoutMs = 100L + + private var active = false + set(value) { + if (!value) { + resetVelocity() + if (TimeUtils.timeSinceMillis(activateTimeMs) < 200L && + mobController.player.controlMode != Player.ControlMode.CURSOR && + mobController.player.canJump + ) { + mobController.player.jump() + } + } else { + activateTimeMs = TimeUtils.millis() + } + field = value + } + + private fun resetVelocity() { + mobController.player.velocity.x = 0f + + if (mobController.player.isFlyMode) { + mobController.player.velocity.y = 0f + } + } + + override fun checkConditions(action: MouseInputAction): Boolean = gameWindowsManager.currentWindowType == GameWindowType.NONE && + applicationContextRepository.isTouch() && + action.actionKey is MouseInputActionKey.Touch && + (action.actionKey.pointer == gameContextRepository.getJoystick().pointer || !active) && + ( + (action.actionKey is MouseInputActionKey.Dragged) || + (action.screenX < action.cameraViewport.width / 2 && !action.actionKey.touchUp || active) + ) && + !(action.actionKey is MouseInputActionKey.Screen && action.isInsideHotbar(textureRegions)) + + private fun handleTouchDown(action: MouseInputAction) { + val key = action.actionKey as MouseInputActionKey.Screen + gameContextRepository.getJoystick().activate(action.screenX, action.screenY, key.pointer) + active = true + } + + private fun handleTouchUp() { + gameContextRepository.getJoystick().deactivate() + active = false + } + + private fun handleCursor() { + val joystick = gameContextRepository.getJoystick() + + if (TimeUtils.timeSinceMillis(cursorTimeoutMs) < 150L) { + return + } + + val pastCursorX = mobController.player.cursorX + val pastCursorY = mobController.player.cursorY + + if (Math.abs(joystick.activeX - joystick.centerX) >= Joystick.RADIUS / 2) { + mobController.player.cursorX += if (joystick.activeX > joystick.centerX) 1 else -1 + cursorTimeoutMs = TimeUtils.millis() + } + + if (Math.abs(joystick.activeY - joystick.centerY) >= Joystick.RADIUS / 2) { + mobController.player.cursorY += if (joystick.activeY > joystick.centerY) 1 else -1 + cursorTimeoutMs = TimeUtils.millis() + } + + mobController.checkPlayerCursorBounds() + + if (mobController.player.cursorX != pastCursorX || mobController.player.cursorY != pastCursorY) { + mobController.player.blockDamage = 0f + } + } + + private fun handleDragged() { + if (!active) { + return + } + + if (mobController.player.controlMode == Player.ControlMode.CURSOR) { + handleCursor() + return + } + + val joystick = gameContextRepository.getJoystick() + val joyVector = joystick.getVelocityVector() + + if (mobController.player.isFlyMode) { + joyVector.scl(2f) + } + + mobController.player.velocity.x = joyVector.x + + mobController.player.direction = if (joyVector.x < 0) { + Direction.LEFT + } else { + Direction.RIGHT + } + + if (mobController.player.isFlyMode) { + mobController.player.velocity.y = joyVector.y + } + } + + override fun handle(action: MouseInputAction) { + when (action.actionKey) { + is MouseInputActionKey.Dragged -> handleDragged() + else -> { + if (action.actionKey.touchUp) { + handleTouchUp() + } else { + handleTouchDown(action) + } + } + } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/mapper/KeyboardInputActionMapper.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/mapper/KeyboardInputActionMapper.kt new file mode 100644 index 0000000..e03cfb1 --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/mapper/KeyboardInputActionMapper.kt @@ -0,0 +1,45 @@ +package ru.fredboy.cavedroid.ux.controls.input.mapper + +import com.badlogic.gdx.Input +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.ux.controls.input.action.KeyboardInputAction +import ru.fredboy.cavedroid.ux.controls.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.Up + 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.Q -> KeyboardInputActionKey.DropItem + + Input.Keys.NUM_1 -> KeyboardInputActionKey.SelectHotbarSlot(0) + Input.Keys.NUM_2 -> KeyboardInputActionKey.SelectHotbarSlot(1) + Input.Keys.NUM_3 -> KeyboardInputActionKey.SelectHotbarSlot(2) + Input.Keys.NUM_4 -> KeyboardInputActionKey.SelectHotbarSlot(3) + Input.Keys.NUM_5 -> KeyboardInputActionKey.SelectHotbarSlot(4) + Input.Keys.NUM_6 -> KeyboardInputActionKey.SelectHotbarSlot(5) + Input.Keys.NUM_7 -> KeyboardInputActionKey.SelectHotbarSlot(6) + Input.Keys.NUM_8 -> KeyboardInputActionKey.SelectHotbarSlot(7) + Input.Keys.NUM_9 -> KeyboardInputActionKey.SelectHotbarSlot(8) + + else -> null + } + + return actionKey?.let { KeyboardInputAction(it, isKeyDown) } + } +} diff --git a/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/mapper/MouseInputActionMapper.kt b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/mapper/MouseInputActionMapper.kt new file mode 100644 index 0000000..51b4c3f --- /dev/null +++ b/core/ux/controls/src/main/kotlin/ru/fredboy/cavedroid/ux/controls/input/mapper/MouseInputActionMapper.kt @@ -0,0 +1,71 @@ +package ru.fredboy.cavedroid.ux.controls.input.mapper + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.Input +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.ux.controls.input.action.MouseInputAction +import ru.fredboy.cavedroid.ux.controls.input.action.keys.MouseInputActionKey +import javax.inject.Inject + +@GameScope +class MouseInputActionMapper @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, +) { + + fun map( + mouseX: Float, + mouseY: Float, + cameraViewport: Rectangle, + button: Int, + touchUp: Boolean, + pointer: Int, + ): MouseInputAction? { + val actionKey = mapActionKey(button, touchUp, pointer) ?: return null + + return MouseInputAction( + screenX = getScreenX(mouseX), + screenY = getScreenY(mouseY), + actionKey = actionKey, + cameraViewport = cameraViewport, + ) + } + + fun mapDragged( + mouseX: Float, + mouseY: Float, + cameraViewport: Rectangle, + pointer: Int, + ): MouseInputAction = MouseInputAction( + screenX = getScreenX(mouseX), + screenY = getScreenY(mouseY), + actionKey = MouseInputActionKey.Dragged(pointer), + cameraViewport = cameraViewport, + ) + + fun mapScrolled( + mouseX: Float, + mouseY: Float, + amountX: Float, + amountY: Float, + cameraViewport: Rectangle, + ): MouseInputAction = MouseInputAction( + screenX = getScreenX(mouseX), + screenY = getScreenY(mouseY), + actionKey = MouseInputActionKey.Scroll(amountX, amountY), + cameraViewport = cameraViewport, + ) + + private fun mapActionKey(button: Int, touchUp: Boolean, pointer: Int): MouseInputActionKey? = when (button) { + Input.Buttons.LEFT -> MouseInputActionKey.Left(touchUp) + Input.Buttons.RIGHT -> MouseInputActionKey.Right(touchUp) + Input.Buttons.MIDDLE -> MouseInputActionKey.Middle(touchUp) + -1 -> MouseInputActionKey.Screen(touchUp, pointer) + else -> null + } + + private fun getScreenX(mouseX: Float): Float = mouseX * (applicationContextRepository.getWidth() / Gdx.graphics.width) + + private fun getScreenY(mouseY: Float): Float = mouseY * (applicationContextRepository.getHeight() / Gdx.graphics.height) +} diff --git a/core/ux/physics/build.gradle.kts b/core/ux/physics/build.gradle.kts new file mode 100644 index 0000000..a2da836 --- /dev/null +++ b/core/ux/physics/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + useAutomultibind() + + useCommonModule() + useDomainModules() + useEntityModules() + useGameModules() +} diff --git a/core/ux/physics/src/main/java/ru/fredboy/cavedroid/ux/physics/GamePhysics.java b/core/ux/physics/src/main/java/ru/fredboy/cavedroid/ux/physics/GamePhysics.java new file mode 100644 index 0000000..1062afc --- /dev/null +++ b/core/ux/physics/src/main/java/ru/fredboy/cavedroid/ux/physics/GamePhysics.java @@ -0,0 +1,392 @@ +package ru.fredboy.cavedroid.ux.physics; + +import com.badlogic.gdx.math.Intersector; +import com.badlogic.gdx.math.MathUtils; +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.math.Vector2; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.fredboy.cavedroid.common.di.GameScope; +import ru.fredboy.cavedroid.common.utils.MeasureUnitsUtilsKt; +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository; +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository; +import ru.fredboy.cavedroid.domain.items.model.block.Block; +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem; +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase; +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter; +import ru.fredboy.cavedroid.entity.drop.model.Drop; +import ru.fredboy.cavedroid.entity.mob.model.FallingBlock; +import ru.fredboy.cavedroid.entity.mob.model.Mob; +import ru.fredboy.cavedroid.entity.mob.model.Player; +import ru.fredboy.cavedroid.game.controller.drop.DropController; +import ru.fredboy.cavedroid.game.controller.mob.MobController; +import ru.fredboy.cavedroid.game.world.GameWorld; + +import javax.inject.Inject; +import java.util.Iterator; + +@GameScope +public class GamePhysics { + + 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, 444.44f); + + private final GameWorld mGameWorld; + private final ApplicationContextRepository mGameContextRepository; + private final MobController mMobController; + private final DropController mDropController; + private final GetItemByKeyUseCase mGetItemByKeyUseCase; + private final DropAdapter mDropAdapter; + + @Inject + public GamePhysics(GameWorld gameWorld, + ApplicationContextRepository gameContextRepository, + MobController mobController, + DropController dropController, + GetItemByKeyUseCase getItemByKeyUseCase, + DropAdapter dropAdapter) { + mGameWorld = gameWorld; + mGameContextRepository = gameContextRepository; + mMobController = mobController; + mDropController = dropController; + mGetItemByKeyUseCase = getItemByKeyUseCase; + mDropAdapter = dropAdapter; + } + + /** + * Checks if mob should jump + * + * @return true if mob should jump + */ + private boolean checkJump(Mob mob) { + 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); + Block block = mGameWorld.getForeMap(blX / 16, blY / 16); + + if (checkColl(new Rectangle(blX, mob.getY() - 18, mob.getWidth(), mob.getHeight())) != null) { + return false; + } + + return (block.getParams().getHasCollision() && block.getParams().getCollisionMargins().getTop() < 8 && + (mob.getY() + mob.getHeight()) - block.getRectangle(blX / 16, blY / 16).y > 8); + } + + /** + * @return colliding rect or null if no collision + */ + @Nullable + private Rectangle checkColl(Rectangle rect) { + int minX = (int) ((rect.x + rect.width / 2) / 16) - 4; + int minY = (int) ((rect.y + rect.height / 2) / 16) - 4; + int maxX = (int) ((rect.x + rect.width / 2) / 16) + 4; + int maxY = (int) ((rect.y + rect.height / 2) / 16) + 4; + + if (minY < 0) { + minY = 0; + } + + if (maxY > mGameWorld.getHeight()) { + maxY = mGameWorld.getHeight(); + } + + 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.getParams().getHasCollision()) { + final Rectangle blockRect = block.getRectangle(x, y); + if (Intersector.overlaps(rect, blockRect)) { + return blockRect; + } + } + } + } + + return null; + } + + private boolean isBlockToJump(Block block) { + return block.getParams().getHasCollision() && block.getParams().getCollisionMargins().getTop() < 8; + } + + private Block getBlock(Rectangle rect) { + return mGameWorld.getForeMap((int) (rect.x + rect.width / 2) / 16, + (int) (rect.y + rect.height / 8 * 7) / 16); + } + + private Rectangle getShiftedPlayerRect(float shift) { + final Player player = mMobController.getPlayer(); + return new Rectangle(player.x + shift, player.y, player.width, player.height); + } + + /** + * @return Rectangle representing magneting target for this drop + */ + @Nullable + private Rectangle getShiftedMagnetingPlayerRect(Drop drop) { + final Player player = mMobController.getPlayer(); + + if (!player.getInventory().canPickItem(drop.getItem())) { + return null; + } + + if (drop.canMagnetTo(player)) { + return getShiftedPlayerRect(0); + } + + final float fullWorldPx = MeasureUnitsUtilsKt.getPx(mGameWorld.getWidth()); + + final Rectangle shiftedLeft = getShiftedPlayerRect(-fullWorldPx); + if (drop.canMagnetTo(shiftedLeft)) { + return shiftedLeft; + } + + final Rectangle shiftedRight = getShiftedPlayerRect(fullWorldPx); + if (drop.canMagnetTo(shiftedRight)) { + return shiftedRight; + } + + return null; + } + + private void pickUpDropIfPossible(Rectangle shiftedPlayerTarget, Drop drop) { + final Player player = mMobController.getPlayer(); + + if (Intersector.overlaps(shiftedPlayerTarget, drop)) { + drop.setPickedUp(player.getInventory().pickUpItem(drop.getInventoryItem())); + } + } + + 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 { + dropVelocity.y += gravity.y * delta; + } + + 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) != null) { + dropVelocity.setZero(); + do { + drop.y--; + } while (checkColl(drop) != null); + } + + if (playerMagnetTarget != null) { + pickUpDropIfPossible(playerMagnetTarget, drop); + } + } + + private void mobXColl(Mob mob) { + if (mob.getVelocity().x == 0f) { + return; + } + + @Nullable Rectangle collidingRect = checkColl(mob); + + if (collidingRect != null) { + if (mob.getCanJump() && !mob.isFlyMode() && collidingRect.y >= mob.y + mob.height - 8) { + mob.y = collidingRect.y - mob.height; + return; + } + + collidingRect = checkColl(mob); + + if (collidingRect != null) { + int d = 0; + + if (mob.getVelocity().x < 0) { + d = 1; + } else if (mob.getVelocity().x > 0) { + d = -1; + } + + if (d < 0) { + mob.x = collidingRect.x - mob.width; + } else { + mob.x = collidingRect.x + collidingRect.width; + } + + if (mob.getCanJump()) { + mob.changeDir(); + } + } + } + + // Check world bounds + final float worldWidthPx = MeasureUnitsUtilsKt.getPx(mGameWorld.getWidth()); + if (mob.getX() + mob.getWidth() / 2 < 0) { + mob.x += worldWidthPx; + } + if (mob.getX() + mob.getWidth() / 2 > worldWidthPx) { + mob.x -= worldWidthPx; + } + } + + private void mobYColl(Mob mob) { + @Nullable final Rectangle collidingRect = checkColl(mob); + if (collidingRect != null) { + int d = -1; + + 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); + } + } + + if (d < 0) { + mob.y = collidingRect.y - mob.height; + } else { + mob.y = collidingRect.y + collidingRect.height; + } + + mob.getVelocity().y = 0; + + } else { + mob.y += 1; + mob.setCanJump(checkColl(mob) != null); + mob.y -= 1; + } + + if (mob.getY() > MeasureUnitsUtilsKt.getPx(mGameWorld.getHeight())) { + mob.kill(); + } + } + + private void playerPhy(@NotNull Player player, float delta) { + if (player.isDead()) { + return; + } + + if (getBlock(player).isFluid()) { + if (mGameContextRepository.isTouch() && player.getVelocity().x != 0 && !player.getSwim() && !player.isFlyMode()) { + player.setSwim(true); + } + if (!player.getSwim()) { + if (!player.isFlyMode() && player.getVelocity().y < 32f) { + player.getVelocity().y += gravity.y * delta; + } + if (!player.isFlyMode() && player.getVelocity().y > 32f) { + player.getVelocity().y -= player.getVelocity().y * 32f * delta; + } + } else { + player.getVelocity().y += PL_JUMP_VELOCITY * delta; + if (player.getVelocity().y < -player.getSpeed()) { + player.getVelocity().y = -player.getSpeed(); + } + } + } else { + if (!player.isFlyMode() && player.getVelocity().y < PL_TERMINAL_VELOCITY) { + player.getVelocity().y += gravity.y * delta; + } + } + + 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 (mGameContextRepository.isTouch() && !player.isFlyMode() && player.getCanJump() && player.getVelocity().x != 0 && checkJump(player)) { + player.jump(); + player.setCanJump(false); + } + } + + private void mobPhy(Mob mob, float delta) { + if (!(mob instanceof FallingBlock) && getBlock(mob).isFluid()) { + if (mob.getVelocity().y > 32f) { + mob.getVelocity().y -= mob.getVelocity().y * 32f * delta; + } + + mob.getVelocity().y += PL_JUMP_VELOCITY * delta; + + if (mob.getVelocity().y < -mob.getSpeed()) { + mob.getVelocity().y = -mob.getSpeed(); + } + } else if (!mob.isFlyMode() && mob.getVelocity().y < PL_TERMINAL_VELOCITY) { + mob.getVelocity().y += gravity.y * delta; + } + + mob.y += mob.getVelocity().y * delta; + mobYColl(mob); + + if (mob.isDead()) { + return; + } + + mob.x += mob.getVelocity().x * delta; + mobXColl(mob); + + if (mob.getCanJump() && mob.getVelocity().x != 0 && checkJump(mob)) { + mob.jump(); + mob.setCanJump(false); + } + } + + public void update(float delta) { + Player player = mMobController.getPlayer(); + + for (Iterator it = mDropController.getAllDrop().iterator(); it.hasNext(); ) { + Drop drop = it.next(); + dropPhy(drop, delta); + if (drop.isPickedUp()) { + it.remove(); + } + } + + for (Iterator it = mMobController.getMobs().iterator(); it.hasNext(); ) { + Mob mob = it.next(); + //todo: Mob ai +// mob.ai(mGameWorld, mGameItemsHolder, mMobController, delta); + mobPhy(mob, delta); + if (mob.isDead()) { + for (InventoryItem invItem : mob.getDropItems(mGetItemByKeyUseCase)) { + mDropController.addDrop(mob.x, mob.y, invItem); + } + + it.remove(); + } + } + + playerPhy(player, delta); + //todo : Update player +// player.ai(mGameWorld, mGameItemsHolder, mMobController, delta); + if (player.isDead()) { +// for (InventoryItem invItem : player.getInventory().getItems()) { +// mDropController.addDrop(player.x, player.y, invItem); +// } + mDropAdapter.dropInventory(player.x, player.y, player.getInventory()); + mMobController.respawnPlayer(); + } + } + +} diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/UpdateBlockActionUtils.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/UpdateBlockActionUtils.kt new file mode 100644 index 0000000..f48101a --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/UpdateBlockActionUtils.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.ux.physics.action + +import ru.fredboy.cavedroid.ux.physics.action.updateblock.IUpdateBlockAction +import ru.fredboy.cavedroid.ux.physics.action.updateblock.UpdateRequiresBlockAction + +fun Map.getRequiresBlockAction(): IUpdateBlockAction { + return requireNotNull(get(UpdateRequiresBlockAction.ACTION_KEY)) { "action requires_block not found" } +} diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/annotation/BindUpdateBlockAction.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/annotation/BindUpdateBlockAction.kt new file mode 100644 index 0000000..9ee7ba0 --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/annotation/BindUpdateBlockAction.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.ux.physics.action.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.ux.physics.action.updateblock.IUpdateBlockAction + +@BindsIntoMapStringKey( + interfaceClass = IUpdateBlockAction::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "UpdateBlockActionsModule", +) +annotation class BindUpdateBlockAction(val stringKey: String) diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/IUpdateBlockAction.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/IUpdateBlockAction.kt new file mode 100644 index 0000000..39e7e35 --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/IUpdateBlockAction.kt @@ -0,0 +1,6 @@ +package ru.fredboy.cavedroid.ux.physics.action.updateblock + +interface IUpdateBlockAction { + + fun update(x: Int, y: Int) +} diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateBedLeftAction.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateBedLeftAction.kt new file mode 100644 index 0000000..2934bc3 --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateBedLeftAction.kt @@ -0,0 +1,26 @@ +package ru.fredboy.cavedroid.ux.physics.action.updateblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.usecase.GetBlockByKeyUseCase +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.physics.action.annotation.BindUpdateBlockAction +import javax.inject.Inject + +@GameScope +@BindUpdateBlockAction(stringKey = UpdateBedLeftAction.BLOCK_KEY) +class UpdateBedLeftAction @Inject constructor( + private val gameWorld: GameWorld, + private val getBlockByKeyUseCase: GetBlockByKeyUseCase, +) : IUpdateBlockAction { + + override fun update(x: Int, y: Int) { + val bedRight = getBlockByKeyUseCase["bed_r"] + if (gameWorld.getForeMap(x + 1, y) != bedRight) { + gameWorld.resetForeMap(x, y) + } + } + + companion object { + const val BLOCK_KEY = "bed_l" + } +} diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateBedRightAction.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateBedRightAction.kt new file mode 100644 index 0000000..5d5ce1f --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateBedRightAction.kt @@ -0,0 +1,26 @@ +package ru.fredboy.cavedroid.ux.physics.action.updateblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.usecase.GetBlockByKeyUseCase +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.physics.action.annotation.BindUpdateBlockAction +import javax.inject.Inject + +@GameScope +@BindUpdateBlockAction(stringKey = UpdateBedRightAction.BLOCK_KEY) +class UpdateBedRightAction @Inject constructor( + private val gameWorld: GameWorld, + private val getBlockByKeyUseCase: GetBlockByKeyUseCase, +) : IUpdateBlockAction { + + override fun update(x: Int, y: Int) { + val bedLeft = getBlockByKeyUseCase["bed_l"] + if (gameWorld.getForeMap(x - 1, y) != bedLeft) { + gameWorld.resetForeMap(x, y) + } + } + + companion object { + const val BLOCK_KEY = "bed_r" + } +} diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateGrassAction.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateGrassAction.kt new file mode 100644 index 0000000..40b047c --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateGrassAction.kt @@ -0,0 +1,30 @@ +package ru.fredboy.cavedroid.ux.physics.action.updateblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.usecase.GetBlockByKeyUseCase +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.physics.action.annotation.BindUpdateBlockAction +import javax.inject.Inject + +@GameScope +@BindUpdateBlockAction(stringKey = UpdateGrassAction.BLOCK_KEY) +class UpdateGrassAction @Inject constructor( + private val gameWorld: GameWorld, + private val getBlockByKeyUseCase: GetBlockByKeyUseCase, +) : IUpdateBlockAction { + + override fun update(x: Int, y: Int) { + val blockOnTop = gameWorld.getForeMap(x, y - 1) + + val makesDirt = blockOnTop.params.hasCollision || blockOnTop.isFluid() + + when { + makesDirt -> gameWorld.setForeMap(x, y, getBlockByKeyUseCase["dirt"]) + blockOnTop.params.key == "snow" -> gameWorld.setForeMap(x, y, getBlockByKeyUseCase["grass_snowed"]) + } + } + + companion object { + const val BLOCK_KEY = "grass" + } +} diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateRequiresBlockAction.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateRequiresBlockAction.kt new file mode 100644 index 0000000..5d5483a --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateRequiresBlockAction.kt @@ -0,0 +1,32 @@ +package ru.fredboy.cavedroid.ux.physics.action.updateblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.game.controller.mob.factory.FallingBlockFactory +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.physics.action.annotation.BindUpdateBlockAction +import javax.inject.Inject + +@GameScope +@BindUpdateBlockAction(stringKey = UpdateRequiresBlockAction.ACTION_KEY) +class UpdateRequiresBlockAction @Inject constructor( + private val gameWorld: GameWorld, + private val fallingBlockFactory: FallingBlockFactory, +) : IUpdateBlockAction { + + override fun update(x: Int, y: Int) { + if (gameWorld.getForeMap(x, y + 1).params.hasCollision.not()) { + val block = gameWorld.getForeMap(x, y) + + if (block.params.isFallable) { + gameWorld.resetForeMap(x, y) + fallingBlockFactory.create(x, y, block) + } else { + gameWorld.destroyForeMap(x, y, true) + } + } + } + + companion object { + const val ACTION_KEY = "requires_block" + } +} diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateSnowedGrassAction.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateSnowedGrassAction.kt new file mode 100644 index 0000000..13960c4 --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/action/updateblock/UpdateSnowedGrassAction.kt @@ -0,0 +1,29 @@ +package ru.fredboy.cavedroid.ux.physics.action.updateblock + +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.items.usecase.GetBlockByKeyUseCase +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.physics.action.annotation.BindUpdateBlockAction +import javax.inject.Inject + +@GameScope +@BindUpdateBlockAction(stringKey = UpdateSnowedGrassAction.BLOCK_KEY) +class UpdateSnowedGrassAction @Inject constructor( + private val gameWorld: GameWorld, + private val getBlockByKeyUseCase: GetBlockByKeyUseCase, +) : IUpdateBlockAction { + + override fun update(x: Int, y: Int) { + val blockOnTop = gameWorld.getForeMap(x, y - 1) + val makesDirt = blockOnTop.params.hasCollision || blockOnTop.isFluid() + + when { + makesDirt -> gameWorld.setForeMap(x, y, getBlockByKeyUseCase["dirt"]) + blockOnTop.params.key != "snow" -> gameWorld.setForeMap(x, y, getBlockByKeyUseCase["grass"]) + } + } + + companion object { + const val BLOCK_KEY = "grass_snowed" + } +} diff --git a/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/task/GameWorldBlocksLogicControllerTask.kt b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/task/GameWorldBlocksLogicControllerTask.kt new file mode 100644 index 0000000..fdf765b --- /dev/null +++ b/core/ux/physics/src/main/kotlin/ru/fredboy/cavedroid/ux/physics/task/GameWorldBlocksLogicControllerTask.kt @@ -0,0 +1,62 @@ +package ru.fredboy.cavedroid.ux.physics.task + +import com.badlogic.gdx.utils.Timer.Task +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.physics.action.getRequiresBlockAction +import ru.fredboy.cavedroid.ux.physics.action.updateblock.IUpdateBlockAction +import javax.inject.Inject + +@GameScope +class GameWorldBlocksLogicControllerTask @Inject constructor( + private val gameWorld: GameWorld, + private val updateBlockActions: Map, + private val mobController: MobController, +) : Task() { + + private var currentRelativeChunk = 0 + + private fun getChunkStart(): Int { + val playerX = mobController.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.run { requiresBlock || isFallable } } + + action?.update(x, y) + } + + override fun run() { + val startX = getChunkStart() + + for (y in gameWorld.height downTo 0) { + for (x in startX.., List>() + + private val updateQueue = PriorityQueue(16) { c1, c2 -> + c1.priority.compareTo(c2.priority) + } + + init { + val waters = itemsRepository.getBlocksByType(Block.Water::class.java) + .sortedBy(Block.Water::state) + val lavas = itemsRepository.getBlocksByType(Block.Lava::class.java) + .sortedBy(Block.Lava::state) + + fluidStatesMap[Block.Water::class] = waters + fluidStatesMap[Block.Lava::class] = lavas + } + + private fun getNextStateBlock(fluid: Block.Fluid): Block.Fluid? { + val stateList = fluidStatesMap[fluid::class] ?: return null + val currentState = stateList.indexOf(fluid) + .takeIf { it >= 0 } ?: return null + + var nextState = currentState + 1 + + if (nextState == 1) { + nextState++ + } + + if (nextState < stateList.size) { + return stateList[nextState] + } + + return null + } + + private fun noFluidNearby(x: Int, y: Int): Boolean { + val current = gameWorld.getForeMap(x, y) + + if (current !is Block.Fluid) { + throw IllegalArgumentException("block at $x;$y is not a fluid") + } + + val onTop = gameWorld.getForeMap(x, y - 1) + val onLeft = gameWorld.getForeMap(x - 1, y) + val onRight = gameWorld.getForeMap(x + 1, y) + + return !onTop.isFluid() && + (onLeft !is Block.Fluid || onLeft.state >= current.state) && + (onRight !is Block.Fluid || onRight.state >= current.state) + } + + private fun drainFluid(x: Int, y: Int): Boolean { + val fluid = (gameWorld.getForeMap(x, y) as? Block.Fluid) + ?: return true + + if (fluid.state > 0) { + if (noFluidNearby(x, y)) { + val nexState = getNextStateBlock(fluid) + if (nexState == null) { + updateQueue.offer(UpdateCommand(-1) { gameWorld.resetForeMap(x, y) }) + return true + } + updateQueue.offer(UpdateCommand(nexState, x, y)) + } + } + + return false + } + + private fun fluidCanFlowThere(fluid: Block.Fluid, targetBlock: Block): Boolean { + return targetBlock.isNone() || + (!targetBlock.params.hasCollision && !targetBlock.isFluid()) || + (fluid::class == targetBlock::class && fluid.state < (targetBlock as Block.Fluid).state) + } + + private fun flowFluidTo(currentFluid: Block.Fluid, x: Int, y: Int, nextStateFluid: Block.Fluid) { + val targetBlock = gameWorld.getForeMap(x, y) + + val command = when { + fluidCanFlowThere(currentFluid, targetBlock) -> UpdateCommand(nextStateFluid, x, y) + + currentFluid.isWater() && targetBlock is Block.Lava && targetBlock.state > 0 -> + UpdateCommand(100) { gameWorld.setForeMap(x, y, itemsRepository.getBlockByKey("cobblestone")) } + + currentFluid.isWater() && targetBlock.isLava() -> + UpdateCommand(100) { gameWorld.setForeMap(x, y, itemsRepository.getBlockByKey("obsidian")) } + + currentFluid.isLava() && targetBlock.isWater() -> + UpdateCommand(200) { gameWorld.setForeMap(x, y, itemsRepository.getBlockByKey("stone")) } + + else -> null + } + + command?.let(updateQueue::offer) + } + + private fun flowFluid(x: Int, y: Int) { + val fluid = gameWorld.getForeMap(x, y) as Block.Fluid + val stateList = fluidStatesMap[fluid::class] ?: return + + if (fluid.state < stateList.lastIndex && gameWorld.getForeMap(x, y + 1).params.hasCollision) { + val nextState = getNextStateBlock(fluid) ?: return + + flowFluidTo(fluid, x - 1, y, nextState) + flowFluidTo(fluid, x + 1, y, nextState) + } else { + flowFluidTo(fluid, x, y + 1, stateList[1]) + } + } + + fun updateFluids(x: Int, y: Int) { + val block = gameWorld.getForeMap(x, y) + if (!block.isFluid() || (block.isLava() && updateTick % 2 == 0)) { + return + } + + if (drainFluid(x, y)) { + return + } + + flowFluid(x, y) + } + + private fun fluidUpdater() { + val midScreen = mobController.player.x.bl + + for (y in gameWorld.height - 1 downTo 0) { + for (x in 0.. + forEachBlockInArea(mob) { x, y -> + val foregroundBlock = gameWorld.getForeMap(x, y) + val backgroundBlock = gameWorld.getBackMap(x, y) + + val damage = max(foregroundBlock.params.damage, backgroundBlock.params.damage) + if (damage > 0) { + mob.damage(damage) + } + } + } + } + + companion object { + const val ENVIRONMENTAL_MOB_DAMAGE_INTERVAL_SEC = 0.5f + } +} diff --git a/core/ux/rendering/build.gradle.kts b/core/ux/rendering/build.gradle.kts new file mode 100644 index 0000000..870744f --- /dev/null +++ b/core/ux/rendering/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useLibgdx() + useDagger() + useAutomultibind() + + useCommonModule() + useDataModules() + useDomainModules() + useEntityModules() + useGameModules() +} diff --git a/core/ux/rendering/src/main/java/ru/fredboy/cavedroid/ux/rendering/GameRenderer.java b/core/ux/rendering/src/main/java/ru/fredboy/cavedroid/ux/rendering/GameRenderer.java new file mode 100644 index 0000000..f849524 --- /dev/null +++ b/core/ux/rendering/src/main/java/ru/fredboy/cavedroid/ux/rendering/GameRenderer.java @@ -0,0 +1,267 @@ +package ru.fredboy.cavedroid.ux.rendering; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +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 com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.utils.TimeUtils; +import org.jetbrains.annotations.Nullable; +import ru.fredboy.cavedroid.common.di.GameScope; +import ru.fredboy.cavedroid.common.utils.MeasureUnitsUtilsKt; +import ru.fredboy.cavedroid.common.utils.RenderingUtilsKt; +import ru.fredboy.cavedroid.domain.assets.usecase.GetFontUseCase; +import ru.fredboy.cavedroid.domain.configuration.model.CameraContext; +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository; +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository; +import ru.fredboy.cavedroid.entity.mob.model.Player; +import ru.fredboy.cavedroid.game.controller.mob.MobController; +import ru.fredboy.cavedroid.game.window.TooltipManager; +import ru.fredboy.cavedroid.game.world.GameWorld; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +@GameScope +public class GameRenderer { + + private static final float CAMERA_SPEED = 72f; + private static final float MAX_CAM_DISTANCE_FROM_PLAYER = 64f; + private final ApplicationContextRepository mApplicationContextRepository; + private final GameContextRepository mGameContextRepository; + private final MobController mMobsController; + private final GameWorld mGameWorld; + private final List mRenderers; + private final TooltipManager mTooltipManager; + private final GetFontUseCase mGetFontUseCase; + private final Vector2 mCamCenterToPlayer = new Vector2(); + private long mCameraDelayMs = 0L; + + private final ShapeRenderer shaper; + private final SpriteBatch spriter; + + @Inject + public GameRenderer(ApplicationContextRepository applicationContextRepository, + GameContextRepository gameContextRepository, + MobController mobsController, + GameWorld gameWorld, + Set renderers, + TooltipManager tooltipManager, + GetFontUseCase getFontUseCase) { + @Nullable final CameraContext cameraContext = gameContextRepository.getCameraContext(); + + shaper = new ShapeRenderer(); + spriter = new SpriteBatch(); + + if (cameraContext != null) { + shaper.setProjectionMatrix(cameraContext.getCamera().combined); + spriter.setProjectionMatrix(cameraContext.getCamera().combined); + } else { + Gdx.app.error("GameRenderer", "Camera context was not set"); + } + + mApplicationContextRepository = applicationContextRepository; + mGameContextRepository = gameContextRepository; + mMobsController = mobsController; + mGameWorld = gameWorld; + mRenderers = new ArrayList<>(renderers); + kotlin.collections.CollectionsKt.sortWith(mRenderers, new Comparator() { + @Override + public int compare(IGameRenderer o1, IGameRenderer o2) { + return o1.getRenderLayer() - o2.getRenderLayer(); + } + }); + mTooltipManager = tooltipManager; + mGetFontUseCase = getFontUseCase; + + Gdx.gl.glClearColor(0f, .6f, .6f, 1f); + } + + private float getCamX() { + @Nullable final CameraContext cameraContext = mGameContextRepository.getCameraContext(); + if (cameraContext != null) { + return cameraContext.getViewport().x; + } else { + Gdx.app.error("GameRenderer", "Camera context was not set"); + return 0; + } + } + + private float getCamY() { + @Nullable final CameraContext cameraContext = mGameContextRepository.getCameraContext(); + if (cameraContext != null) { + return cameraContext.getViewport().y; + } else { + Gdx.app.error("GameRenderer", "Camera context was not set"); + return 0; + } + } + + private float getWidth() { + return mApplicationContextRepository.getWidth(); + } + private float getHeight() { + return mApplicationContextRepository.getHeight(); + } + + private void setCamPos(float x, float y) { + @Nullable final CameraContext cameraContext = mGameContextRepository.getCameraContext(); + + if (cameraContext != null) { + cameraContext.getCamera().position.set(x, y, 0); + cameraContext.getViewport().x = x; + cameraContext.getViewport().y = y; + } else { + Gdx.app.error("GameRenderer", "Camera context was not set"); + } + } + + private Rectangle getCameraViewport() { + @Nullable final CameraContext cameraContext = mGameContextRepository.getCameraContext(); + return cameraContext != null ? cameraContext.getViewport() : new Rectangle(); + } + + private void updateDynamicCameraPosition(float delta) { + Player player = mMobsController.getPlayer(); + + float plTargetX = player.getX() + player.getWidth() / 2; + float plTargetY = player.getY() + player.getHeight() / 2; + + float camCenterX = getCamX() + getWidth() / 2; + float camCenterY = getCamY() + getHeight() / 2; + + float camTargetX, camTargetY; + + boolean followPlayer = player.getControlMode() == Player.ControlMode.WALK || !mApplicationContextRepository.isTouch(); + + if (followPlayer) { + camTargetX = plTargetX + Math.min(player.getVelocity().x * 2, getWidth() / 2); + camTargetY = plTargetY + player.getVelocity().y; + } else { + camTargetX = MeasureUnitsUtilsKt.getPx(player.getCursorX()) + MeasureUnitsUtilsKt.getPx(1) / 2; + camTargetY = MeasureUnitsUtilsKt.getPx(player.getCursorY()) + MeasureUnitsUtilsKt.getPx(1) / 2; + } + + Vector2 moveVector = new Vector2(camTargetX - camCenterX, camTargetY - camCenterY); + + if (followPlayer && player.getVelocity().isZero()) { + mCameraDelayMs = TimeUtils.millis(); + mCamCenterToPlayer.x = plTargetX - camCenterX; + mCamCenterToPlayer.y = plTargetY - camCenterY; + } + + if (TimeUtils.timeSinceMillis(mCameraDelayMs) < 500L && !player.getVelocity().isZero()) { + updateStaticCameraPosition(plTargetX - mCamCenterToPlayer.x, + camCenterY + moveVector.y * delta * 2); + return; + } + + float camX = getCamX(); + float camY = getCamY(); + float fullWorldPx = MeasureUnitsUtilsKt.getPx(mGameWorld.getWidth()); + float worldWidthScreenOffset = fullWorldPx - getWidth() / 2; + + if (moveVector.x >= worldWidthScreenOffset) { + camX += fullWorldPx; + moveVector.x -= fullWorldPx; + } else if (moveVector.x <= -worldWidthScreenOffset) { + camX -= fullWorldPx; + moveVector.x += fullWorldPx; + } + + setCamPos(camX + moveVector.x * delta * 2, camY + moveVector.y * delta * 2); + + + camX = getCamX(); + camY = getCamY(); + + if (camX + getWidth() / 2 > plTargetX + MAX_CAM_DISTANCE_FROM_PLAYER) { + camX = plTargetX + MAX_CAM_DISTANCE_FROM_PLAYER - getWidth() / 2; + } + + if (camY + getHeight() / 2 > plTargetY + MAX_CAM_DISTANCE_FROM_PLAYER) { + camY = plTargetY + MAX_CAM_DISTANCE_FROM_PLAYER - getHeight() / 2; + } + + if (camX + getWidth() / 2 < plTargetX - MAX_CAM_DISTANCE_FROM_PLAYER) { + camX = plTargetX - MAX_CAM_DISTANCE_FROM_PLAYER - getWidth() / 2; + } + + if (camY + getHeight() / 2 < plTargetY - MAX_CAM_DISTANCE_FROM_PLAYER) { + camY = plTargetY - MAX_CAM_DISTANCE_FROM_PLAYER - getHeight() / 2; + } + + setCamPos(camX, camY); + } + + private void updateStaticCameraPosition(float targetX, float targetY) { + setCamPos(targetX - getWidth() / 2, targetY - getHeight() / 2); + } + + private void updateStaticCameraPosition() { + Player player = mMobsController.getPlayer(); + + updateStaticCameraPosition(player.getX() + player.getWidth() / 2, + player.getY() + player.getHeight() / 2); + } + + private void updateCameraPosition(float delta) { + if (mApplicationContextRepository.useDynamicCamera()) { + updateDynamicCameraPosition(delta); + } else { + updateStaticCameraPosition(); + } + } + + private float transformScreenX(int screenX) { + return getWidth() / Gdx.graphics.getWidth() * screenX; + } + + private float transformScreenY(int screenY) { + return getHeight() / Gdx.graphics.getHeight() * screenY; + } + + private void handleMousePosition() { + final float screenX = transformScreenX(Gdx.input.getX()); + final float screenY = transformScreenY(Gdx.input.getY()); + + if (!mTooltipManager.getCurrentMouseTooltip().isEmpty()) { + RenderingUtilsKt.drawString(spriter, mGetFontUseCase.invoke(), + mTooltipManager.getCurrentMouseTooltip(), screenX + 1, screenY + 1, Color.BLACK); + RenderingUtilsKt.drawString(spriter, mGetFontUseCase.invoke(), + mTooltipManager.getCurrentMouseTooltip(), screenX, screenY, Color.WHITE); + } + } + + public void onResize() { + spriter.setProjectionMatrix(mGameContextRepository.getCameraContext().getCamera().combined); + shaper.setProjectionMatrix(mGameContextRepository.getCameraContext().getCamera().combined); + } + + public void render(float delta) { + updateCameraPosition(delta); + + if (mGameContextRepository.getJoystick() != null && mGameContextRepository.getJoystick().getActive()) { + mGameContextRepository.getJoystick().updateState( + transformScreenX(Gdx.input.getX(mGameContextRepository.getJoystick().getPointer())), + transformScreenY(Gdx.input.getY(mGameContextRepository.getJoystick().getPointer())) + ); + } + + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + + spriter.begin(); + for (IGameRenderer iGameRenderer : mRenderers) { + iGameRenderer.draw(spriter, shaper, getCameraViewport(), delta); + } + handleMousePosition(); + spriter.end(); + + } + +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/BackgroundBlocksRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/BackgroundBlocksRenderer.kt new file mode 100644 index 0000000..cc0f68a --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/BackgroundBlocksRenderer.kt @@ -0,0 +1,61 @@ +package ru.fredboy.cavedroid.ux.rendering + +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.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.forEachBlockInArea +import ru.fredboy.cavedroid.domain.assets.usecase.GetBlockDamageFrameCountUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetBlockDamageSpriteUseCase +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.rendering.annotation.BindRenderer +import javax.inject.Inject + +@GameScope +@BindRenderer +class BackgroundBlocksRenderer @Inject constructor( + gameWorld: GameWorld, + mobController: MobController, + containerController: ContainerController, + getBlockDamageFrameCount: GetBlockDamageFrameCountUseCase, + getBlockDamageSprite: GetBlockDamageSpriteUseCase, +) : BlocksRenderer(gameWorld, mobController, containerController, getBlockDamageFrameCount, getBlockDamageSprite) { + + 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 + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/BlocksRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/BlocksRenderer.kt new file mode 100644 index 0000000..be4bd99 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/BlocksRenderer.kt @@ -0,0 +1,124 @@ +package ru.fredboy.cavedroid.ux.rendering + +import com.badlogic.gdx.graphics.g2d.Sprite +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.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.assets.usecase.GetBlockDamageFrameCountUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetBlockDamageSpriteUseCase +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.domain.world.model.Layer +import ru.fredboy.cavedroid.entity.container.model.Furnace +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld + +abstract class BlocksRenderer( + protected val gameWorld: GameWorld, + protected val mobsController: MobController, + protected val containerController: ContainerController, + protected val getBlockDamageFrameCount: GetBlockDamageFrameCountUseCase, + protected val getBlockDamageSprite: GetBlockDamageSpriteUseCase, +) : IGameRenderer { + + protected abstract val background: Boolean + + private val Block.canSeeThrough + get() = isNone() || params.isTransparent + + private fun blockDamageSprite(index: Int): Sprite? { + if (index !in 0.. 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 + if (backgroundBlock is Block.Furnace) { + val furnace = containerController.getContainer(x, y, Layer.BACKGROUND.z) as? Furnace + backgroundBlock.draw(spriteBatch, drawX, drawY, furnace?.isActive ?: false) + } else { + 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 + + if (foregroundBlock is Block.Furnace) { + val furnace = containerController.getContainer(x, y, Layer.FOREGROUND.z) as? Furnace + foregroundBlock.draw(spriteBatch, drawX, drawY, furnace?.isActive ?: false) + } else { + foregroundBlock.draw(spriteBatch, drawX, drawY) + } + } + } + + companion object { + private const val MAX_BLOCK_DAMAGE_INDEX = 10 + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DebugInfoStringsProvider.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DebugInfoStringsProvider.kt new file mode 100644 index 0000000..f4d4baf --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DebugInfoStringsProvider.kt @@ -0,0 +1,39 @@ +package ru.fredboy.cavedroid.ux.rendering + +import com.badlogic.gdx.Gdx +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import javax.inject.Inject + +@GameScope +class DebugInfoStringsProvider @Inject constructor( + private val mobController: MobController, + private val dropController: DropController, + private val containerController: ContainerController, + private val gameWorld: GameWorld, +) { + + fun getDebugStrings(): List { + val player = mobController.player + + return listOf( + "FPS: ${Gdx.graphics.framesPerSecond}", + "X: ${player.mapX}", + "Y: ${player.upperMapY} (${gameWorld.height - player.upperMapY})", + "CurX: ${player.cursorX}", + "CurY: ${player.cursorY}", + "Velocity: ${player.velocity}", + "Swim: ${player.swim}", + "Mobs: ${mobController.mobs.size}", + "Drops: ${dropController.size}", + "Containers: ${containerController.size}", + "Block: ${gameWorld.getForeMap(player.cursorX, player.cursorY).params.key}", + "Hand: ${player.activeItem.item.params.key}", + "Game mode: ${player.gameMode}", + "Block damage: ${player.blockDamage}", + ) + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DebugRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DebugRenderer.kt new file mode 100644 index 0000000..c353501 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DebugRenderer.kt @@ -0,0 +1,115 @@ +package ru.fredboy.cavedroid.ux.rendering + +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.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.bl +import ru.fredboy.cavedroid.common.utils.drawString +import ru.fredboy.cavedroid.common.utils.forEachBlockInArea +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.assets.usecase.GetFontUseCase +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import ru.fredboy.cavedroid.domain.items.model.block.Block +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.rendering.annotation.BindRenderer +import javax.inject.Inject + +@GameScope +@BindRenderer +class DebugRenderer @Inject constructor( + private val gameContextRepository: GameContextRepository, + private val gameWorld: GameWorld, + private val mobController: MobController, + private val debugInfoStringsProvider: DebugInfoStringsProvider, + private val getFont: GetFontUseCase, +) : 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 = */ mobController.player.x - (minimapSize.px / 2), + /* y = */ mobController.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.color = 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(getFont(), str, 0f, index * 10f) + } + } + + override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) { + if (gameContextRepository.shouldShowInfo()) { + drawDebugInfo(spriteBatch) + } + + if (gameContextRepository.shouldShowMap()) { + 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 + } + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DropsRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DropsRenderer.kt new file mode 100644 index 0000000..a52b084 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/DropsRenderer.kt @@ -0,0 +1,41 @@ +package ru.fredboy.cavedroid.ux.rendering + +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.cycledInsideWorld +import ru.fredboy.cavedroid.common.utils.drawSprite +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.rendering.annotation.BindRenderer +import javax.inject.Inject + +@GameScope +@BindRenderer +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 + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/ForegroundBlocksRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/ForegroundBlocksRenderer.kt new file mode 100644 index 0000000..2b04553 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/ForegroundBlocksRenderer.kt @@ -0,0 +1,40 @@ +package ru.fredboy.cavedroid.ux.rendering + +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.forEachBlockInArea +import ru.fredboy.cavedroid.domain.assets.usecase.GetBlockDamageFrameCountUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetBlockDamageSpriteUseCase +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.rendering.annotation.BindRenderer +import javax.inject.Inject + +@GameScope +@BindRenderer +class ForegroundBlocksRenderer @Inject constructor( + gameWorld: GameWorld, + mobController: MobController, + containerController: ContainerController, + getBlockDamageFrameCount: GetBlockDamageFrameCountUseCase, + getBlockDamageSprite: GetBlockDamageSpriteUseCase, +) : BlocksRenderer(gameWorld, mobController, containerController, getBlockDamageFrameCount, getBlockDamageSprite) { + + 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 + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/HudRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/HudRenderer.kt new file mode 100644 index 0000000..e68e686 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/HudRenderer.kt @@ -0,0 +1,155 @@ +package ru.fredboy.cavedroid.ux.rendering + +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.drawString +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.domain.assets.usecase.GetFontUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringHeightUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringWidthUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.TooltipManager +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.rendering.annotation.BindRenderer +import javax.inject.Inject + +@GameScope +@BindRenderer +class HudRenderer @Inject constructor( + private val gameWorld: GameWorld, + private val mobController: MobController, + private val tooltipManager: TooltipManager, + private val textureRegions: GetTextureRegionByNameUseCase, + private val getStringWidth: GetStringWidthUseCase, + private val getStringHeight: GetStringHeightUseCase, + private val getFont: GetFontUseCase, +) : IGameRenderer { + + override val renderLayer = RENDER_LAYER + + private val cursorTexture get() = requireNotNull(textureRegions[CURSOR_KEY]) + private val hotbarTexture get() = requireNotNull(textureRegions[HOTBAR_KEY]) + private val hotbarSelectorTexture get() = requireNotNull(textureRegions[HOTBAR_SELECTOR_KEY]) + private val wholeHeartTexture get() = requireNotNull(textureRegions[WHOLE_HEART_KEY]) + private val emptyHeartTexture get() = requireNotNull(textureRegions[EMPTY_HEART_KEY]) + private val halfHeartTexture get() = requireNotNull(textureRegions[HALF_HEART_KEY]) + + private fun drawCursor(spriteBatch: SpriteBatch, viewport: Rectangle) { + val cursorX = mobController.player.cursorX + val cursorY = mobController.player.cursorY + + if (gameWorld.hasForeAt(cursorX, cursorY) || + gameWorld.hasBackAt(cursorX, cursorY) || + mobController.player.controlMode == Player.ControlMode.CURSOR + ) { + spriteBatch.draw(cursorTexture, cursorX.px - viewport.x, cursorY.px - viewport.y) + } + } + + private fun drawHealth(spriteBatch: SpriteBatch, x: Float, y: Float) { + val player = mobController.player + + if (player.gameMode == 1) { + return + } + + val wholeHeart = wholeHeartTexture + val halfHeart = halfHeartTexture + val emptyHeart = emptyHeartTexture + + val totalHearts = Player.MAX_HEALTH / 2 + val wholeHearts = player.health / 2 + + for (i in 0.. + if (item.item.isNone()) { + return@forEachIndexed + } + + item.draw( + spriteBatch = spriteBatch, + shapeRenderer = shapeRenderer, + font = getFont(), + x = hotbarX + HotbarConfig.horizontalMargin + + index * (HotbarConfig.itemSeparatorWidth + HotbarConfig.itemSlotSpace), + y = HotbarConfig.verticalMargin, + getStringWidth = getStringWidth::invoke, + getStringHeight = getStringHeight::invoke, + ) + } + } + + private fun drawHotbarSelector(spriteBatch: SpriteBatch, hotbarX: Float) { + spriteBatch.draw( + /* region = */ hotbarSelectorTexture, + /* x = */ hotbarX - HotbarSelectorConfig.horizontalPadding + + mobController.player.activeSlot * (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) + + val tooltip = tooltipManager.currentHotbarTooltip + if (tooltip.isNotBlank()) { + spriteBatch.drawString( + font = getFont(), + str = tooltip, + x = viewport.width / 2 - getStringWidth(tooltip) / 2, + y = hotbarTexture.regionHeight.toFloat(), + ) + } + } + + 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 const val EMPTY_HEART_KEY = "heart_empty" + + 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 + } + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/IGameRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/IGameRenderer.kt new file mode 100644 index 0000000..1ea8210 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/IGameRenderer.kt @@ -0,0 +1,20 @@ +package ru.fredboy.cavedroid.ux.rendering + +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, + ) +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/MobsRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/MobsRenderer.kt new file mode 100644 index 0000000..bc38011 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/MobsRenderer.kt @@ -0,0 +1,47 @@ +package ru.fredboy.cavedroid.ux.rendering + +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.utils.cycledInsideWorld +import ru.fredboy.cavedroid.common.utils.px +import ru.fredboy.cavedroid.entity.mob.model.Mob +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld +import ru.fredboy.cavedroid.ux.rendering.annotation.BindRenderer +import javax.inject.Inject + +@GameScope +@BindRenderer +class MobsRenderer @Inject constructor( + private val mobController: MobController, + 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 = mobController.player + player.draw( + /* spriteBatch = */ spriteBatch, + /* x = */ player.x - viewport.x - player.width / 2, + /* y = */ player.y - viewport.y, + /* delta = */ delta, + ) + + mobController.mobs.forEach { mob -> + drawMob(spriteBatch, viewport, mob, delta) + } + } + + companion object { + private const val RENDER_LAYER = 100100 + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/TouchControlsRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/TouchControlsRenderer.kt new file mode 100644 index 0000000..00f02db --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/TouchControlsRenderer.kt @@ -0,0 +1,108 @@ +package ru.fredboy.cavedroid.ux.rendering + +import com.badlogic.gdx.graphics.g2d.Sprite +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.common.model.Joystick +import ru.fredboy.cavedroid.common.utils.drawSprite +import ru.fredboy.cavedroid.domain.assets.model.TouchButton +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetTouchButtonsUseCase +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.rendering.annotation.BindRenderer +import javax.inject.Inject + +@GameScope +@BindRenderer +class TouchControlsRenderer @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val gameContextRepository: GameContextRepository, + private val mobController: MobController, + private val gameWindowsManager: GameWindowsManager, + private val textureRegions: GetTextureRegionByNameUseCase, + private val getTouchButtons: GetTouchButtonsUseCase, +) : IGameRenderer { + + override val renderLayer get() = RENDER_LAYER + + private val shadeTexture get() = textureRegions[SHADE_KEY] + + private val joyBackground = Sprite(textureRegions["joy_background"]) + private val joyStick = Sprite(textureRegions["joy_stick"]) + + private fun drawJoystick(spriteBatch: SpriteBatch) { + val joystick = gameContextRepository.getJoystick().takeIf { it.active } ?: return + + spriteBatch.drawSprite( + sprite = joyBackground, + x = joystick.centerX - Joystick.RADIUS, + y = joystick.centerY - Joystick.RADIUS, + width = Joystick.SIZE, + height = Joystick.SIZE, + ) + + spriteBatch.drawSprite( + sprite = joyStick, + x = joystick.activeX - Joystick.STICK_SIZE / 2, + y = joystick.activeY - Joystick.STICK_SIZE / 2, + width = Joystick.STICK_SIZE, + height = Joystick.STICK_SIZE, + ) + } + + private val TouchButton.rectangleOnScreen + get() = Rectangle( + /* x = */ if (rectangle.x < 0f) { + applicationContextRepository.getWidth() + rectangle.x + } else { + rectangle.x + }, + /* y = */ if (rectangle.y < 0f) { + applicationContextRepository.getHeight() + rectangle.y + } else { + rectangle.y + }, + /* width = */ rectangle.width, + /* height = */ rectangle.height, + ) + + override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) { + if (!applicationContextRepository.isTouch() || gameWindowsManager.currentWindowType != GameWindowType.NONE) { + return + } + + val touchControlsMap = getTouchButtons() + + touchControlsMap.forEach { (key, value) -> + val touchKey = value.rectangleOnScreen + spriteBatch.draw( + /* region = */ textureRegions[key], + /* x = */ touchKey.x, + /* y = */ touchKey.y, + /* width = */ touchKey.width, + /* height = */ touchKey.height, + ) + } + + // FIXME: Add pressed state for buttons + if (mobController.player.controlMode == Player.ControlMode.CURSOR) { + val altKeyRect = touchControlsMap["alt"]?.rectangleOnScreen ?: return + spriteBatch.draw(shadeTexture, altKeyRect.x, altKeyRect.y, altKeyRect.width, altKeyRect.height) + } + + drawJoystick(spriteBatch) + } + + companion object { + private const val RENDER_LAYER = 100700 + + private const val SHADE_KEY = "shade" + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/WindowsRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/WindowsRenderer.kt new file mode 100644 index 0000000..b06c334 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/WindowsRenderer.kt @@ -0,0 +1,48 @@ +package ru.fredboy.cavedroid.ux.rendering + +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.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.game.window.GameWindowType +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.ux.rendering.annotation.BindRenderer +import ru.fredboy.cavedroid.ux.rendering.windows.ChestWindowRenderer +import ru.fredboy.cavedroid.ux.rendering.windows.CraftingWindowRenderer +import ru.fredboy.cavedroid.ux.rendering.windows.CreativeWindowRenderer +import ru.fredboy.cavedroid.ux.rendering.windows.FurnaceWindowRenderer +import ru.fredboy.cavedroid.ux.rendering.windows.SurvivalWindowRenderer +import javax.inject.Inject + +@GameScope +@BindRenderer +class WindowsRenderer @Inject constructor( + private val creativeWindowRenderer: CreativeWindowRenderer, + private val survivalWindowRenderer: SurvivalWindowRenderer, + private val craftingWindowRenderer: CraftingWindowRenderer, + private val gameWindowsManager: GameWindowsManager, + private val furnaceWindowRenderer: FurnaceWindowRenderer, + private val chestWindowRenderer: ChestWindowRenderer, +) : IGameRenderer { + + override val renderLayer get() = RENDER_LAYER + + override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) { + when (val windowType = gameWindowsManager.currentWindowType) { + GameWindowType.CREATIVE_INVENTORY -> creativeWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta) + GameWindowType.SURVIVAL_INVENTORY -> survivalWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta) + GameWindowType.CRAFTING_TABLE -> craftingWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta) + GameWindowType.FURNACE -> furnaceWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta) + GameWindowType.CHEST -> chestWindowRenderer.draw(spriteBatch, shapeRenderer, viewport, delta) + GameWindowType.NONE -> return + else -> Gdx.app.error(TAG, "Cannot draw window: ${windowType.name}") + } + } + + companion object { + private const val TAG = "WindowsRenderer" + + const val RENDER_LAYER = 100600 + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/annotation/BindRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/annotation/BindRenderer.kt new file mode 100644 index 0000000..9d868e5 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/annotation/BindRenderer.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.ux.rendering.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoSet +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.ux.rendering.IGameRenderer + +@BindsIntoSet( + interfaceClass = IGameRenderer::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "RenderModule", +) +annotation class BindRenderer diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/AbstractWindowRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/AbstractWindowRenderer.kt new file mode 100644 index 0000000..ce94926 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/AbstractWindowRenderer.kt @@ -0,0 +1,63 @@ +package ru.fredboy.cavedroid.ux.rendering.windows + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.g2d.BitmapFont +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import ru.fredboy.cavedroid.common.utils.drawSprite +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringHeightUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringWidthUseCase +import ru.fredboy.cavedroid.domain.items.model.inventory.InventoryItem +import ru.fredboy.cavedroid.domain.items.model.item.Item + +abstract class AbstractWindowRenderer { + + protected inline fun drawItemsGrid( + spriteBatch: SpriteBatch, + shapeRenderer: ShapeRenderer, + font: BitmapFont, + gridX: Float, + gridY: Float, + items: Iterable, + itemsInRow: Int, + cellWidth: Float, + cellHeight: Float, + getStringWidth: GetStringWidthUseCase, + getStringHeight: GetStringHeightUseCase, + ) { + 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 = spriteBatch, + shapeRenderer = shapeRenderer, + font = font, + x = itemX, + y = itemY, + getStringWidth = getStringWidth::invoke, + getStringHeight = getStringHeight::invoke, + ) ?: item?.let { spriteBatch.drawSprite(it.sprite, itemX, itemY) } + } + } + + companion object { + protected const val _TAG = "AbstractWindowRenderer" + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/ChestWindowRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/ChestWindowRenderer.kt new file mode 100644 index 0000000..9e072da --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/ChestWindowRenderer.kt @@ -0,0 +1,104 @@ +package ru.fredboy.cavedroid.ux.rendering.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.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetFontUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringHeightUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringWidthUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.ChestInventoryWindow +import ru.fredboy.cavedroid.ux.rendering.IGameRenderer +import ru.fredboy.cavedroid.ux.rendering.WindowsRenderer +import javax.inject.Inject + +@GameScope +class ChestWindowRenderer @Inject constructor( + private val mobController: MobController, + private val gameWindowsManager: GameWindowsManager, + private val textureRegions: GetTextureRegionByNameUseCase, + private val getStringWidth: GetStringWidthUseCase, + private val getStringHeight: GetStringHeightUseCase, + private val getFont: GetFontUseCase, +) : AbstractWindowRenderer(), + IGameRenderer { + + override val renderLayer get() = WindowsRenderer.Companion.RENDER_LAYER + + private val chestWindowTexture get() = requireNotNull(textureRegions[CHEST_WINDOW_KEY]) + + override fun draw(spriteBatch: SpriteBatch, shapeRenderer: ShapeRenderer, viewport: Rectangle, delta: Float) { + val windowTexture = chestWindowTexture + val window = gameWindowsManager.currentWindow as ChestInventoryWindow + + val windowX = viewport.width / 2 - windowTexture.regionWidth / 2 + val windowY = viewport.height / 2 - windowTexture.regionHeight / 2 + + spriteBatch.draw(windowTexture, windowX, windowY) + + drawItemsGrid( + spriteBatch = spriteBatch, + shapeRenderer = shapeRenderer, + font = getFont(), + gridX = windowX + GameWindowsConfigs.Chest.contentsMarginLeft, + gridY = windowY + GameWindowsConfigs.Chest.contentsMarginTop, + items = window.chest.items, + itemsInRow = GameWindowsConfigs.Chest.itemsInRow, + cellWidth = GameWindowsConfigs.Chest.itemsGridColWidth, + cellHeight = GameWindowsConfigs.Chest.itemsGridRowHeight, + getStringWidth = getStringWidth, + getStringHeight = getStringHeight, + ) + + drawItemsGrid( + spriteBatch = spriteBatch, + shapeRenderer = shapeRenderer, + font = getFont(), + gridX = windowX + GameWindowsConfigs.Chest.itemsGridMarginLeft, + gridY = windowY + GameWindowsConfigs.Chest.itemsGridMarginTop, + items = mobController.player.inventory.items.asSequence() + .drop(GameWindowsConfigs.Chest.hotbarCells) + .take(GameWindowsConfigs.Chest.itemsInCol * GameWindowsConfigs.Chest.itemsInRow) + .asIterable(), + itemsInRow = GameWindowsConfigs.Chest.itemsInRow, + cellWidth = GameWindowsConfigs.Chest.itemsGridColWidth, + cellHeight = GameWindowsConfigs.Chest.itemsGridRowHeight, + getStringWidth = getStringWidth, + getStringHeight = getStringHeight, + ) + + drawItemsGrid( + spriteBatch = spriteBatch, + shapeRenderer = shapeRenderer, + font = getFont(), + gridX = windowX + GameWindowsConfigs.Chest.itemsGridMarginLeft, + gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Chest.hotbarOffsetFromBottom, + items = mobController.player.inventory.items.asSequence() + .take(GameWindowsConfigs.Chest.hotbarCells) + .asIterable(), + itemsInRow = GameWindowsConfigs.Chest.hotbarCells, + cellWidth = GameWindowsConfigs.Chest.itemsGridColWidth, + cellHeight = GameWindowsConfigs.Chest.itemsGridRowHeight, + getStringWidth = getStringWidth, + getStringHeight = getStringHeight, + ) + + window.selectedItem?.drawSelected( + spriteBatch = spriteBatch, + font = getFont(), + x = Gdx.input.x * (viewport.width / Gdx.graphics.width), + y = Gdx.input.y * (viewport.height / Gdx.graphics.height), + getStringWidth = getStringWidth::invoke, + getStringHeight = getStringHeight::invoke, + ) + } + + companion object { + private const val CHEST_WINDOW_KEY = "chest" + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/CraftingWindowRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/CraftingWindowRenderer.kt new file mode 100644 index 0000000..6e17369 --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/CraftingWindowRenderer.kt @@ -0,0 +1,114 @@ +package ru.fredboy.cavedroid.ux.rendering.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.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetFontUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringHeightUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringWidthUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.CraftingInventoryWindow +import ru.fredboy.cavedroid.ux.rendering.IGameRenderer +import ru.fredboy.cavedroid.ux.rendering.WindowsRenderer +import javax.inject.Inject + +@GameScope +class CraftingWindowRenderer @Inject constructor( + private val mobController: MobController, + private val gameWindowsManager: GameWindowsManager, + private val textureRegions: GetTextureRegionByNameUseCase, + private val getStringWidth: GetStringWidthUseCase, + private val getStringHeight: GetStringHeightUseCase, + private val getFont: GetFontUseCase, +) : AbstractWindowRenderer(), + IGameRenderer { + + override val renderLayer get() = WindowsRenderer.Companion.RENDER_LAYER + + private val craftingWindowTexture get() = requireNotNull(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, + font = getFont(), + gridX = windowX + GameWindowsConfigs.Crafting.itemsGridMarginLeft, + gridY = windowY + GameWindowsConfigs.Crafting.itemsGridMarginTop, + items = mobController.player.inventory.items.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, + getStringWidth = getStringWidth, + getStringHeight = getStringHeight, + ) + + drawItemsGrid( + spriteBatch = spriteBatch, + shapeRenderer = shapeRenderer, + font = getFont(), + gridX = windowX + GameWindowsConfigs.Crafting.itemsGridMarginLeft, + gridY = windowY + windowTexture.regionHeight - GameWindowsConfigs.Crafting.hotbarOffsetFromBottom, + items = mobController.player.inventory.items.asSequence() + .take(GameWindowsConfigs.Crafting.hotbarCells) + .asIterable(), + itemsInRow = GameWindowsConfigs.Crafting.hotbarCells, + cellWidth = GameWindowsConfigs.Crafting.itemsGridColWidth, + cellHeight = GameWindowsConfigs.Crafting.itemsGridRowHeight, + getStringWidth = getStringWidth, + getStringHeight = getStringHeight, + ) + + drawItemsGrid( + spriteBatch = spriteBatch, + shapeRenderer = shapeRenderer, + font = getFont(), + gridX = windowX + GameWindowsConfigs.Crafting.craftOffsetX, + gridY = windowY + GameWindowsConfigs.Crafting.craftOffsetY, + items = window.craftingItems, + itemsInRow = GameWindowsConfigs.Crafting.craftGridSize, + cellWidth = GameWindowsConfigs.Crafting.itemsGridColWidth, + cellHeight = GameWindowsConfigs.Crafting.itemsGridRowHeight, + getStringWidth = getStringWidth, + getStringHeight = getStringHeight, + ) + + window.craftResult.draw( + spriteBatch = spriteBatch, + shapeRenderer = shapeRenderer, + font = getFont(), + x = windowX + GameWindowsConfigs.Crafting.craftResultOffsetX, + y = windowY + GameWindowsConfigs.Crafting.craftResultOffsetY, + getStringWidth = getStringWidth::invoke, + getStringHeight = getStringHeight::invoke, + ) + + window.selectedItem?.drawSelected( + spriteBatch = spriteBatch, + font = getFont(), + x = Gdx.input.x * (viewport.width / Gdx.graphics.width), + y = Gdx.input.y * (viewport.height / Gdx.graphics.height), + getStringWidth = getStringWidth::invoke, + getStringHeight = getStringHeight::invoke, + ) + } + + companion object { + private const val CRAFTING_WINDOW_KEY = "crafting_table" + } +} diff --git a/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/CreativeWindowRenderer.kt b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/CreativeWindowRenderer.kt new file mode 100644 index 0000000..2333f6f --- /dev/null +++ b/core/ux/rendering/src/main/kotlin/ru/fredboy/cavedroid/ux/rendering/windows/CreativeWindowRenderer.kt @@ -0,0 +1,96 @@ +package ru.fredboy.cavedroid.ux.rendering.windows + +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.usecase.GetFontUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringHeightUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringWidthUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.window.GameWindowsConfigs +import ru.fredboy.cavedroid.game.window.GameWindowsManager +import ru.fredboy.cavedroid.game.window.inventory.CreativeInventoryWindow +import ru.fredboy.cavedroid.ux.rendering.IGameRenderer +import ru.fredboy.cavedroid.ux.rendering.WindowsRenderer +import javax.inject.Inject +import kotlin.math.min + +@GameScope +class CreativeWindowRenderer @Inject constructor( + private val gameWindowsManager: GameWindowsManager, + private val itemsRepository: ItemsRepository, + private val mobController: MobController, + private val textureRegions: GetTextureRegionByNameUseCase, + private val getStringWidth: GetStringWidthUseCase, + private val getStringHeight: GetStringHeightUseCase, + private val getFont: GetFontUseCase, +) : AbstractWindowRenderer(), + IGameRenderer { + + override val renderLayer get() = WindowsRenderer.Companion.RENDER_LAYER + + private val creativeWindowTexture get() = requireNotNull(textureRegions[CREATIVE_WINDOW_KEY]) + private val scrollIndicatorTexture get() = requireNotNull(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 / + (gameWindowsManager.currentWindow as CreativeInventoryWindow).getMaxScroll(itemsRepository) + + spriteBatch.draw(creativeWindow, windowX, windowY) + spriteBatch.draw( + /* region = */ scrollIndicatorTexture, + /* x = */ windowX + GameWindowsConfigs.Creative.scrollIndicatorMarginLeft, + /* y = */ windowY + GameWindowsConfigs.Creative.scrollIndicatorMarginTop + + (gameWindowsManager.creativeScrollAmount * oneScrollAmount), + ) + + val allItems = itemsRepository.getAllItems() + val startIndex = gameWindowsManager.creativeScrollAmount * GameWindowsConfigs.Creative.itemsInRow + val endIndex = min(startIndex + GameWindowsConfigs.Creative.itemsOnPage, allItems.size) + val items = sequence { + for (i in startIndex.. + if (index % 3 > 1 || index / 3 > 1) { + null + } else { + it + } + }.asIterable(), + itemsInRow = GameWindowsConfigs.Survival.craftGridSize, + cellWidth = GameWindowsConfigs.Survival.itemsGridColWidth, + cellHeight = GameWindowsConfigs.Survival.itemsGridRowHeight, + getStringWidth = getStringWidth, + getStringHeight = getStringHeight, + ) + + window.craftResult.draw( + spriteBatch = spriteBatch, + shapeRenderer = shapeRenderer, + font = getFont(), + x = windowX + GameWindowsConfigs.Survival.craftResultOffsetX, + y = windowY + GameWindowsConfigs.Survival.craftResultOffsetY, + getStringWidth = getStringWidth::invoke, + getStringHeight = getStringHeight::invoke, + ) + + window.selectedItem?.drawSelected( + spriteBatch = spriteBatch, + font = getFont(), + x = Gdx.input.x * (viewport.width / Gdx.graphics.width), + y = Gdx.input.y * (viewport.height / Gdx.graphics.height), + getStringWidth = getStringWidth::invoke, + getStringHeight = getStringHeight::invoke, + ) + } + + companion object { + private const val SURVIVAL_WINDOW_KEY = "survival" + } +} diff --git a/core/zygote/build.gradle.kts b/core/zygote/build.gradle.kts new file mode 100644 index 0000000..d315c7f --- /dev/null +++ b/core/zygote/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + kotlin + ksp +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useAutomultibind() + useLibgdx() + useDagger() + + useCommonModule() + useDataModules() + useDomainModules() + useEntityModules() + useGameModules() + useUxModules() + + useModule(":core:domain:menu") +} diff --git a/core/zygote/src/main/java/ru/fredboy/cavedroid/zygote/game/GameProc.java b/core/zygote/src/main/java/ru/fredboy/cavedroid/zygote/game/GameProc.java new file mode 100644 index 0000000..44099cb --- /dev/null +++ b/core/zygote/src/main/java/ru/fredboy/cavedroid/zygote/game/GameProc.java @@ -0,0 +1,91 @@ +package ru.fredboy.cavedroid.zygote.game; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.utils.Disposable; +import com.badlogic.gdx.utils.Timer; +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository; +import ru.fredboy.cavedroid.ux.controls.GameInputProcessor; +import ru.fredboy.cavedroid.ux.physics.GamePhysics; +import ru.fredboy.cavedroid.ux.physics.task.GameWorldBlocksLogicControllerTask; +import ru.fredboy.cavedroid.ux.physics.task.GameWorldFluidsLogicControllerTask; +import ru.fredboy.cavedroid.ux.physics.task.GameWorldMobDamageControllerTask; +import ru.fredboy.cavedroid.common.di.GameScope; +import ru.fredboy.cavedroid.entity.mob.model.Player; +import ru.fredboy.cavedroid.game.controller.container.ContainerController; +import ru.fredboy.cavedroid.game.controller.mob.MobController; +import ru.fredboy.cavedroid.ux.rendering.GameRenderer; + +import javax.inject.Inject; + +@GameScope +public class GameProc implements Disposable { + + private final GamePhysics mGamePhysics; + private final GameRenderer mGameRenderer; + private final MobController mMobsController; + private final ContainerController mContainerController; + private final GameWorldFluidsLogicControllerTask mGameWorldFluidsLogicControllerTask; + private final GameWorldBlocksLogicControllerTask mGameWorldBlocksLogicControllerTask; + private final GameWorldMobDamageControllerTask mGameWorldMobDamageControllerTask; + private final GameInputProcessor mGameInputProcessor; + + private final Timer mWorldLogicTimer = new Timer(); + + @Inject + public GameProc(ApplicationContextRepository gameContextRepository, + GamePhysics gamePhysics, + GameRenderer gameRenderer, + MobController mobsController, + ContainerController containerController, + GameWorldFluidsLogicControllerTask gameWorldFluidsLogicControllerTask, + GameWorldBlocksLogicControllerTask gameWorldBlocksLogicControllerTask, + GameWorldMobDamageControllerTask gameWorldMobDamageControllerTask, + GameInputProcessor gameInputProcessor + ) { + mGamePhysics = gamePhysics; + mGameRenderer = gameRenderer; + mMobsController = mobsController; + mContainerController = containerController; + mGameWorldFluidsLogicControllerTask = gameWorldFluidsLogicControllerTask; + mGameWorldBlocksLogicControllerTask = gameWorldBlocksLogicControllerTask; + mGameWorldMobDamageControllerTask = gameWorldMobDamageControllerTask; + mGameInputProcessor = gameInputProcessor; + + mobsController.getPlayer().setControlMode(gameContextRepository.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().setGameMode(gameMode); + } + + public void update(float delta) { + mMobsController.update(delta); + mGamePhysics.update(delta); + mGameInputProcessor.update(delta); + mGameRenderer.render(delta); + mContainerController.update(delta); + } + + public void onResize() { + mGameRenderer.onResize(); + } + + public void show() { + Gdx.input.setInputProcessor(mGameInputProcessor); + } + + @Override + public void dispose() { + mWorldLogicTimer.stop(); + mGameWorldFluidsLogicControllerTask.cancel(); + mGameWorldBlocksLogicControllerTask.cancel(); + mGameWorldMobDamageControllerTask.cancel(); + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/CaveDroidApplication.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/CaveDroidApplication.kt new file mode 100644 index 0000000..ba699da --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/CaveDroidApplication.kt @@ -0,0 +1,110 @@ +package ru.fredboy.cavedroid.zygote + +import com.badlogic.gdx.Application +import com.badlogic.gdx.Game +import com.badlogic.gdx.Gdx +import ru.fredboy.cavedroid.common.api.ApplicationController +import ru.fredboy.cavedroid.common.api.PreferencesStore +import ru.fredboy.cavedroid.common.utils.ratio +import ru.fredboy.cavedroid.data.configuration.model.ApplicationContext +import ru.fredboy.cavedroid.zygote.di.ApplicationComponent +import ru.fredboy.cavedroid.zygote.di.DaggerApplicationComponent +import ru.fredboy.cavedroid.zygote.game.GameScreen + +class CaveDroidApplication( + private val gameDataDirectoryPath: String, + private val isTouchScreen: Boolean, + private val isDebug: Boolean, + private val preferencesStore: PreferencesStore, +) : Game(), + ApplicationController { + + lateinit var applicationComponent: ApplicationComponent + private set + + private fun newGame(gameMode: Int) { + setScreen(applicationComponent.gameScreen.apply { newGame(gameMode) }) + } + + private fun initFullscreenMode(isFullscreen: Boolean) { + if (Gdx.app.type != Application.ApplicationType.Desktop) { + return + } + + if (isFullscreen) { + Gdx.graphics.setFullscreenMode(Gdx.graphics.displayMode) + } else { + Gdx.graphics.setWindowedMode(960, 540) + } + } + + override fun create() { + val width = DEFAULT_VIEWPORT_WIDTH + val height = width / Gdx.graphics.ratio + + val isFullscreen = preferencesStore.getPreference("fullscreen").toBoolean() + initFullscreenMode(isFullscreen) + + applicationComponent = DaggerApplicationComponent.builder() + .applicationContext( + ApplicationContext( + isDebug = isDebug, + isTouch = isTouchScreen, + gameDirectory = gameDataDirectoryPath, + width = width, + height = height, + isFullscreen = isFullscreen, + useDynamicCamera = preferencesStore.getPreference("dyncam").toBoolean(), + screenScale = (preferencesStore.getPreference("screen_scale") ?: "2").toInt(), + ), + ) + .applicationController(this) + .preferencesStore(preferencesStore) + .build() + + Gdx.app.logLevel = Application.LOG_DEBUG + + Gdx.files.absolute(gameDataDirectoryPath).mkdirs() + applicationComponent.initializeAssets() + setScreen(applicationComponent.menuScreen) + } + + override fun dispose() { + applicationComponent.menuScreen.dispose() + applicationComponent.gameScreen.dispose() + applicationComponent.disposeAssets() + } + + override fun quitGame() { + (screen as? GameScreen)?.let { gameScreen -> + gameScreen.dispose() + setScreen(applicationComponent.menuScreen) + } ?: Gdx.app.error(TAG, "quitGame called when active screen is not Game") + } + + override fun newGameCreative() { + newGame(1) + } + + override fun newGameSurvival() { + newGame(0) + } + + override fun loadGame() { + setScreen(applicationComponent.gameScreen.apply { loadGame() }) + } + + override fun exitGame() { + setScreen(null) + Gdx.app.exit() + } + + override fun triggerResize() { + resize(Gdx.graphics.width, Gdx.graphics.height) + } + + companion object { + private const val TAG = "CaveDroidApplication" + private const val DEFAULT_VIEWPORT_WIDTH = 480f + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/base/BaseScreen.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/base/BaseScreen.kt new file mode 100644 index 0000000..3a215b8 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/base/BaseScreen.kt @@ -0,0 +1,14 @@ +package ru.fredboy.cavedroid.zygote.base + +import com.badlogic.gdx.Screen +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository + +abstract class BaseScreen( + private val applicationContextRepository: ApplicationContextRepository, +) : Screen { + + override fun resize(width: Int, height: Int) { + applicationContextRepository.setWidth(width.toFloat() / applicationContextRepository.getScreenScale()) + applicationContextRepository.setHeight(height.toFloat() / applicationContextRepository.getScreenScale()) + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/di/ApplicationComponent.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/di/ApplicationComponent.kt new file mode 100644 index 0000000..9ad5d5b --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/di/ApplicationComponent.kt @@ -0,0 +1,83 @@ +package ru.fredboy.cavedroid.zygote.di + +import dagger.BindsInstance +import dagger.Component +import ru.fredboy.cavedroid.common.api.ApplicationController +import ru.fredboy.cavedroid.common.api.PreferencesStore +import ru.fredboy.cavedroid.data.assets.di.DataAssetsModule +import ru.fredboy.cavedroid.data.configuration.di.ApplicationContextModule +import ru.fredboy.cavedroid.data.configuration.model.ApplicationContext +import ru.fredboy.cavedroid.data.items.di.DataItemsModule +import ru.fredboy.cavedroid.data.save.di.DataSaveModule +import ru.fredboy.cavedroid.domain.assets.repository.BlockAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.BlockDamageAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.FontAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.ItemsAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.MobAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.TextureRegionsAssetsRepository +import ru.fredboy.cavedroid.domain.assets.repository.TouchButtonsAssetsRepository +import ru.fredboy.cavedroid.domain.assets.usecase.DisposeAssetsUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.InitializeAssetsUseCase +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.domain.save.repository.SaveDataRepository +import ru.fredboy.cavedroid.zygote.game.GameScreen +import ru.fredboy.cavedroid.zygote.menu.MenuScreen +import javax.inject.Singleton + +@Singleton +@Component( + modules = [ + DataAssetsModule::class, + DataItemsModule::class, + DataSaveModule::class, + ApplicationContextModule::class, + ], +) +interface ApplicationComponent { + + val initializeAssets: InitializeAssetsUseCase + + val disposeAssets: DisposeAssetsUseCase + + val gameScreen: GameScreen + + val menuScreen: MenuScreen + + val applicationContextRepository: ApplicationContextRepository + + val blockAssetsRepository: BlockAssetsRepository + + val blockDamageAssetsRepository: BlockDamageAssetsRepository + + val fontAssetsRepository: FontAssetsRepository + + val mobAssetsRepository: MobAssetsRepository + + val itemAssetsRepository: ItemsAssetsRepository + + val textureRegionsAssetsRepository: TextureRegionsAssetsRepository + + val touchButtonsAssetsRepository: TouchButtonsAssetsRepository + + val itemsRepository: ItemsRepository + + val saveDataRepository: SaveDataRepository + + val applicationController: ApplicationController + + @Component.Builder + interface Builder { + + @BindsInstance + fun applicationContext(context: ApplicationContext): Builder + + @BindsInstance + fun applicationController(impl: ApplicationController): Builder + + @BindsInstance + fun preferencesStore(impl: PreferencesStore): Builder + + fun build(): ApplicationComponent + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/GameScreen.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/GameScreen.kt new file mode 100644 index 0000000..ccc72c8 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/GameScreen.kt @@ -0,0 +1,117 @@ +package ru.fredboy.cavedroid.zygote.game + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.OrthographicCamera +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.model.Joystick +import ru.fredboy.cavedroid.data.configuration.model.GameContext +import ru.fredboy.cavedroid.domain.configuration.model.CameraContext +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.entity.mob.model.Player +import ru.fredboy.cavedroid.zygote.CaveDroidApplication +import ru.fredboy.cavedroid.zygote.base.BaseScreen +import ru.fredboy.cavedroid.zygote.game.di.DaggerGameComponent +import ru.fredboy.cavedroid.zygote.game.di.GameComponent +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GameScreen @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, +) : BaseScreen(applicationContextRepository) { + + private var gameComponent: GameComponent? = null + + private fun getGameContext(isLoadGame: Boolean): GameContext = GameContext( + isLoadGame = isLoadGame, + showInfo = false, + showMap = false, + joystick = Joystick(Player.SPEED), + cameraContext = CameraContext( + viewport = Rectangle( + /* x = */ 0f, + /* y = */ 0f, + /* width = */ applicationContextRepository.getWidth(), + /* height = */ applicationContextRepository.getHeight(), + ), + camera = OrthographicCamera().apply { + setToOrtho( + /* yDown = */ true, + /* viewportWidth = */ applicationContextRepository.getWidth(), + /* viewportHeight = */ applicationContextRepository.getHeight(), + ) + }, + ), + ) + + private fun getGameComponent(isLoadGame: Boolean): GameComponent { + val gameContext = getGameContext(isLoadGame) + + return DaggerGameComponent.builder() + .applicationComponent((Gdx.app.applicationListener as CaveDroidApplication).applicationComponent) + .gameContext(gameContext) + .build() + } + + private fun resetGameComponent() { + gameComponent?.gameProc?.dispose() + gameComponent = null + } + + fun newGame(gameMode: Int) { + resetGameComponent() + gameComponent = getGameComponent(false).apply { + gameProc.setPlayerGameMode(gameMode) + } + } + + fun loadGame() { + resetGameComponent() + gameComponent = getGameComponent(true) + } + + override fun show() { + val proc = requireNotNull(gameComponent?.gameProc) { + "GameScreen#show: gameComponent was not set before show" + } + proc.show() + } + + override fun render(delta: Float) { + val proc = requireNotNull(gameComponent?.gameProc) { + "GameScreen#render: gameComponent was not set before render" + } + proc.update(delta) + } + + override fun resize(width: Int, height: Int) { + super.resize(width, height) + + gameComponent?.gameContextRepository?.getCameraContext()?.let { cameraContext -> + cameraContext.viewport.apply { + setWidth(applicationContextRepository.getWidth()) + setHeight(applicationContextRepository.getHeight()) + } + cameraContext.camera.setToOrtho( + /* yDown = */ true, + /* viewportWidth = */ applicationContextRepository.getWidth(), + /* viewportHeight = */ applicationContextRepository.getHeight(), + ) + } + + gameComponent?.gameProc?.onResize() + } + + override fun pause() { + } + + override fun resume() { + } + + override fun hide() { + } + + override fun dispose() { + resetGameComponent() + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/di/GameComponent.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/di/GameComponent.kt new file mode 100644 index 0000000..e57388c --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/di/GameComponent.kt @@ -0,0 +1,58 @@ +package ru.fredboy.cavedroid.zygote.game.di + +import dagger.BindsInstance +import dagger.Component +import ru.deadsoftware.cavedroid.generated.module.KeyboardInputHandlersModule +import ru.deadsoftware.cavedroid.generated.module.MouseInputHandlersModule +import ru.deadsoftware.cavedroid.generated.module.PlaceBlockActionsModule +import ru.deadsoftware.cavedroid.generated.module.RenderModule +import ru.deadsoftware.cavedroid.generated.module.UpdateBlockActionsModule +import ru.deadsoftware.cavedroid.generated.module.UseBlockActionsModule +import ru.deadsoftware.cavedroid.generated.module.UseItemActionsModule +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.data.configuration.di.GameContextModule +import ru.fredboy.cavedroid.data.configuration.model.GameContext +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import ru.fredboy.cavedroid.game.controller.container.di.ControllerContainerModule +import ru.fredboy.cavedroid.game.controller.drop.di.ControllerDropModule +import ru.fredboy.cavedroid.game.controller.mob.di.MobControllerModule +import ru.fredboy.cavedroid.game.world.di.GameWorldModule +import ru.fredboy.cavedroid.zygote.di.ApplicationComponent +import ru.fredboy.cavedroid.zygote.game.GameProc + +@GameScope +@Component( + dependencies = [ ApplicationComponent::class ], + modules = [ + GameModule::class, + UseItemActionsModule::class, + UpdateBlockActionsModule::class, + PlaceBlockActionsModule::class, + RenderModule::class, + KeyboardInputHandlersModule::class, + MouseInputHandlersModule::class, + UseBlockActionsModule::class, + GameWorldModule::class, + ControllerContainerModule::class, + ControllerDropModule::class, + GameContextModule::class, + MobControllerModule::class, + ], +) +interface GameComponent { + + val gameProc: GameProc + + val gameContextRepository: GameContextRepository + + @Component.Builder + interface Builder { + + fun applicationComponent(component: ApplicationComponent): Builder + + @BindsInstance + fun gameContext(context: GameContext): Builder + + fun build(): GameComponent + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/di/GameModule.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/di/GameModule.kt new file mode 100644 index 0000000..ea9a561 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/game/di/GameModule.kt @@ -0,0 +1,116 @@ +package ru.fredboy.cavedroid.zygote.game.di + +import dagger.Module +import dagger.Provides +import ru.fredboy.cavedroid.common.di.GameScope +import ru.fredboy.cavedroid.domain.assets.repository.MobAssetsRepository +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.configuration.repository.GameContextRepository +import ru.fredboy.cavedroid.domain.items.repository.ItemsRepository +import ru.fredboy.cavedroid.domain.items.usecase.GetFallbackItemUseCase +import ru.fredboy.cavedroid.domain.items.usecase.GetItemByKeyUseCase +import ru.fredboy.cavedroid.domain.save.repository.SaveDataRepository +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerFactory +import ru.fredboy.cavedroid.entity.container.abstraction.ContainerWorldAdapter +import ru.fredboy.cavedroid.entity.drop.abstraction.DropAdapter +import ru.fredboy.cavedroid.entity.drop.abstraction.DropWorldAdapter +import ru.fredboy.cavedroid.entity.mob.abstraction.MobWorldAdapter +import ru.fredboy.cavedroid.game.controller.container.ContainerController +import ru.fredboy.cavedroid.game.controller.drop.DropController +import ru.fredboy.cavedroid.game.controller.mob.MobController +import ru.fredboy.cavedroid.game.world.GameWorld + +@Module +object GameModule { + + @Provides + @GameScope + fun provideDropController( + applicationContextRepository: ApplicationContextRepository, + gameContextRepository: GameContextRepository, + saveDataRepository: SaveDataRepository, + itemsRepository: ItemsRepository, + dropWorldAdapter: DropWorldAdapter, + ): DropController = if (gameContextRepository.isLoadGame()) { + saveDataRepository.loadDropController( + gameDataFolder = applicationContextRepository.getGameDirectory(), + dropWorldAdapter = dropWorldAdapter, + ) + } else { + DropController( + itemsRepository = itemsRepository, + dropWorldAdapter = dropWorldAdapter, + ) + } + + @Provides + @GameScope + fun provideContainerController( + applicationContextRepository: ApplicationContextRepository, + gameContextRepository: GameContextRepository, + saveDataRepository: SaveDataRepository, + getItemByKeyUseCase: GetItemByKeyUseCase, + containerWorldAdapter: ContainerWorldAdapter, + containerFactory: ContainerFactory, + dropAdapter: DropAdapter, + ): ContainerController = if (gameContextRepository.isLoadGame()) { + saveDataRepository.loadContainerController( + gameDataFolder = applicationContextRepository.getGameDirectory(), + containerWorldAdapter = containerWorldAdapter, + containerFactory = containerFactory, + dropAdapter = dropAdapter, + ) + } else { + ContainerController( + getItemByKeyUseCase = getItemByKeyUseCase, + containerWorldAdapter = containerWorldAdapter, + containerFactory = containerFactory, + dropAdapter = dropAdapter, + ) + } + + @Provides + @GameScope + fun provideMobController( + applicationContextRepository: ApplicationContextRepository, + gameContextRepository: GameContextRepository, + saveDataRepository: SaveDataRepository, + mobAssetsRepository: MobAssetsRepository, + getFallbackItemUseCase: GetFallbackItemUseCase, + mobWorldAdapter: MobWorldAdapter, + ): MobController = if (gameContextRepository.isLoadGame()) { + saveDataRepository.loadMobController( + gameDataFolder = applicationContextRepository.getGameDirectory(), + mobWorldAdapter = mobWorldAdapter, + ) + } else { + MobController( + mobAssetsRepository = mobAssetsRepository, + getFallbackItemUseCase = getFallbackItemUseCase, + mobWorldAdapter = mobWorldAdapter, + ) + } + + @Provides + @GameScope + fun provideGameWorld( + applicationContextRepository: ApplicationContextRepository, + gameContextRepository: GameContextRepository, + saveDataRepository: SaveDataRepository, + itemsRepository: ItemsRepository, + ): GameWorld { + val mapData = if (gameContextRepository.isLoadGame()) { + saveDataRepository.loadMap( + gameDataFolder = applicationContextRepository.getGameDirectory(), + ) + } else { + null + } + + return GameWorld( + itemsRepository = itemsRepository, + initialForeMap = mapData?.retrieveForeMap(), + initialBackMap = mapData?.retrieveBackMap(), + ) + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/MenuScreen.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/MenuScreen.kt new file mode 100644 index 0000000..a181f54 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/MenuScreen.kt @@ -0,0 +1,67 @@ +package ru.fredboy.cavedroid.zygote.menu + +import com.badlogic.gdx.Gdx +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository +import ru.fredboy.cavedroid.zygote.CaveDroidApplication +import ru.fredboy.cavedroid.zygote.base.BaseScreen +import ru.fredboy.cavedroid.zygote.menu.di.DaggerMenuComponent +import ru.fredboy.cavedroid.zygote.menu.di.MenuComponent +import ru.fredboy.cavedroid.zygote.menu.input.MenuInputProcessor +import ru.fredboy.cavedroid.zygote.menu.renderer.MenuRenderer +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MenuScreen @Inject constructor( + applicationContextRepository: ApplicationContextRepository, +) : BaseScreen(applicationContextRepository) { + + private val menuRenderer: MenuRenderer + + private val menuInputProcessor: MenuInputProcessor + + private val menuButtonRepository: MenuButtonRepository + + init { + val menuComponent: MenuComponent = DaggerMenuComponent.builder() + .applicationComponent((Gdx.app.applicationListener as CaveDroidApplication).applicationComponent) + .build() + + menuRenderer = menuComponent.menuRenderer + menuInputProcessor = menuComponent.menuInputProcessor + menuButtonRepository = menuComponent.menuButtonRepository + } + + fun resetMenu() { + menuButtonRepository.setCurrentMenu("main") + } + + override fun show() { + resetMenu() + Gdx.input.inputProcessor = menuInputProcessor + } + + override fun render(delta: Float) { + menuRenderer.render(delta) + } + + override fun resize(width: Int, height: Int) { + super.resize(width, height) + menuRenderer.onResize() + } + + override fun pause() { + } + + override fun resume() { + } + + override fun hide() { + } + + override fun dispose() { + menuRenderer.dispose() + menuButtonRepository.dispose() + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/ExitGameAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/ExitGameAction.kt new file mode 100644 index 0000000..f20dbb2 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/ExitGameAction.kt @@ -0,0 +1,21 @@ +package ru.fredboy.cavedroid.zygote.menu.action + +import ru.fredboy.cavedroid.common.api.ApplicationController +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.zygote.menu.action.annotation.BindsMenuAction +import javax.inject.Inject + +@MenuScope +@BindsMenuAction(stringKey = ExitGameAction.KEY) +class ExitGameAction @Inject constructor( + private val gameController: ApplicationController, +) : IMenuAction { + + override fun perform() { + gameController.exitGame() + } + + companion object { + const val KEY = "exit_game_action" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/IMenuAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/IMenuAction.kt new file mode 100644 index 0000000..6f5d92e --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/IMenuAction.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.zygote.menu.action + +interface IMenuAction { + + fun perform() + + fun canPerform(): Boolean = true +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/LoadGameAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/LoadGameAction.kt new file mode 100644 index 0000000..2105214 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/LoadGameAction.kt @@ -0,0 +1,27 @@ +package ru.fredboy.cavedroid.zygote.menu.action + +import ru.fredboy.cavedroid.common.api.ApplicationController +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.save.repository.SaveDataRepository +import ru.fredboy.cavedroid.zygote.menu.action.annotation.BindsMenuAction +import javax.inject.Inject + +@MenuScope +@BindsMenuAction(stringKey = LoadGameAction.KEY) +class LoadGameAction @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val gameController: ApplicationController, + private val saveDataRepository: SaveDataRepository, +) : IMenuAction { + + override fun perform() { + gameController.loadGame() + } + + override fun canPerform(): Boolean = saveDataRepository.exists(applicationContextRepository.getGameDirectory()) + + companion object { + const val KEY = "load_game_action" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/NewGameCreativeAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/NewGameCreativeAction.kt new file mode 100644 index 0000000..01f8e2e --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/NewGameCreativeAction.kt @@ -0,0 +1,21 @@ +package ru.fredboy.cavedroid.zygote.menu.action + +import ru.fredboy.cavedroid.common.api.ApplicationController +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.zygote.menu.action.annotation.BindsMenuAction +import javax.inject.Inject + +@MenuScope +@BindsMenuAction(stringKey = NewGameCreativeAction.KEY) +class NewGameCreativeAction @Inject constructor( + private val gameController: ApplicationController, +) : IMenuAction { + + override fun perform() { + gameController.newGameCreative() + } + + companion object { + const val KEY = "new_game_creative_action" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/NewGameSurvivalAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/NewGameSurvivalAction.kt new file mode 100644 index 0000000..079077e --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/NewGameSurvivalAction.kt @@ -0,0 +1,21 @@ +package ru.fredboy.cavedroid.zygote.menu.action + +import ru.fredboy.cavedroid.common.api.ApplicationController +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.zygote.menu.action.annotation.BindsMenuAction +import javax.inject.Inject + +@MenuScope +@BindsMenuAction(stringKey = NewGameSurvivalAction.KEY) +class NewGameSurvivalAction @Inject constructor( + private val gameController: ApplicationController, +) : IMenuAction { + + override fun perform() { + gameController.newGameSurvival() + } + + companion object { + const val KEY = "new_game_survival_action" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenMainMenuAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenMainMenuAction.kt new file mode 100644 index 0000000..db42b0c --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenMainMenuAction.kt @@ -0,0 +1,21 @@ +package ru.fredboy.cavedroid.zygote.menu.action + +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository +import ru.fredboy.cavedroid.zygote.menu.action.annotation.BindsMenuAction +import javax.inject.Inject + +@MenuScope +@BindsMenuAction(OpenMainMenuAction.KEY) +class OpenMainMenuAction @Inject constructor( + private val menuButtonRepository: MenuButtonRepository, +) : IMenuAction { + + override fun perform() { + menuButtonRepository.setCurrentMenu("main") + } + + companion object { + const val KEY = "menu_main_action" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenNewGameMenuAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenNewGameMenuAction.kt new file mode 100644 index 0000000..cc0724f --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenNewGameMenuAction.kt @@ -0,0 +1,21 @@ +package ru.fredboy.cavedroid.zygote.menu.action + +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository +import ru.fredboy.cavedroid.zygote.menu.action.annotation.BindsMenuAction +import javax.inject.Inject + +@MenuScope +@BindsMenuAction(OpenNewGameMenuAction.KEY) +class OpenNewGameMenuAction @Inject constructor( + private val menuButtonRepository: MenuButtonRepository, +) : IMenuAction { + + override fun perform() { + menuButtonRepository.setCurrentMenu("new_game") + } + + companion object { + const val KEY = "new_game_action" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenOptionsMenuAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenOptionsMenuAction.kt new file mode 100644 index 0000000..2247f4b --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/OpenOptionsMenuAction.kt @@ -0,0 +1,21 @@ +package ru.fredboy.cavedroid.zygote.menu.action + +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository +import ru.fredboy.cavedroid.zygote.menu.action.annotation.BindsMenuAction +import javax.inject.Inject + +@MenuScope +@BindsMenuAction(OpenOptionsMenuAction.KEY) +class OpenOptionsMenuAction @Inject constructor( + private val menuButtonRepository: MenuButtonRepository, +) : IMenuAction { + + override fun perform() { + menuButtonRepository.setCurrentMenu("options") + } + + companion object { + const val KEY = "options_action" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/annotation/BindsMenuAction.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/annotation/BindsMenuAction.kt new file mode 100644 index 0000000..1a21e85 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/action/annotation/BindsMenuAction.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.zygote.menu.action.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.zygote.menu.action.IMenuAction + +@BindsIntoMapStringKey( + interfaceClass = IMenuAction::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "MenuActionsModule", +) +annotation class BindsMenuAction(val stringKey: String) diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/di/MenuComponent.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/di/MenuComponent.kt new file mode 100644 index 0000000..e556010 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/di/MenuComponent.kt @@ -0,0 +1,39 @@ +package ru.fredboy.cavedroid.zygote.menu.di + +import dagger.Component +import ru.deadsoftware.cavedroid.generated.module.MenuActionsModule +import ru.deadsoftware.cavedroid.generated.module.MenuBooleanOptionsModule +import ru.deadsoftware.cavedroid.generated.module.MenuNumericalOptionsModule +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.data.menu.di.DataMenuModule +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository +import ru.fredboy.cavedroid.zygote.di.ApplicationComponent +import ru.fredboy.cavedroid.zygote.menu.input.MenuInputProcessor +import ru.fredboy.cavedroid.zygote.menu.renderer.MenuRenderer + +@MenuScope +@Component( + dependencies = [ ApplicationComponent::class ], + modules = [ + DataMenuModule::class, + MenuBooleanOptionsModule::class, + MenuActionsModule::class, + MenuNumericalOptionsModule::class, + ], +) +interface MenuComponent { + + val menuRenderer: MenuRenderer + + val menuInputProcessor: MenuInputProcessor + + val menuButtonRepository: MenuButtonRepository + + @Component.Builder + interface Builder { + + fun applicationComponent(impl: ApplicationComponent): Builder + + fun build(): MenuComponent + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/input/MenuInputProcessor.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/input/MenuInputProcessor.kt new file mode 100644 index 0000000..1e30666 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/input/MenuInputProcessor.kt @@ -0,0 +1,112 @@ +package ru.fredboy.cavedroid.zygote.menu.input + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.InputProcessor +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.menu.model.MenuButton +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository +import ru.fredboy.cavedroid.zygote.menu.action.IMenuAction +import ru.fredboy.cavedroid.zygote.menu.option.bool.IMenuBooleanOption +import ru.fredboy.cavedroid.zygote.menu.option.numerical.IMenuNumericalOption +import javax.inject.Inject + +@MenuScope +class MenuInputProcessor @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val menuButtonRepository: MenuButtonRepository, + private val menuButtonActions: Map, + private val menuButtonBooleanOption: Map, + private val buttonNumericalOptions: Map, +) : InputProcessor { + + override fun touchUp( + screenX: Int, + screenY: Int, + pointer: Int, + button: Int, + ): Boolean { + val touchX = applicationContextRepository.getWidth() / Gdx.graphics.width * screenX.toFloat() + val touchY = applicationContextRepository.getHeight() / Gdx.graphics.height * screenY.toFloat() + + menuButtonRepository.getCurrentMenuButtons()?.values?.forEachIndexed { index, menuButton -> + if (!menuButton.isEnabled) { + return@forEachIndexed + } + + // TODO: Fix magic numbers + val rect = Rectangle( + /* x = */ applicationContextRepository.getWidth() / 2 - 100, + /* y = */ applicationContextRepository.getHeight() / 4 + index * 30, + /* width = */ 200f, + /* height = */ 20f, + ) + + if (rect.contains(touchX, touchY)) { + when (menuButton) { + is MenuButton.Simple -> { + val action = menuButtonActions[menuButton.actionKey] ?: run { + Gdx.app.error(TAG, "Menu handler for action '${menuButton.actionKey}' not found") + return@forEachIndexed + } + + if (action.canPerform()) { + action.perform() + } else { + Gdx.app.debug(TAG, "Can't perform action ${menuButton.actionKey}") + } + } + + is MenuButton.BooleanOption -> { + menuButton.optionKeys.forEach { optionKey -> + menuButtonBooleanOption[optionKey]?.toggleOption() ?: run { + Gdx.app.error(TAG, "Menu option handler for option '$optionKey' not found") + } + } + } + + is MenuButton.NumericalOption -> { + menuButton.optionKeys.forEach { optionKey -> + buttonNumericalOptions[optionKey]?.setNextOption() ?: run { + Gdx.app.error(TAG, "Menu option handler for option '$optionKey' not found") + } + } + } + } + } + } + + return true + } + + override fun keyDown(keycode: Int): Boolean = false + + override fun keyUp(keycode: Int): Boolean = false + + override fun keyTyped(character: Char): Boolean = false + + override fun touchDown( + screenX: Int, + screenY: Int, + pointer: Int, + button: Int, + ): Boolean = false + + override fun touchCancelled( + screenX: Int, + screenY: Int, + pointer: Int, + button: Int, + ): Boolean = false + + override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean = false + + override fun mouseMoved(screenX: Int, screenY: Int): Boolean = false + + override fun scrolled(amountX: Float, amountY: Float): Boolean = false + + companion object { + private const val TAG = "MenuInputProcessor" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/annotation/BindsMenuBooleanOption.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/annotation/BindsMenuBooleanOption.kt new file mode 100644 index 0000000..685caa3 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/annotation/BindsMenuBooleanOption.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.zygote.menu.option.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.zygote.menu.option.bool.IMenuBooleanOption + +@BindsIntoMapStringKey( + interfaceClass = IMenuBooleanOption::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "MenuBooleanOptionsModule", +) +annotation class BindsMenuBooleanOption(val stringKey: String) diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/annotation/BindsMenuNumericalOption.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/annotation/BindsMenuNumericalOption.kt new file mode 100644 index 0000000..bd8db8f --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/annotation/BindsMenuNumericalOption.kt @@ -0,0 +1,12 @@ +package ru.fredboy.cavedroid.zygote.menu.option.annotation + +import ru.fredboy.automultibind.annotations.BindsIntoMapStringKey +import ru.fredboy.cavedroid.common.automultibind.MultibindingConfig +import ru.fredboy.cavedroid.zygote.menu.option.numerical.IMenuNumericalOption + +@BindsIntoMapStringKey( + interfaceClass = IMenuNumericalOption::class, + modulePackage = MultibindingConfig.GENERATED_MODULES_PACKAGE, + moduleName = "MenuNumericalOptionsModule", +) +annotation class BindsMenuNumericalOption(val stringKey: String) diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/DynamicCameraMenuBooleanOption.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/DynamicCameraMenuBooleanOption.kt new file mode 100644 index 0000000..be63889 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/DynamicCameraMenuBooleanOption.kt @@ -0,0 +1,23 @@ +package ru.fredboy.cavedroid.zygote.menu.option.bool + +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.zygote.menu.option.annotation.BindsMenuBooleanOption +import javax.inject.Inject + +@MenuScope +@BindsMenuBooleanOption(DynamicCameraMenuBooleanOption.KEY) +class DynamicCameraMenuBooleanOption @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, +) : IMenuBooleanOption { + + override fun getOption(): Boolean = applicationContextRepository.useDynamicCamera() + + override fun toggleOption() { + applicationContextRepository.setUseDynamicCamera(!getOption()) + } + + companion object { + const val KEY = "dyncam" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/FullscreenMenuBooleanOption.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/FullscreenMenuBooleanOption.kt new file mode 100644 index 0000000..6a984f7 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/FullscreenMenuBooleanOption.kt @@ -0,0 +1,23 @@ +package ru.fredboy.cavedroid.zygote.menu.option.bool + +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.zygote.menu.option.annotation.BindsMenuBooleanOption +import javax.inject.Inject + +@MenuScope +@BindsMenuBooleanOption(FullscreenMenuBooleanOption.KEY) +class FullscreenMenuBooleanOption @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, +) : IMenuBooleanOption { + + override fun getOption(): Boolean = applicationContextRepository.isFullscreen() + + override fun toggleOption() { + applicationContextRepository.setFullscreen(!getOption()) + } + + companion object { + const val KEY = "fullscreen" + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/IMenuBooleanOption.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/IMenuBooleanOption.kt new file mode 100644 index 0000000..f9f72eb --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/bool/IMenuBooleanOption.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.zygote.menu.option.bool + +interface IMenuBooleanOption { + + fun getOption(): Boolean + + fun toggleOption() +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/numerical/IMenuNumericalOption.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/numerical/IMenuNumericalOption.kt new file mode 100644 index 0000000..ae08ac5 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/numerical/IMenuNumericalOption.kt @@ -0,0 +1,8 @@ +package ru.fredboy.cavedroid.zygote.menu.option.numerical + +interface IMenuNumericalOption { + + fun getOption(): Number + + fun setNextOption() +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/numerical/ScreenScaleMenuNumericalOption.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/numerical/ScreenScaleMenuNumericalOption.kt new file mode 100644 index 0000000..f49e454 --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/option/numerical/ScreenScaleMenuNumericalOption.kt @@ -0,0 +1,30 @@ +package ru.fredboy.cavedroid.zygote.menu.option.numerical + +import ru.fredboy.cavedroid.common.api.ApplicationController +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.zygote.menu.option.annotation.BindsMenuNumericalOption +import javax.inject.Inject + +@MenuScope +@BindsMenuNumericalOption(ScreenScaleMenuNumericalOption.KEY) +class ScreenScaleMenuNumericalOption @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val applicationController: ApplicationController, +) : IMenuNumericalOption { + + override fun getOption(): Number { + return applicationContextRepository.getScreenScale() + } + + override fun setNextOption() { + val nextIndex = (SCALE_VALUES.indexOf(getOption()) + 1) % SCALE_VALUES.size + val nextValue = SCALE_VALUES[nextIndex] + applicationContextRepository.setScreenScale(nextValue) + applicationController.triggerResize() + } + companion object { + const val KEY = "screen_scale" + private val SCALE_VALUES = arrayOf(1, 2, 3, 4, 5, 6) + } +} diff --git a/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/renderer/MenuRenderer.kt b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/renderer/MenuRenderer.kt new file mode 100644 index 0000000..2433c9d --- /dev/null +++ b/core/zygote/src/main/kotlin/ru/fredboy/cavedroid/zygote/menu/renderer/MenuRenderer.kt @@ -0,0 +1,167 @@ +package ru.fredboy.cavedroid.zygote.menu.renderer + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.OrthographicCamera +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.math.Rectangle +import ru.fredboy.cavedroid.common.CaveDroidConstants +import ru.fredboy.cavedroid.common.di.MenuScope +import ru.fredboy.cavedroid.common.utils.drawString +import ru.fredboy.cavedroid.domain.assets.usecase.GetFontUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringHeightUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetStringWidthUseCase +import ru.fredboy.cavedroid.domain.assets.usecase.GetTextureRegionByNameUseCase +import ru.fredboy.cavedroid.domain.configuration.repository.ApplicationContextRepository +import ru.fredboy.cavedroid.domain.menu.model.MenuButton +import ru.fredboy.cavedroid.domain.menu.repository.MenuButtonRepository +import ru.fredboy.cavedroid.zygote.menu.action.IMenuAction +import ru.fredboy.cavedroid.zygote.menu.option.bool.IMenuBooleanOption +import ru.fredboy.cavedroid.zygote.menu.option.numerical.IMenuNumericalOption +import javax.inject.Inject + +@MenuScope +class MenuRenderer @Inject constructor( + private val applicationContextRepository: ApplicationContextRepository, + private val menuButtonRepository: MenuButtonRepository, + private val getTextureRegionByName: GetTextureRegionByNameUseCase, + private val menuButtonActions: Map, + private val buttonBooleanOptions: Map, + private val buttonNumericalOptions: Map, + private val getFont: GetFontUseCase, + private val getStringWidth: GetStringWidthUseCase, + private val getStringHeight: GetStringHeightUseCase, +) { + + private val camera = OrthographicCamera() + + private val spriter = SpriteBatch() + + init { + camera.setToOrtho( + /* yDown = */ true, + /* viewportWidth = */ applicationContextRepository.getWidth(), + /* viewportHeight = */ applicationContextRepository.getHeight(), + ) + + spriter.projectionMatrix = camera.combined + } + + private fun getButtonTextureRegionKey(button: MenuButton): String { + if (button is MenuButton.Simple && menuButtonActions[button.actionKey]?.canPerform() != true) { + return KEY_BUTTON_DISABLED_TEXTURE + } + + return KEY_BUTTON_ENABLED_TEXTURE.takeIf { button.isEnabled } ?: KEY_BUTTON_DISABLED_TEXTURE + } + + private fun renderButton(button: MenuButton, position: Int) { + val textureRegion = getTextureRegionByName[getButtonTextureRegionKey(button)] ?: run { + Gdx.app.error(TAG, "Couldn't render button because region not found") + return + } + + val label = when (button) { + is MenuButton.Simple -> button.label + is MenuButton.BooleanOption -> String.format( + button.label, + *button.optionKeys.map { key -> buttonBooleanOptions[key]?.getOption().toString() }.toTypedArray(), + ) + is MenuButton.NumericalOption -> String.format( + button.label, + *button.optionKeys.mapNotNull { key -> buttonNumericalOptions[key]?.getOption() }.toTypedArray(), + ) + } + + val buttonX = applicationContextRepository.getWidth() / 2 - textureRegion.regionWidth / 2 + val buttonY = applicationContextRepository.getHeight() / 4 + position.toFloat() * 30 + + val buttonRect = Rectangle( + /* x = */ buttonX, + /* y = */ buttonY, + /* width = */ textureRegion.regionWidth.toFloat(), + /* height = */ textureRegion.regionHeight.toFloat(), + ) + + val inputX = applicationContextRepository.getWidth() / Gdx.graphics.width * Gdx.input.x.toFloat() + val inputY = applicationContextRepository.getHeight() / Gdx.graphics.height * Gdx.input.y.toFloat() + + spriter.draw( + if (button.isEnabled && buttonRect.contains(inputX, inputY)) { + getTextureRegionByName[KEY_BUTTON_SELECTED_TEXTURE] ?: textureRegion + } else { + textureRegion + }, + /* x = */ buttonX, + /* y = */ buttonY, + ) + + spriter.drawString( + font = getFont(), + str = label, + x = buttonX + textureRegion.regionWidth / 2 - getStringWidth(label) / 2, + y = buttonY + textureRegion.regionHeight / 2 - getStringHeight(label), + ) + } + + private fun drawBackground() { + val backgroundRegion = getTextureRegionByName["background"] ?: return + val gameLogo = getTextureRegionByName["gamelogo"] ?: return + + val backgroundRegionWidth = backgroundRegion.regionWidth + val backgroundRegionHeight = backgroundRegion.regionWidth + + for (x in 0..applicationContextRepository.getWidth().toInt() / backgroundRegionWidth) { + for (y in 0..applicationContextRepository.getHeight().toInt() / backgroundRegionHeight) { + spriter.draw( + /* region = */ backgroundRegion, + /* x = */ x * backgroundRegionWidth.toFloat(), + /* y = */ y * backgroundRegionHeight.toFloat(), + ) + } + } + + spriter.draw( + /* region = */ gameLogo, + /* x = */ applicationContextRepository.getWidth() / 2 - gameLogo.regionWidth.toFloat() / 2, + /* y = */ 8f, + ) + } + + fun onResize() { + camera.setToOrtho( + /* yDown = */ true, + /* viewportWidth = */ applicationContextRepository.getWidth(), + /* viewportHeight = */ applicationContextRepository.getHeight(), + ) + spriter.projectionMatrix = camera.combined + } + + @Suppress("unused") + fun render(delta: Float) { + spriter.begin() + drawBackground() + + menuButtonRepository.getCurrentMenuButtons()?.values + ?.forEachIndexed { position, button -> renderButton(button, position) } + + spriter.drawString( + font = getFont(), + str = "CaveDroid " + CaveDroidConstants.VERSION, + x = 0f, + y = applicationContextRepository.getHeight() - getStringHeight("CaveDroid " + CaveDroidConstants.VERSION) * 1.5f, + ) + spriter.end() + } + + fun dispose() { + spriter.dispose() + } + + companion object { + private const val TAG = "MenuRenderer" + + private const val KEY_BUTTON_SELECTED_TEXTURE = "button_2" + private const val KEY_BUTTON_ENABLED_TEXTURE = "button_1" + private const val KEY_BUTTON_DISABLED_TEXTURE = "button_0" + } +} diff --git a/desktop/build.gradle b/desktop/build.gradle deleted file mode 100644 index b3a6693..0000000 --- a/desktop/build.gradle +++ /dev/null @@ -1,47 +0,0 @@ -apply plugin: "java" - -sourceCompatibility = 1.8 -sourceSets.main.java.srcDirs = [ "src/" ] -sourceSets.main.resources.srcDirs = ["../android/assets"] - -project.ext.mainClassName = "ru.deadsoftware.cavedroid.desktop.DesktopLauncher" -project.ext.assetsDir = new File("../android/assets") - -task run(dependsOn: classes, type: JavaExec) { - main = project.mainClassName - classpath = sourceSets.main.runtimeClasspath - standardInput = System.in - workingDir = project.assetsDir - ignoreExitValue = true as JavaExecSpec -} - -task runTouch(dependsOn: classes, type: JavaExec) { - main = project.mainClassName - classpath = sourceSets.main.runtimeClasspath - standardInput = System.in - workingDir = project.assetsDir - ignoreExitValue = true as JavaExecSpec - args "--touch" -} - -task debug(dependsOn: classes, type: JavaExec) { - main = project.mainClassName - classpath = sourceSets.main.runtimeClasspath - standardInput = System.in - workingDir = project.assetsDir - ignoreExitValue = true as JavaExecSpec - debug = true -} - -task dist(type: Jar) { - manifest { - attributes 'Main-Class': project.mainClassName - } - from { - configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } - } - with jar -} - - -dist.dependsOn classes \ No newline at end of file diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts new file mode 100644 index 0000000..32a59d1 --- /dev/null +++ b/desktop/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + kotlin +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +private val desktopLauncherClassName = "ru.deadsoftware.cavedroid.desktop.DesktopLauncher" + +tasks.register("run") { + dependsOn("build") + mainClass = desktopLauncherClassName + classpath = sourceSets["main"].runtimeClasspath + workingDir = sourceSets["main"].resources.sourceDirectories.first() + args("--debug") +} + +tasks.register("runTouch") { + dependsOn("build") + mainClass = desktopLauncherClassName + classpath = sourceSets["main"].runtimeClasspath + workingDir = sourceSets["main"].resources.sourceDirectories.first() + args("--touch", "--debug") +} + +tasks.register("dist") { + dependsOn("build") + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + manifest { + attributes["Main-Class"] = desktopLauncherClassName + } + from( + files( + configurations.runtimeClasspath.map { classpath -> + classpath.map { file -> + file.takeIf(File::isDirectory) ?: zipTree(file) + } + }, + ), + ) + with(tasks.jar.get()) +} + +dependencies { + useCommonModule() + useZygoteModule() + + implementation(Dependencies.LibGDX.gdx) + implementation(Dependencies.LibGDX.Desktop.backend) + implementation(Dependencies.LibGDX.Desktop.natives) +} diff --git a/desktop/gradle/wrapper/gradle-wrapper.properties b/desktop/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d884b3f --- /dev/null +++ b/desktop/gradle/wrapper/gradle-wrapper.properties @@ -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/main/kotlin/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.kt b/desktop/src/main/kotlin/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.kt new file mode 100644 index 0000000..3d18042 --- /dev/null +++ b/desktop/src/main/kotlin/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.kt @@ -0,0 +1,56 @@ +package ru.deadsoftware.cavedroid.desktop + +import com.badlogic.gdx.Files +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration +import ru.fredboy.cavedroid.zygote.CaveDroidApplication + +internal object DesktopLauncher { + + @JvmStatic + fun main(arg: Array) { + val config = Lwjgl3ApplicationConfiguration() + + with(config) { + setWindowIcon( + /* fileType = */ Files.FileType.Internal, + "icons/icon512.png", + "icons/icon256.png", + "icons/icon128.png", + ) + setTitle("CaveDroid") + setWindowedMode(960, 540) + useVsync(true) + } + + var touch = false + var debug = false + var assetsPath: String? = null + + for (anArg in arg) { + if (anArg == "--touch") { + touch = true + } + + if (anArg == "--debug") { + debug = true + } + + if (anArg.startsWith("--assets")) { + val splitArg: Array = anArg.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + if (splitArg.size >= 2) { + assetsPath = splitArg[1] + } + } + } + + val caveGame = CaveDroidApplication( + gameDataDirectoryPath = System.getProperty("user.home") + "/.cavedroid", + isTouchScreen = touch, + isDebug = debug, + preferencesStore = DesktopPreferencesStore(), + ) + + Lwjgl3Application(caveGame, config) + } +} diff --git a/desktop/src/main/kotlin/ru/deadsoftware/cavedroid/desktop/DesktopPreferencesStore.kt b/desktop/src/main/kotlin/ru/deadsoftware/cavedroid/desktop/DesktopPreferencesStore.kt new file mode 100644 index 0000000..e34b735 --- /dev/null +++ b/desktop/src/main/kotlin/ru/deadsoftware/cavedroid/desktop/DesktopPreferencesStore.kt @@ -0,0 +1,15 @@ +package ru.deadsoftware.cavedroid.desktop + +import ru.fredboy.cavedroid.common.api.PreferencesStore +import java.util.prefs.Preferences + +class DesktopPreferencesStore : PreferencesStore { + + private val prefs = Preferences.userNodeForPackage(DesktopPreferencesStore::class.java) + + override fun getPreference(key: String): String? = prefs.get(key, null) + + override fun setPreference(key: String, value: String?) { + prefs.put(key, value) + } +} diff --git a/desktop/src/main/resources b/desktop/src/main/resources new file mode 120000 index 0000000..2978ef3 --- /dev/null +++ b/desktop/src/main/resources @@ -0,0 +1 @@ +../../../assets \ No newline at end of file diff --git a/desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.java b/desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.java deleted file mode 100644 index 2e9ae42..0000000 --- a/desktop/src/ru/deadsoftware/cavedroid/desktop/DesktopLauncher.java +++ /dev/null @@ -1,25 +0,0 @@ -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 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); - } -} diff --git a/gen-changelog.sh b/gen-changelog.sh new file mode 100755 index 0000000..397e28d --- /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 '' + +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 '' diff --git a/gradle.properties b/gradle.properties index 339fa15..dc8c2b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,5 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms128m -Xmx1500m org.gradle.configureondemand=true +android.useAndroidX=true +ksp.incremental=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3745855..b349727 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #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 diff --git a/gradlew b/gradlew index 4453cce..54f115a 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,22 @@ #!/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 f955316..c42c579 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@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/ios/build.gradle.kts b/ios/build.gradle.kts new file mode 100644 index 0000000..2ab79ef --- /dev/null +++ b/ios/build.gradle.kts @@ -0,0 +1,86 @@ +plugins { + kotlin + id("robovm") +} + +java.sourceCompatibility = ApplicationInfo.sourceCompatibility +java.targetCompatibility = ApplicationInfo.sourceCompatibility + +dependencies { + useCommonModule() + useZygoteModule() + + implementation(Dependencies.LibGDX.gdx) + implementation(Dependencies.RoboVM.rt) + implementation(Dependencies.RoboVM.cocoatouch) + implementation(Dependencies.LibGDX.Ios.backend) + implementation(Dependencies.LibGDX.Ios.natives) +} + +val generatePlist by tasks.registering { + doLast { + val outputPlist = file("$buildDir/generated/Info.plist.xml") + outputPlist.parentFile.mkdirs() + outputPlist.writeText( + """ + + + + + CFBundleDevelopmentRegion + ru + CFBundleDisplayName + ${ApplicationInfo.name} + CFBundleExecutable + IOSLauncher + CFBundleIdentifier + ${ApplicationInfo.packageName} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${ApplicationInfo.name} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${ApplicationInfo.versionName} + CFBundleSignature + ???? + CFBundleVersion + ${ApplicationInfo.versionCode} + LSRequiresIPhoneOS + + UIViewControllerBasedStatusBarAppearance + + UIStatusBarHidden + + MinimumOSVersion + 11.0 + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + opengles-2 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleIconName + AppIcon + + + """.trimIndent(), + ) + } +} + +tasks.matching { it.name.startsWith("createIpa") || it.name.startsWith("launchIPhone") } + .configureEach { dependsOn(generatePlist) } + +robovm { + isIosSkipSigning = true +} diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/1024.png b/ios/data/Media.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000..27dfbb6 Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/1024.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/114.png b/ios/data/Media.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000..47c3038 Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/114.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/120.png b/ios/data/Media.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000..94017ae Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/120.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/180.png b/ios/data/Media.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000..526cb68 Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/180.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/29.png b/ios/data/Media.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000..bcc43e5 Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/29.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/40.png b/ios/data/Media.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000..f2bfe25 Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/40.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/57.png b/ios/data/Media.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000..ee511b4 Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/57.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/58.png b/ios/data/Media.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000..367ea25 Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/58.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/60.png b/ios/data/Media.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000..7d7a96b Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/60.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/80.png b/ios/data/Media.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000..74e0e3e Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/80.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/87.png b/ios/data/Media.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000..c706b9a Binary files /dev/null and b/ios/data/Media.xcassets/AppIcon.appiconset/87.png differ diff --git a/ios/data/Media.xcassets/AppIcon.appiconset/Contents.json b/ios/data/Media.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..4613af7 --- /dev/null +++ b/ios/data/Media.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,100 @@ +{ + "images": [ + { + "size": "60x60", + "expected-size": "180", + "filename": "180.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "3x" + }, + { + "size": "40x40", + "expected-size": "80", + "filename": "80.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "2x" + }, + { + "size": "40x40", + "expected-size": "120", + "filename": "120.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "3x" + }, + { + "size": "60x60", + "expected-size": "120", + "filename": "120.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "2x" + }, + { + "size": "57x57", + "expected-size": "57", + "filename": "57.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "1x" + }, + { + "size": "29x29", + "expected-size": "58", + "filename": "58.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "2x" + }, + { + "size": "29x29", + "expected-size": "29", + "filename": "29.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "1x" + }, + { + "size": "29x29", + "expected-size": "87", + "filename": "87.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "3x" + }, + { + "size": "57x57", + "expected-size": "114", + "filename": "114.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "2x" + }, + { + "size": "20x20", + "expected-size": "40", + "filename": "40.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "2x" + }, + { + "size": "20x20", + "expected-size": "60", + "filename": "60.png", + "folder": "Media.xcassets/AppIcon.appiconset/", + "idiom": "iphone", + "scale": "3x" + }, + { + "size": "1024x1024", + "filename": "1024.png", + "expected-size": "1024", + "idiom": "ios-marketing", + "folder": "Media.xcassets/AppIcon.appiconset/", + "scale": "1x" + } + ] +} diff --git a/ios/robovm.xml b/ios/robovm.xml new file mode 100644 index 0000000..ee5b704 --- /dev/null +++ b/ios/robovm.xml @@ -0,0 +1,47 @@ + + IOSLauncher + ru.fredboy.cavedroid.ios.IOSLauncher + ios + ios + build/generated/Info.plist.xml + conservative + + + ../assets + + ** + + true + + + data + + + + com.badlogic.gdx.scenes.scene2d.ui.* + com.badlogic.gdx.graphics.g3d.particles.** + com.android.okhttp.HttpHandler + com.android.okhttp.HttpsHandler + com.android.org.conscrypt.** + com.android.org.bouncycastle.jce.provider.BouncyCastleProvider + com.android.org.bouncycastle.jcajce.provider.keystore.BC$Mappings + com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi + com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi$Std + com.android.org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi + com.android.org.bouncycastle.crypto.digests.AndroidDigestFactoryOpenSSL + org.apache.harmony.security.provider.cert.DRLCertFactory + org.apache.harmony.security.provider.crypto.CryptoProvider + + + z + + + UIKit + QuartzCore + CoreGraphics + OpenAL + AudioToolbox + AVFoundation + GameController + + diff --git a/ios/src/main/kotlin/ru/fredboy/cavedroid/ios/IOSLauncher.kt b/ios/src/main/kotlin/ru/fredboy/cavedroid/ios/IOSLauncher.kt new file mode 100644 index 0000000..120cad4 --- /dev/null +++ b/ios/src/main/kotlin/ru/fredboy/cavedroid/ios/IOSLauncher.kt @@ -0,0 +1,45 @@ +package ru.fredboy.cavedroid.ios + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.backends.iosrobovm.IOSApplication +import com.badlogic.gdx.backends.iosrobovm.IOSApplicationConfiguration +import org.robovm.apple.foundation.NSAutoreleasePool +import org.robovm.apple.foundation.NSFileManager +import org.robovm.apple.foundation.NSSearchPathDirectory +import org.robovm.apple.foundation.NSSearchPathDomainMask +import org.robovm.apple.uikit.UIApplication +import ru.fredboy.cavedroid.zygote.CaveDroidApplication + +object IOSLauncher : IOSApplication.Delegate() { + override fun createApplication(): IOSApplication? { + val config = IOSApplicationConfiguration() + + val paths = NSFileManager.getDefaultManager() + .getURLsForDirectory( + NSSearchPathDirectory.DocumentDirectory, + NSSearchPathDomainMask.UserDomainMask, + ) + + val dataDir = paths.firstOrNull()?.path ?: run { + Gdx.app.error("IOSLauncher", "Couldn't locate data dir") + Gdx.app.exit() + return null + } + + val caveDroidApplication = CaveDroidApplication( + gameDataDirectoryPath = dataDir, + isTouchScreen = true, + isDebug = false, + preferencesStore = IOSPreferencesStore(), + ) + + return IOSApplication(caveDroidApplication, config) + } + + @JvmStatic + fun main(argv: Array) { + val pool = NSAutoreleasePool() + UIApplication.main(argv, null, IOSLauncher::class.java) + pool.close() + } +} diff --git a/ios/src/main/kotlin/ru/fredboy/cavedroid/ios/IOSPreferencesStore.kt b/ios/src/main/kotlin/ru/fredboy/cavedroid/ios/IOSPreferencesStore.kt new file mode 100644 index 0000000..525bf1f --- /dev/null +++ b/ios/src/main/kotlin/ru/fredboy/cavedroid/ios/IOSPreferencesStore.kt @@ -0,0 +1,22 @@ +package ru.fredboy.cavedroid.ios + +import org.robovm.apple.foundation.NSUserDefaults +import ru.fredboy.cavedroid.common.api.PreferencesStore + +class IOSPreferencesStore : PreferencesStore { + + private val userDefaults = NSUserDefaults.getStandardUserDefaults() + + override fun getPreference(key: String): String? { + return userDefaults.getString(key) + } + + override fun setPreference(key: String, value: String?) { + if (value != null) { + userDefaults.put(key, value) + } else { + userDefaults.remove(key) + } + userDefaults.synchronize() // optional, usually not needed unless you want immediate persistence + } +} diff --git a/make-release.sh b/make-release.sh new file mode 100755 index 0000000..119cd20 --- /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.sh "$0" || exit 1 + +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/android-$1.apk" +cp desktop/build/libs/*.jar "$release_dir/" + +echo "$release_dir/" diff --git a/require-clean-work-tree.sh b/require-clean-work-tree.sh new file mode 100755 index 0000000..cdd7e75 --- /dev/null +++ b/require-clean-work-tree.sh @@ -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/settings.gradle b/settings.gradle deleted file mode 100644 index 77ae463..0000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include 'desktop', 'android', 'core' \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..547663e --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,62 @@ +include("android") +include("desktop") +include("ios") + +/** + * Global modules + */ +include("core:common") + +/** + * Data modules + */ +include("core:data:assets") +include("core:data:configuration") +include("core:data:items") +include("core:data:menu") +include("core:data:save") + +/** + * First level domain models + */ +include("core:domain:assets") +include("core:domain:configuration") +include("core:domain:items") +include("core:domain:menu") +include("core:domain:world") +include("core:domain:save") + +/** + * Second level domain models + */ +include("core:entity:container") +include("core:entity:drop") +include("core:entity:mob") + +/** + * Game scope modules + */ +include("core:game") + +// controller modules +include("core:game:controller:drop") +include("core:game:controller:container") +include("core:game:controller:mob") + +// ui windows module +include("core:game:window") + +// world module +include("core:game:world") + +/** + * UX Modules, physics, rendering, controls, etc. + */ +include("core:ux:controls") +include("core:ux:physics") +include("core:ux:rendering") + +/** + * Zygote module: initialization, menu, screens... + */ +include("core:zygote") diff --git a/up-version.sh b/up-version.sh new file mode 100755 index 0000000..1ca19a7 --- /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/\(const val versionName = \)\".*\"/\1\"'"$new_version"'\"/g' buildSrc/src/main/kotlin/ApplicationInfo.kt +sed -i 's/\(\s*const val versionCode = \)\([0-9]*\)/echo "\1$((\2+1))"/ge' buildSrc/src/main/kotlin/ApplicationInfo.kt +sed -i 's/\(const val VERSION = \)\".*\"/\1\"'"$new_version_string"'\"/' core/common/src/main/kotlin/ru/fredboy/cavedroid/common/CaveDroidConstants.kt + +git add buildSrc/src/main/kotlin/ApplicationInfo.kt core/common/src/main/kotlin/ru/fredboy/cavedroid/common/CaveDroidConstants.kt + +git commit -m "Update version" +git tag "$new_version" +q \ No newline at end of file