DEADSOFTWARE

Store block references intead of ids
authorfredboy <fredboy@protonmail.com>
Sat, 20 Apr 2024 15:29:45 +0000 (22:29 +0700)
committerfredboy <fredboy@protonmail.com>
Sat, 20 Apr 2024 15:29:45 +0000 (22:29 +0700)
29 files changed:
android/assets/json/game_items.json
core/src/ru/deadsoftware/cavedroid/game/GameInput.java
core/src/ru/deadsoftware/cavedroid/game/GameItems.java
core/src/ru/deadsoftware/cavedroid/game/GameItemsHolder.kt
core/src/ru/deadsoftware/cavedroid/game/GameModule.java
core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java
core/src/ru/deadsoftware/cavedroid/game/GameProc.java
core/src/ru/deadsoftware/cavedroid/game/GameRenderer.java
core/src/ru/deadsoftware/cavedroid/game/GameSaver.java
core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToBackgroundAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToForegroundAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGrassAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateGravelAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateRequiresBlockAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/updateblock/UpdateSandAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseLavaBucketAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UseWaterBucketAction.kt
core/src/ru/deadsoftware/cavedroid/game/mobs/FallingGravel.java
core/src/ru/deadsoftware/cavedroid/game/mobs/FallingSand.java
core/src/ru/deadsoftware/cavedroid/game/mobs/Mob.java
core/src/ru/deadsoftware/cavedroid/game/mobs/Pig.kt
core/src/ru/deadsoftware/cavedroid/game/mobs/Player.java
core/src/ru/deadsoftware/cavedroid/game/model/block/Block.kt
core/src/ru/deadsoftware/cavedroid/game/model/dto/BlockDto.kt
core/src/ru/deadsoftware/cavedroid/game/model/mapper/BlockMapper.kt
core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldBlocksLogicControllerTask.kt
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldFluidsLogicControllerTask.java
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldGenerator.kt

index cd2fae927034f94013c763bb11fa7f36e4770be5..279e13fb5fd262d440178fbd0c23fce60b1abfd5 100644 (file)
     "lava": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 0,
       "frames": 16,
       "hp": -1,
       "id": 9,
     "lava_12": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 2,
       "frames": 16,
       "hp": -1,
       "id": 65,
     "lava_16": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 1,
       "frames": 16,
       "hp": -1,
       "id": 64,
     "lava_4": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 4,
       "frames": 16,
       "hp": -1,
       "id": 67,
     "lava_8": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 3,
       "frames": 16,
       "hp": -1,
       "id": 66,
     "water": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 0,
       "frames": 16,
       "hp": -1,
       "id": 8,
     "water_12": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 2,
       "frames": 16,
       "hp": -1,
       "id": 61,
     "water_16": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 1,
       "frames": 16,
       "hp": -1,
       "id": 60,
     "water_4": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 4,
       "frames": 16,
       "hp": -1,
       "id": 63,
     "water_8": {
       "animated": true,
       "collision": false,
-      "fluid": true,
+      "state": 3,
       "frames": 16,
       "hp": -1,
       "id": 62,
index bab16fd0533b3733701e803a7f944fc84fe3dba3..32826528c62ba6ed0ce38772a5b44a45ff965f13 100644 (file)
@@ -21,7 +21,6 @@ import ru.deadsoftware.cavedroid.game.world.GameWorld;
 import ru.deadsoftware.cavedroid.misc.Assets;
 import ru.deadsoftware.cavedroid.misc.ControlMode;
 
-import javax.annotation.CheckForNull;
 import javax.inject.Inject;
 
 import java.util.Map;
@@ -37,6 +36,7 @@ public class GameInput {
     private final GameWorld mGameWorld;
     private final DropController mDropController;
     private final MobsController mMobsController;
+    private final GameItemsHolder mGameItemsHolder;
     private final Map<String, IUseItemAction> mUseItemActionMap;
     private final Map<String, IPlaceBlockAction> mPlaceBlockActionMap;
 
@@ -64,12 +64,14 @@ public class GameInput {
                      GameWorld gameWorld,
                      DropController dropController,
                      MobsController mobsController,
+                     GameItemsHolder gameItemsHolder,
                      Map<String, IUseItemAction> useItemActionMap,
                      Map<String, IPlaceBlockAction> placeBlockActionMap) {
         mMainConfig = mainConfig;
         mGameWorld = gameWorld;
         mDropController = dropController;
         mMobsController = mobsController;
+        mGameItemsHolder = gameItemsHolder;
         mUseItemActionMap = useItemActionMap;
         mPlaceBlockActionMap = placeBlockActionMap;
 
@@ -79,7 +81,7 @@ public class GameInput {
     }
 
     private boolean checkSwim() {
-        return GameItems.isFluid(mGameWorld.getForeMap(mPlayer.getMapX(), mPlayer.getLowerMapY()));
+        return mGameWorld.getForeMap(mPlayer.getMapX(), mPlayer.getLowerMapY()).isFluid();
     }
 
     private void goUpwards() {
@@ -150,7 +152,7 @@ public class GameInput {
     }
 
     private boolean isNotAutoselectable(int x, int y) {
-        return (!mGameWorld.hasForeAt(x, y) || !mGameWorld.getForeMapBlock(x, y).hasCollision());
+        return (!mGameWorld.hasForeAt(x, y) || !mGameWorld.getForeMap(x, y).hasCollision());
     }
 
     private void checkCursorBounds() {
@@ -247,27 +249,27 @@ public class GameInput {
         if (mMainConfig.checkGameUiWindow(GameUiWindow.NONE)) {
             mPlayer.startHitting();
 
-            if ((mGameWorld.hasForeAt(mCurX, mCurY) && mGameWorld.getForeMapBlock(mCurX, mCurY).getHp() >= 0) ||
+            if ((mGameWorld.hasForeAt(mCurX, mCurY) && mGameWorld.getForeMap(mCurX, mCurY).getHp() >= 0) ||
                     (!mGameWorld.hasForeAt(mCurX, mCurY) && mGameWorld.hasBackAt(mCurX, mCurY) &&
-                            mGameWorld.getBackMapBlock(mCurX, mCurY).getHp() >= 0)) {
+                            mGameWorld.getBackMap(mCurX, mCurY).getHp() >= 0)) {
                 if (mPlayer.gameMode == 0) {
                     mBlockDamage++;
                     if (mGameWorld.hasForeAt(mCurX, mCurY)) {
-                        if (mBlockDamage >= mGameWorld.getForeMapBlock(mCurX, mCurY).getHp()) {
+                        if (mBlockDamage >= mGameWorld.getForeMap(mCurX, mCurY).getHp()) {
                             mGameWorld.destroyForeMap(mCurX, mCurY);
                             mBlockDamage = 0;
                         }
                     } else if (mGameWorld.hasBackAt(mCurX, mCurY)) {
-                        if (mBlockDamage >= mGameWorld.getBackMapBlock(mCurX, mCurY).getHp()) {
+                        if (mBlockDamage >= mGameWorld.getBackMap(mCurX, mCurY).getHp()) {
                             mGameWorld.destroyBackMap(mCurX, mCurY);
                             mBlockDamage = 0;
                         }
                     }
                 } else {
                     if (mGameWorld.hasForeAt(mCurX, mCurY)) {
-                        mGameWorld.placeToForeground(mCurX, mCurY, 0);
+                        mGameWorld.placeToForeground(mCurX, mCurY, mGameItemsHolder.getFallbackBlock());
                     } else if (mGameWorld.hasBackAt(mCurX, mCurY)) {
-                        mGameWorld.placeToBackground(mCurX, mCurY, 0);
+                        mGameWorld.placeToBackground(mCurX, mCurY, mGameItemsHolder.getFallbackBlock());
                     }
                     mTouchedDown = false;
                 }
@@ -336,10 +338,6 @@ public class GameInput {
                 pig.attachToController(mMobsController);
                 break;
 
-            case Input.Keys.Q:
-                mGameWorld.placeToForeground(mCurX, mCurY, 8);
-                break;
-
             case Input.Keys.GRAVE:
                 mMobsController.getPlayer().gameMode = (mMobsController.getPlayer().gameMode + 1) % 2;
                 break;
index 4024c3614c111a17c64c689ad8fc80b5a500a05e..370f1b0081367e6d601100cf7b630576f1091fcd 100644 (file)
@@ -55,12 +55,6 @@ public class GameItems {
         return getBlock(id) instanceof Block.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);
     }
@@ -148,6 +142,7 @@ public class GameItems {
                 int id = Assets.getIntFromJson(block, "id", count);
                 int dropCount = Assets.getIntFromJson(block, "drop_count", 1);
                 String fullBlock = Assets.getStringFromJson(block, "full_block", null);
+                int state = Assets.getIntFromJson(block, "state", 0);
                 blocksIds.put(key, id);
 
                 if (count >= id) {
@@ -178,8 +173,8 @@ public class GameItems {
                 );
 
                 Block newBlock = switch (meta) {
-                    case "water" -> new Block.Water(params);
-                    case "lava" -> new Block.Lava(params);
+                    case "water" -> new Block.Water(params, state);
+                    case "lava" -> new Block.Lava(params, state);
                     case "slab" -> new Block.Slab(params, fullBlock);
                     default -> new Block.Normal(params);
                 };
index 73da0c2cf9ba63ef0540f5af7def55dfe5c73ba4..058e84d7f283a8ab47dc03df09d48a997cb3824e 100644 (file)
@@ -14,6 +14,7 @@ import javax.inject.Inject
 
 @GameScope
 class GameItemsHolder @Inject constructor(
+    private val assetLoader: AssetLoader,
     private val blockMapper: BlockMapper,
     private val itemMapper: ItemMapper,
 ) {
@@ -23,14 +24,19 @@ class GameItemsHolder @Inject constructor(
     private val blocksMap = LinkedHashMap<String, Block>()
     private val itemsMap = LinkedHashMap<String, Item>()
 
-    private lateinit var fallbackBlock: Block
-    private lateinit var fallbackItem: Item
-
+    lateinit var fallbackBlock: Block
+        private set
+    lateinit var fallbackItem: Item
+        private set
 
+    init {
+        initialize()
+    }
 
     private fun loadBlocks(dtoMap: Map<String, BlockDto>) {
         dtoMap.forEach { (key, dto) ->
             blocksMap[key] = blockMapper.map(key, dto)
+                .apply(Block::initialize)
         }
 
         fallbackBlock = blocksMap[FALLBACK_BLOCK_KEY]
@@ -50,7 +56,7 @@ class GameItemsHolder @Inject constructor(
             ?: throw IllegalArgumentException("Fallback item key '$FALLBACK_ITEM_KEY' not found")
     }
 
-    fun initialize(assetLoader: AssetLoader) {
+    fun initialize() {
         if (_initialized) {
             Gdx.app.debug(TAG, "Attempted to init when already initialized")
             return
@@ -66,6 +72,10 @@ class GameItemsHolder @Inject constructor(
     }
 
     private fun <T> Map<String, T>.getOrFallback(key: String, fallback: T, lazyErrorMessage: () -> String): T {
+        if (!_initialized) {
+            throw IllegalStateException("GameItemsHolder was not initialized before use")
+        }
+
         val t = this[key] ?: run {
             Gdx.app.error(TAG, lazyErrorMessage.invoke())
             return fallback
@@ -85,6 +95,10 @@ class GameItemsHolder @Inject constructor(
         }
     }
 
+    fun <T : Block> getBlocksByType(type: Class<T>): List<T> {
+        return blocksMap.values.filterIsInstance(type)
+    }
+
     companion object {
         private const val TAG = "GameItemsHolder"
 
index a72dbcd9e3fe06d09b908924510d0a0a3a0e0f49..c47a0e19144a7a6f3d25e78a18d16df26fc2df56 100644 (file)
@@ -43,11 +43,14 @@ public class GameModule {
 
     @Provides
     @GameScope
-    public static GameWorld provideGameWorld(DropController dropController, MobsController mobsController) {
-        int[][] fm = data != null ? data.retrieveForeMap() : null;
-        int[][] bm = data != null ? data.retrieveBackMap() : null;
+    public static GameWorld provideGameWorld(DropController dropController,
+                                             MobsController mobsController,
+                                             GameItemsHolder gameItemsHolder) {
+        // TODO: 4/20/24 RE-enable saves
+//        int[][] fm = data != null ? data.retrieveForeMap() : null;
+//        int[][] bm = data != null ? data.retrieveBackMap() : null;
         makeDataNullIfEmpty();
-        return new GameWorld(dropController, mobsController, fm, bm);
+        return new GameWorld(dropController, mobsController, gameItemsHolder, null, null);
     }
 
 }
index 1ff603c26148f9a473cd9705bfc29ae665e1690a..9edffe4a9f3c41e128b1cfb30ee5e2561a6db5f1 100644 (file)
@@ -8,6 +8,7 @@ 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.model.block.Block;
 import ru.deadsoftware.cavedroid.game.objects.Drop;
 import ru.deadsoftware.cavedroid.game.objects.DropController;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
@@ -29,16 +30,19 @@ public class GamePhysics {
     private final MainConfig mMainConfig;
     private final MobsController mMobsController;
     private final DropController mDropController;
+    private final GameItemsHolder mGameItemsHolder;
 
     @Inject
     public GamePhysics(GameWorld gameWorld,
                        MainConfig mainConfig,
                        MobsController mobsController,
-                       DropController dropController) {
+                       DropController dropController,
+                       GameItemsHolder gameItemsHolder) {
         mGameWorld = gameWorld;
         mMainConfig = mainConfig;
         mMobsController = mobsController;
         mDropController = dropController;
+        mGameItemsHolder = gameItemsHolder;
     }
 
     /**
@@ -50,14 +54,14 @@ public class GamePhysics {
         int dir = mob.looksLeft() ? 0 : 1;
         int blX = (int) (mob.getX() + mob.getWidth() * dir - 8 + 16 * dir);
         int blY = (int) (mob.getY() + mob.getHeight() - 8);
-        int block = mGameWorld.getForeMap(blX / 16, blY / 16);
+        Block block = mGameWorld.getForeMap(blX / 16, blY / 16);
 
         if (checkColl(new Rectangle(blX, mob.getY() - 18, mob.getWidth(), mob.getHeight()))) {
-            block = 0;
+            return false;
         }
 
-        return (block > 0 && GameItems.getBlock(block).toJump() &&
-                (mob.getY() + mob.getHeight()) - GameItems.getBlock(block).getRectangle(blX / 16, blY / 16).y > 8);
+        return (block.toJump() &&
+                (mob.getY() + mob.getHeight()) - block.getRectangle(blX / 16, blY / 16).y > 8);
     }
 
     private boolean checkColl(Rectangle rect) {
@@ -74,12 +78,15 @@ public class GamePhysics {
             maxY = mGameWorld.getHeight();
         }
 
-        int block;
+        Block block;
         for (int y = minY; y < maxY; y++) {
             for (int x = minX; x < maxX; x++) {
+                if (!mGameWorld.hasForeAt(x, y)) {
+                    continue;
+                }
                 block = mGameWorld.getForeMap(x, y);
-                if (block > 0 && GameItems.getBlock(block).hasCollision()) {
-                    if (Intersector.overlaps(rect, GameItems.getBlock(block).getRectangle(x, y))) {
+                if (block.hasCollision()) {
+                    if (Intersector.overlaps(rect, block.getRectangle(x, y))) {
                         return true;
                     }
                 }
@@ -89,7 +96,7 @@ public class GamePhysics {
         return false;
     }
 
-    private int getBlock(Rectangle rect) {
+    private Block getBlock(Rectangle rect) {
         return mGameWorld.getForeMap((int) (rect.x + rect.width / 2) / 16,
                 (int) (rect.y + rect.height / 8 * 7) / 16);
     }
@@ -239,7 +246,7 @@ public class GamePhysics {
             return;
         }
 
-        if (GameItems.isFluid(getBlock(player))) {
+        if (getBlock(player).isFluid()) {
             if (mMainConfig.isTouch() && player.getVelocity().x != 0 && !player.swim && !player.isFlyMode()) {
                 player.swim = true;
             }
@@ -266,7 +273,7 @@ public class GamePhysics {
         mobYColl(player);
 
         player.x += player.getVelocity().x * (player.isFlyMode() ? 1.5f : 1) *
-                (GameItems.isFluid(getBlock(player)) && !player.isFlyMode() ? .8f : 1) * delta;
+                (getBlock(player).isFluid() && !player.isFlyMode() ? .8f : 1) * delta;
 
         mobXColl(player);
 
@@ -277,7 +284,7 @@ public class GamePhysics {
     }
 
     private void mobPhy(Mob mob, float delta) {
-        if (mob.getType() == Mob.Type.MOB && GameItems.isFluid(getBlock(mob))) {
+        if (mob.getType() == Mob.Type.MOB && getBlock(mob).isFluid()) {
             if (mob.getVelocity().y > 32f) {
                 mob.getVelocity().y -= mob.getVelocity().y * 32f * delta;
             }
@@ -320,7 +327,7 @@ public class GamePhysics {
 
         for (Iterator<Mob> it = mMobsController.getMobs().iterator(); it.hasNext(); ) {
             Mob mob = it.next();
-            mob.ai(mGameWorld, delta);
+            mob.ai(mGameWorld, mGameItemsHolder, delta);
             mobPhy(mob, delta);
             if (mob.isDead()) {
                 it.remove();
@@ -329,7 +336,7 @@ public class GamePhysics {
 
         playerPhy(player, delta);
         if (player.isDead()) {
-            player.respawn(mGameWorld);
+            player.respawn(mGameWorld, mGameItemsHolder);
         }
     }
 
index 0c3947b499e789a144cad218de8bf294b90e0e9c..3a2244052b1c8b1353ea104751570aea671fe055 100644 (file)
@@ -42,7 +42,7 @@ public class GameProc implements Disposable {
         mGameWorldBlocksLogicControllerTask = gameWorldBlocksLogicControllerTask;
         mGameItemsHolder = gameItemsHolder;
 
-        mGameItemsHolder.initialize(assetLoader);
+        mGameItemsHolder.initialize();
 
         mWorldLogicTimer.scheduleTask(gameWorldFluidsLogicControllerTask, 0,
                 GameWorldFluidsLogicControllerTask.FLUID_UPDATE_INTERVAL_SEC);
index dd136b8aa2a75aba3bcdf11c3e3422b854f9f8f0..49b9f0c9bc4d64fd61a9514ff6d1e6e7cb86067a 100644 (file)
@@ -63,9 +63,9 @@ public class GameRenderer extends Renderer {
         return y * 16 - getCamY();
     }
 
-    private void drawWreck(int bl) {
+    private void drawWreck(Block bl) {
         if (mGameInput.getBlockDamage() > 0) {
-            int index = 10 * mGameInput.getBlockDamage() / GameItems.getBlock(bl).getHp();
+            int index = 10 * mGameInput.getBlockDamage() / bl.getHp();
             String key = "break_" + index;
 
             if (index > 10 || index < 0) {
@@ -79,16 +79,16 @@ public class GameRenderer extends Renderer {
 
     private void drawBlock(int x, int y, boolean drawBG) {
         if (drawBG) {
-            if ((!mGameWorld.hasForeAt(x, y) || mGameWorld.getForeMapBlock(x, y).isTransparent())
+            if ((!mGameWorld.hasForeAt(x, y) || mGameWorld.getForeMap(x, y).isTransparent())
                     && mGameWorld.hasBackAt(x, y)) {
-                mGameWorld.getBackMapBlock(x, y).draw(spriter, drawX(x), drawY(y));
+                mGameWorld.getBackMap(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 (mGameWorld.hasForeAt(x, y) && mGameWorld.getForeMap(x, y).isBackground() == drawBG) {
+            mGameWorld.getForeMap(x, y).draw(spriter, drawX(x), drawY(y));
             if (x == mGameInput.getCurX() && y == mGameInput.getCurY()) {
                 drawWreck(mGameWorld.getForeMap(mGameInput.getCurX(), mGameInput.getCurY()));
             }
@@ -119,7 +119,7 @@ public class GameRenderer extends Renderer {
             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())
+                    if ((!mGameWorld.hasForeAt(x, y) || mGameWorld.getForeMap(x, y).isTransparent())
                             && mGameWorld.hasBackAt(x, y)) {
                         shaper.rect(drawX(x), drawY(y), 16, 16);
                     }
@@ -301,7 +301,7 @@ public class GameRenderer extends Renderer {
         final boolean hasBackMap = mGameWorld.hasBackAt(x, y);
 
         if (hasForeMap) {
-            final Block block = mGameWorld.getForeMapBlock(x, y);
+            final Block block = mGameWorld.getForeMap(x, y);
 
             if (GameItems.isWater(block)) {
                 result = Color.BLUE;
@@ -385,10 +385,9 @@ public class GameRenderer extends Renderer {
             drawString("Swim: " + player.swim, 0, 60);
             drawString("Mobs: " + mMobsController.getMobs().size(), 0, 70);
             drawString("Drops: " + mDropController.getSize(), 0, 80);
-            drawString("Block: " + GameItems.getBlockKey(mGameWorld.getForeMap(mGameInput.getCurX(), mGameInput.getCurY())), 0, 90);
+            drawString("Block: " + mGameWorld.getForeMap(mGameInput.getCurX(), mGameInput.getCurY()).getParams().getKey(), 0, 90);
             drawString("Hand: " + GameItems.getItemKey(mMobsController.getPlayer().inventory[mMobsController.getPlayer().slot]), 0, 100);
             drawString("Game mode: " + player.gameMode, 0, 110);
-            drawString("Check swim: " + GameItems.isFluid(mGameWorld.getForeMap(player.getMapX(), player.getLowerMapY())), 0, 120);
             spriter.end();
         }
 
index 0e149839bb77bfee69de30e2584a386b8edd65d1..0864a6878ae4fac02fbbf5e9c3cf4b252fe89ee3 100644 (file)
@@ -179,8 +179,9 @@ public class GameSaver {
             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());
+            // TODO: 4/20/24 save map
+//            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();
         }
index dd61e0bbd006e8a42112066df985328b51208203..bdebfcbfed133a3ac880bd0086b86ec4049df2cc 100644 (file)
@@ -10,8 +10,8 @@ class PlaceBlockItemToBackgroundAction @Inject constructor(
     private val gameWorld: GameWorld,
 ) : IPlaceBlockAction {
 
-    override fun place(item: Item.Placeable, x: Int, y: Int) {
-        gameWorld.placeToBackground(x, y, item.block.params.id)
+    override fun place(placeable: Item.Placeable, x: Int, y: Int) {
+        gameWorld.placeToBackground(x, y, placeable.block)
     }
 
     companion object {
index 75c7bcb5302dd84e62ea2877cee036a9be722acf..1f74448ab60dfa012777121d6338e9787593fe9d 100644 (file)
@@ -10,8 +10,8 @@ class PlaceBlockItemToForegroundAction @Inject constructor(
     private val gameWorld: GameWorld,
 ) : IPlaceBlockAction {
 
-    override fun place(item: Item.Placeable, x: Int, y: Int) {
-        gameWorld.placeToForeground(x, y, item.block.params.id)
+    override fun place(placeable: Item.Placeable, x: Int, y: Int) {
+        gameWorld.placeToForeground(x, y, placeable.block)
     }
 
     companion object {
index a9397c9ef0e58d0aa26edc9d222cdb3a5141b26f..27005b8da53f4eb008c31ab26ba92b2568f11953 100644 (file)
@@ -1,6 +1,6 @@
 package ru.deadsoftware.cavedroid.game.actions.updateblock
 
-import ru.deadsoftware.cavedroid.game.GameItems
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.world.GameWorld
 import javax.inject.Inject
@@ -8,12 +8,13 @@ import javax.inject.Inject
 @GameScope
 class UpdateGrassAction @Inject constructor(
     private val gameWorld: GameWorld,
+    private val mGameItemsHolder: GameItemsHolder,
 ) : IUpdateBlockAction {
 
     override fun update(x: Int, y: Int) {
-        val blockOnTop = gameWorld.getForeMapBlock(x, y - 1)
+        val blockOnTop = gameWorld.getForeMap(x, y - 1)
         if (blockOnTop.collision || blockOnTop.isFluid()) {
-            gameWorld.setForeMap(x, y, GameItems.getBlockId("dirt"))
+            gameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("dirt"))
         }
     }
 
index 4d39acf778b4cd2cc616c62385c78e1cb6aa969e..6cc026f0dbaa25551d30c28a6fee676b89d304ab 100644 (file)
@@ -13,10 +13,10 @@ class UpdateGravelAction @Inject constructor(
 ) : IUpdateBlockAction {
 
     override fun update(x: Int, y: Int) {
-        val shouldFall = gameWorld.getForeMapBlock(x, y + 1).collision.not()
+        val shouldFall = gameWorld.getForeMap(x, y + 1).params.hasCollision.not()
 
         if (shouldFall) {
-            gameWorld.setForeMap(x, y, 0)
+            gameWorld.resetForeMap(x, y)
             FallingGravel(x * 16f, y * 16f)
                 .apply { attachToController(mobsController) }
         }
index 2e273731dce10ed28357f045e8cfdb833be0d07c..57172a5f24a7ef53408e4516b5aa7e3f3bc583eb 100644 (file)
@@ -10,7 +10,7 @@ class UpdateRequiresBlockAction @Inject constructor(
 ) : IUpdateBlockAction {
 
     override fun update(x: Int, y: Int) {
-        if (!gameWorld.getForeMapBlock(x, y + 1).collision) {
+        if (gameWorld.getForeMap(x, y + 1).params.hasCollision.not()) {
             gameWorld.destroyForeMap(x, y)
         }
     }
index c90899c75af7ecf3e91d2714a3638528b079de0d..4a2b58e885c42fdbda8c71fadea87c2c065d4628 100644 (file)
@@ -13,10 +13,10 @@ class UpdateSandAction @Inject constructor(
 ) : IUpdateBlockAction {
 
     override fun update(x: Int, y: Int) {
-        val shouldFall = gameWorld.getForeMapBlock(x, y + 1).collision.not()
+        val shouldFall = gameWorld.getForeMap(x, y + 1).params.hasCollision.not()
 
         if (shouldFall) {
-            gameWorld.setForeMap(x, y, 0)
+            gameWorld.resetForeMap(x, y)
             FallingSand(x * 16f, y * 16f)
                 .apply { attachToController(mobsController) }
         }
index 0bbc0429c0df8e1c1635076d87d0b2ad737ad46f..7256ae9519c2c420bb333be106f3505389f71ed5 100644 (file)
@@ -1,6 +1,7 @@
 package ru.deadsoftware.cavedroid.game.actions.useitem
 
 import ru.deadsoftware.cavedroid.game.GameItems
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.model.item.Item
@@ -11,10 +12,11 @@ import javax.inject.Inject
 class UseLavaBucketAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
 ) : IUseItemAction {
 
     override fun perform(item: Item.Usable, x: Int, y: Int) {
-        gameWorld.placeToForeground(x, y, GameItems.getBlockId("lava"))
+        gameWorld.placeToForeground(x, y, gameItemsHolder.getBlock("lava"))
         mobsController.player.setCurrentInventorySlotItem(GameItems.getItemId("bucket_empty"))
     }
 
index 9668c4aa7fc4d90775f0f2079ac3d02af5d9cda0..32025ced4c98f86d9ca7093de11e17e019a7e6a7 100644 (file)
@@ -1,6 +1,7 @@
 package ru.deadsoftware.cavedroid.game.actions.useitem
 
 import ru.deadsoftware.cavedroid.game.GameItems
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.model.item.Item
@@ -11,10 +12,11 @@ import javax.inject.Inject
 class UseWaterBucketAction @Inject constructor(
     private val gameWorld: GameWorld,
     private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
 ) : IUseItemAction {
 
     override fun perform(item: Item.Usable, x: Int, y: Int) {
-        gameWorld.placeToForeground(x, y, GameItems.getBlockId("water"))
+        gameWorld.placeToForeground(x, y, gameItemsHolder.getBlock("water"))
         mobsController.player.setCurrentInventorySlotItem(GameItems.getItemId("bucket_empty"))
     }
 
index 81fdb93b5dbd0e2c8e918167969f19a517f6e17d..e80f91b5e70987b2697fe9005a2d10d6e1841248 100644 (file)
@@ -3,6 +3,7 @@ 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.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
 /**
@@ -33,9 +34,9 @@ public class FallingGravel extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
         if (mVelocity.isZero()) {
-            gameWorld.setForeMap(getMapX(), getMiddleMapY(), 11);
+            gameWorld.setForeMap(getMapX(), getMiddleMapY(), gameItemsHolder.getBlock("gravel"));
             kill();
         }
     }
index 7186b3a1ff13ee0af1375eb65fd6b2aba9cc202d..14ec825d5735d37f7000ca84df0e4851b582c9e1 100644 (file)
@@ -3,6 +3,7 @@ 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.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
 
@@ -34,9 +35,9 @@ public class FallingSand extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
         if (mVelocity.isZero()) {
-            gameWorld.setForeMap(getMapX(), getMiddleMapY(), 10);
+            gameWorld.setForeMap(getMapX(), getMiddleMapY(), gameItemsHolder.getBlock("sand"));
             kill();
         }
     }
index feb5ceb08a0165f58c00538678dbcc982e4a6812..394346f2b9252f31d4982d7b2c4771d9d42bcf1c 100644 (file)
@@ -5,6 +5,7 @@ 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.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
 import java.io.Serializable;
@@ -270,7 +271,7 @@ public abstract class Mob extends Rectangle implements Serializable {
 
     public abstract void draw(SpriteBatch spriteBatch, float x, float y, float delta);
 
-    public abstract void ai(GameWorld gameWorld, float delta);
+    public abstract void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta);
 
     public abstract void changeDir();
 
index 4f9d5fcef6caa2d48dd59cb8cdb291696a8c55f8..b55f42934be9f2d8d100d72d7068aa6e055c2951 100644 (file)
@@ -3,6 +3,7 @@ package ru.deadsoftware.cavedroid.game.mobs
 import com.badlogic.gdx.graphics.g2d.SpriteBatch
 import com.badlogic.gdx.math.MathUtils
 import com.badlogic.gdx.math.Vector2
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.world.GameWorld
 import ru.deadsoftware.cavedroid.misc.utils.drawSprite
 import ru.deadsoftware.cavedroid.misc.utils.mobs.MobSprites.Pig.getBackgroundLeg
@@ -27,7 +28,7 @@ class Pig(x: Float, y: Float) : Mob(x, y, WIDTH, HEIGHT, randomDir(), Type.MOB,
         velocity.y = JUMP_VELOCITY
     }
     
-    override fun ai(world: GameWorld, delta: Float) {
+    override fun ai(world: GameWorld, gameItemsHolder: GameItemsHolder, delta: Float) {
         if (MathUtils.randomBoolean(delta)) {
             if (velocity.x != 0f) {
                 velocity.x = 0f
index 6563fd9f7cdace58920e325301815b9577087c2c..d6d848c12621aad20fd0c6c471814aaaf60b26c6 100644 (file)
@@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch;
 import com.badlogic.gdx.math.MathUtils;
 import com.badlogic.gdx.math.Vector2;
 import ru.deadsoftware.cavedroid.game.GameItems;
+import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.model.item.Item;
 import ru.deadsoftware.cavedroid.game.objects.Drop;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
@@ -36,8 +37,8 @@ public class Player extends Mob {
         swim = false;
     }
 
-    public void respawn(GameWorld gameWorld) {
-        Vector2 pos = getSpawnPoint(gameWorld);
+    public void respawn(GameWorld gameWorld, GameItemsHolder itemsHolder) {
+        Vector2 pos = getSpawnPoint(gameWorld, itemsHolder);
         this.x = pos.x;
         this.y = pos.y;
         mVelocity.setZero();
@@ -55,15 +56,15 @@ public class Player extends Mob {
         }
     }
 
-    private Vector2 getSpawnPoint(GameWorld gameWorld) {
+    private Vector2 getSpawnPoint(GameWorld gameWorld, GameItemsHolder itemsHolder) {
         int y;
         for (y = 0; y < gameWorld.getHeight(); y++) {
             if (y == gameWorld.getHeight() - 1) {
                 y = 60;
-                gameWorld.setForeMap(0, y, 1);
+                gameWorld.setForeMap(0, y, itemsHolder.getBlock("grass"));
                 break;
             }
-            if (gameWorld.hasForeAt(0, y) && gameWorld.getForeMapBlock(0, y).hasCollision()) {
+            if (gameWorld.hasForeAt(0, y) && gameWorld.getForeMap(0, y).hasCollision()) {
                 break;
             }
         }
@@ -91,7 +92,7 @@ public class Player extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
     }
 
     @Override
index 45363440e290fad732955cddb78424b7c2e6c01f..798e3d9b8bfe3e5506de866b4c6e06215cbfaaf5 100644 (file)
@@ -26,7 +26,7 @@ sealed class Block {
         }
 
     val sprite: Sprite
-        get() = requireNotNull(_sprite)
+        get() = requireNotNull(_sprite) { "null sprite for block '${params.key}'" }
 
     private val currentAnimationFrame: Int
         get() {
@@ -115,14 +115,18 @@ sealed class Block {
         val fullBlockKey: String,
     ): Block()
 
-    sealed class Fluid: Block()
+    sealed class Fluid: Block() {
+        abstract val state: Int
+    }
     
     data class Water(
         override val params: CommonBlockParams,
+        override val state: Int,
     ) : Fluid()
 
     data class Lava(
         override val params: CommonBlockParams,
+        override val state: Int,
     ) : Fluid()
 
     /* Legacy accessors below */
index 697a04201da7f1ec1984426bbfbec4fa81eabbd1..ae1bfba8d768cc9cfe03cf195060f27044cdcc00 100644 (file)
@@ -26,4 +26,5 @@ data class BlockDto(
     @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,
 )
index 0838491c3fa173b2361f0174cd94dbabe8a6aebc..363801c013f02d96b3619b37b230dd37272ec223 100644 (file)
@@ -18,8 +18,8 @@ class BlockMapper @Inject constructor(
         val commonBlockParams = mapCommonParams(key, dto)
 
         return when (dto.meta) {
-            "water" -> Water(commonBlockParams)
-            "lava" -> Lava(commonBlockParams)
+            "water" -> Water(commonBlockParams, requireNotNull(dto.state))
+            "lava" -> Lava(commonBlockParams, requireNotNull(dto.state))
             "slab" -> Slab(commonBlockParams, requireNotNull(dto.fullBlock))
             else -> Normal(commonBlockParams)
         }
index d4a66bcb8ad622ca43193d9df7f9cdb95d24d020..39ae5d39e070c4ec8d24312c21300bd5361214a4 100644 (file)
@@ -2,6 +2,7 @@ package ru.deadsoftware.cavedroid.game.world;
 
 import kotlin.Pair;
 import ru.deadsoftware.cavedroid.game.GameItems;
+import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.GameScope;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
 import ru.deadsoftware.cavedroid.game.model.block.Block;
@@ -16,19 +17,22 @@ public class GameWorld {
 
     private final DropController mDropController;
     private final MobsController mMobsController;
+    private final GameItemsHolder mGameItemsHolder;
 
     private final int mWidth;
     private final int mHeight;
-    private final int[][] mForeMap;
-    private final int[][] mBackMap;
+    private final Block[][] mForeMap;
+    private final Block[][] mBackMap;
 
     @Inject
     public GameWorld(DropController dropController,
                      MobsController mobsController,
-                     @CheckForNull int[][] foreMap,
-                     @CheckForNull int[][] backMap) {
+                     GameItemsHolder gameItemsHolder,
+                     @CheckForNull Block[][] foreMap,
+                     @CheckForNull Block[][] backMap) {
         mDropController = dropController;
         mMobsController = mobsController;
+        mGameItemsHolder = gameItemsHolder;
 
         boolean isNewGame = foreMap == null || backMap == null;
 
@@ -36,10 +40,10 @@ public class GameWorld {
             final WorldGeneratorConfig config = WorldGeneratorConfig.Companion.getDefault();
             mWidth = config.getWidth();
             mHeight = config.getHeight();
-            Pair<int[][], int[][]> maps = new GameWorldGenerator(config).generate();
+            Pair<Block[][], Block[][]> maps = new GameWorldGenerator(config, mGameItemsHolder).generate();
             mForeMap = maps.getFirst();
             mBackMap = maps.getSecond();
-            mMobsController.getPlayer().respawn(this);
+            mMobsController.getPlayer().respawn(this, mGameItemsHolder);
         } else {
             mForeMap = foreMap;
             mBackMap = backMap;
@@ -64,11 +68,11 @@ public class GameWorld {
         return mHeight * 16f;
     }
 
-    public int[][] getFullForeMap() {
+    public Block[][] getFullForeMap() {
         return mForeMap;
     }
 
-    public int[][] getFullBackMap() {
+    public Block[][] getFullBackMap() {
         return mBackMap;
     }
 
@@ -80,8 +84,8 @@ public class GameWorld {
         return x;
     }
 
-    private int getMap(int x, int y, int layer) {
-        int map = 0;
+    private Block getMap(int x, int y, int layer) {
+        Block map = mGameItemsHolder.getFallbackBlock();
         try {
             x = transformX(x);
             map = (layer == 0) ? mForeMap[x][y] : mBackMap[x][y];
@@ -90,7 +94,7 @@ public class GameWorld {
         return map;
     }
 
-    private void setMap(int x, int y, int layer, int value) {
+    private void setMap(int x, int y, int layer, Block value) {
         try {
             x = transformX(x);
             if (layer == 0) {
@@ -103,68 +107,61 @@ public class GameWorld {
     }
 
     public boolean hasForeAt(int x, int y) {
-        return getMap(x, y, 0) != 0;
+        return getMap(x, y, 0) != mGameItemsHolder.getFallbackBlock();
     }
 
     public boolean hasBackAt(int x, int y) {
-        return getMap(x, y, 1) != 0;
+        return getMap(x, y, 1) != mGameItemsHolder.getFallbackBlock();
     }
 
-    public int getForeMap(int x, int y) {
+    public Block 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, Block block) {
+        setMap(x, y, 0, block);
     }
 
-    public void setForeMap(int x, int y, int id) {
-        setMap(x, y, 0, id);
+    public void resetForeMap(int x, int y) {
+        setForeMap(x, y, mGameItemsHolder.getFallbackBlock());
     }
 
-    public int getBackMap(int x, int y) {
+    public Block 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, Block block) {
+        setMap(x, y, 1, block);
     }
 
-    public void setBackMap(int x, int y, int id) {
-        setMap(x, y, 1, id);
-    }
-
-    public void placeToForeground(int x, int y, int value) {
-        if (!hasForeAt(x, y) || value == 0 || !GameItems.getBlock(getForeMap(x, y)).hasCollision()) {
+    public void placeToForeground(int x, int y, Block value) {
+        if (!hasForeAt(x, y) || value == mGameItemsHolder.getFallbackBlock() || !getForeMap(x, y).hasCollision()) {
             setForeMap(x, y, value);
-        } else if (GameItems.isSlab(value) && getForeMap(x, y) == value) {
-            final Block block = GameItems.getBlock(value);
-            if (block instanceof Block.Slab) {
-                setForeMap(x, y, GameItems.getBlockId(((Block.Slab) block).getFullBlockKey()));
-            }
+        } else if (value instanceof Block.Slab && getForeMap(x, y) == value) {
+            setForeMap(x, y, mGameItemsHolder.getBlock(((Block.Slab) value).getFullBlockKey()));
         }
     }
 
-    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)) {
+    public void placeToBackground(int x, int y, Block value) {
+        if (value == mGameItemsHolder.getFallbackBlock() || (getBackMap(x, y) == mGameItemsHolder.getFallbackBlock() && value.hasCollision()) &&
+                (!value.isTransparent() || value == mGameItemsHolder.getBlock("glass"))) {
             setBackMap(x, y, value);
         }
     }
 
     public void destroyForeMap(int x, int y) {
-        Block block = GameItems.getBlock(getForeMap(x, y));
+        Block block = getForeMap(x, y);
         if (block.hasDrop()) {
             mDropController.addDrop(transformX(x) * 16 + 4, y * 16 + 4, GameItems.getItemId(block.getDrop()));
         }
-        placeToForeground(x, y, 0);
+        placeToForeground(x, y, mGameItemsHolder.getFallbackBlock());
     }
 
     public void destroyBackMap(int x, int y) {
-        Block block = GameItems.getBlock(getBackMap(x, y));
+        Block block = getBackMap(x, y);
         if (block.hasDrop()) {
             mDropController.addDrop(transformX(x) * 16 + 4, y * 16 + 4, GameItems.getItemId(block.getDrop()));
         }
-        placeToBackground(x, y, 0);
+        placeToBackground(x, y, mGameItemsHolder.getFallbackBlock());
     }
 }
\ No newline at end of file
index c515330d8b51261d55249ab746b25c0c2f2ea3fb..f0f4af907499745719466741018db1abbc53f12a 100644 (file)
@@ -25,7 +25,7 @@ class GameWorldBlocksLogicControllerTask @Inject constructor(
     }
 
     private fun updateBlock(x: Int, y: Int) {
-        val block = gameWorld.getForeMapBlock(x, y)
+        val block = gameWorld.getForeMap(x, y)
         val blockKey = block.params.key
         val action = updateBlockActions[blockKey]
             ?: updateBlockActions.getRequiresBlockAction().takeIf { block.params.requiresBlock }
index d996cf42989c41a1025b54fb1c1fd2b77e053cf0..ed6b1ec634d70cc880ddb81f9d28897eaf811f16 100644 (file)
 package ru.deadsoftware.cavedroid.game.world;
 
 import com.badlogic.gdx.utils.Timer;
+import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.GameScope;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
+import ru.deadsoftware.cavedroid.game.model.block.Block;
 
+import javax.annotation.CheckForNull;
 import javax.inject.Inject;
-import java.util.Arrays;
-
-import static ru.deadsoftware.cavedroid.game.GameItems.*;
+import java.util.*;
 
 @GameScope
 public class GameWorldFluidsLogicControllerTask extends Timer.Task {
 
     public static final float FLUID_UPDATE_INTERVAL_SEC = 0.1f;
-    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 final GameWorld mGameWorld;
     private final MobsController mMobsController;
+    private final GameItemsHolder mGameItemsHolder;
+
+    private final Map<Class<? extends Block.Fluid>, List<? extends Block.Fluid>> mFluidStatesMap;
 
     @Inject
     GameWorldFluidsLogicControllerTask(GameWorld gameWorld,
-                                       MobsController mobsController) {
+                                       MobsController mobsController,
+                                       GameItemsHolder gameItemsHolder) {
         mGameWorld = gameWorld;
         mMobsController = mobsController;
+        mGameItemsHolder = gameItemsHolder;
+
+        final List<Block.Water> waters = mGameItemsHolder.getBlocksByType(Block.Water.class);
+        waters.sort(Comparator.comparingInt(Block.Water::getState));
+
+        final List<Block.Lava> lavas = mGameItemsHolder.getBlocksByType(Block.Lava.class);
+        lavas.sort(Comparator.comparingInt(Block.Lava::getState));
+
+        mFluidStatesMap = new HashMap<>();
+        mFluidStatesMap.put(Block.Water.class, waters);
+        mFluidStatesMap.put(Block.Lava.class, lavas);
     }
 
-    private int getBlockState(int id) {
-        return isWater(id) ? Arrays.binarySearch(WATER_IDS, id) : Arrays.binarySearch(LAVA_IDS, id);
+    @CheckForNull
+    private List<? extends Block.Fluid> getFluidStateList(Block.Fluid fluid) {
+        return mFluidStatesMap.get(fluid.getClass());
     }
 
-    private int getNextBlockState(int id) {
-        if (!isFluid(id)) {
+    private int getCurrentStateIndex(Block.Fluid fluid) {
+        @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
+
+        if (stateList == null) {
             return -1;
         }
-        int state = getBlockState(id);
-        if (state < FLUID_STATES - 1) {
-            return state + 1;
-        }
-        return -1;
+
+        return stateList.indexOf(fluid);
     }
 
-    private int getNextBlockStateId(int id) {
-        int nextState = getNextBlockState(id);
-        if (nextState == -1) {
-            return 0;
+    @CheckForNull
+    private Block.Fluid getNextStateBlock(Block.Fluid fluid) {
+        @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
+
+        if (stateList == null) {
+            return null;
         }
-        if (isWater(id)) {
-            return WATER_IDS[nextState];
+
+        int currentState = stateList.indexOf(fluid);
+
+        if (currentState < 0) {
+            return null;
         }
-        return LAVA_IDS[nextState];
-    }
 
-    private int id(int x, int y) {
-        return mGameWorld.getForeMap(x, y);
-    }
+        int nextState = currentState + 1;
+
+        if (nextState == 1) {
+            nextState++;
+        }
 
-    private boolean sameFluid(int thisId, int thatId) {
-        return isFluid(thatId) && isWater(thatId) == isWater(thisId);
+        if (nextState < stateList.size()) {
+            return stateList.get(nextState);
+        }
+
+        return null;
     }
 
     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));
+        return !mGameWorld.getForeMap(x, y - 1).isFluid() &&
+                (!mGameWorld.getForeMap(x - 1, y).isFluid() || ((Block.Fluid)mGameWorld.getForeMap(x - 1, y)).getState() >= ((Block.Fluid)mGameWorld.getForeMap(x, y)).getState()) &&
+                (!mGameWorld.getForeMap(x + 1, y).isFluid() || ((Block.Fluid)mGameWorld.getForeMap(x + 1, y)).getState() >= ((Block.Fluid)mGameWorld.getForeMap(x, y)).getState());
     }
 
     private boolean drainFluid(int x, int y) {
-        if (getBlockState(id(x, y)) > 0) {
+        final Block block = mGameWorld.getForeMap(x, y);
+
+        if (!(block instanceof Block.Fluid fluid)) {
+            return true;
+        }
+
+        if (fluid.getState() > 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;
+                @CheckForNull final Block nextState = getNextStateBlock(fluid);
+                if (nextState == null) {
+                    mGameWorld.resetForeMap(x, y);
+                    return true;
+                }
+
+                mGameWorld.setForeMap(x, y, nextState);
             }
         }
         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
+    private boolean fluidCanFlowThere(Block.Fluid fluid, Block targetBlock) {
+        return targetBlock == mGameItemsHolder.getFallbackBlock() ||
+                (!targetBlock.getParams().getHasCollision() && !targetBlock.isFluid()) ||
+                (fluid.getClass() == targetBlock.getClass() && fluid.getState() < ((Block.Fluid)targetBlock).getState());
+    }
+
+    private void flowFluidTo(Block.Fluid currentFluid, int x, int y, Block.Fluid nextStateFluid) {
+        final Block targetBlock = mGameWorld.getForeMap(x, y);
+
+        if (fluidCanFlowThere(currentFluid, targetBlock)) {
+            mGameWorld.setForeMap(x, y, nextStateFluid);
+        } else if (currentFluid.isWater() && targetBlock.isLava()) {
+            if (((Block.Lava)targetBlock).getState() > 0) {
+                mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("cobblestone"));
             } else {
-                mGameWorld.setForeMap(x, y, 68); //obsidian
+                mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("obsidian"));
             }
-        } else if (isLava(thisId) && isWater(thatId)) {
-            mGameWorld.setForeMap(x, y, 1); //stone
+        } else if (currentFluid.isLava() && targetBlock.isWater()) {
+            mGameWorld.setForeMap(x, y, mGameItemsHolder.getBlock("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++;
+        Block.Fluid fluid = (Block.Fluid) mGameWorld.getForeMap(x, y);
+        @CheckForNull final List<? extends Block.Fluid> stateList = getFluidStateList(fluid);
+
+        if (stateList == null) {
+            return;
+        }
+
+        if (fluid.getState() < stateList.size() - 1 && mGameWorld.getForeMap(x, y + 1).hasCollision()) {
+            @CheckForNull Block.Fluid nextState = getNextStateBlock(fluid);
+
+            if (nextState == null) {
+                return;
             }
-            flowFluidTo(id, x - 1, y, nextStateId);
-            flowFluidTo(id, x + 1, y, nextStateId);
+
+            flowFluidTo(fluid, x - 1, y, nextState);
+            flowFluidTo(fluid, x + 1, y, nextState);
         } else {
-            flowFluidTo(id, x, y + 1, isWater(id) ? WATER_IDS[1] : LAVA_IDS[1]);
+            flowFluidTo(fluid, x, y + 1, stateList.get(1));
         }
 
     }
 
     private void updateFluids(int x, int y) {
-        if (!isFluid(id(x, y))) {
+        if (!mGameWorld.getForeMap(x, y).isFluid()) {
             return;
         }
         if (drainFluid(x, y)) {
index 8776254f7dca8d6a1d3b3212aff95335bdb22733..b984c4dbacfd0e25002201701f8d332c0df7fcac 100644 (file)
@@ -1,6 +1,7 @@
 package ru.deadsoftware.cavedroid.game.world
 
-import ru.deadsoftware.cavedroid.game.GameItems
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.model.block.Block
 import ru.deadsoftware.cavedroid.game.model.world.Biome
 import ru.deadsoftware.cavedroid.game.model.world.generator.WorldGeneratorConfig
 import kotlin.math.abs
@@ -9,12 +10,13 @@ import kotlin.random.Random
 
 class GameWorldGenerator(
     private val config: WorldGeneratorConfig,
+    private val gameItemsHolder: GameItemsHolder,
 ) {
 
     private val random = Random(config.seed)
 
-    private val foreMap by lazy { Array(config.width) { IntArray(config.height) } }
-    private val backMap by lazy { Array(config.width) { IntArray(config.height) } }
+    private val foreMap by lazy { Array(config.width) { Array(config.height) { gameItemsHolder.fallbackBlock } } }
+    private val backMap by lazy { Array(config.width) { Array(config.height) { gameItemsHolder.fallbackBlock } } }
 
     private val heights by lazy { generateHeights() }
     private val biomesMap by lazy { generateBiomes() }
@@ -62,20 +64,20 @@ class GameWorldGenerator(
 
         val surfaceHeight = heights[x]
 
-        val grassId = GameItems.getBlockId("grass")
-        val bedrockId = GameItems.getBlockId("bedrock")
-        val dirtId = GameItems.getBlockId("dirt")
-        val stoneId = GameItems.getBlockId("stone")
+        val grass = gameItemsHolder.getBlock("grass")
+        val bedrock = gameItemsHolder.getBlock("bedrock")
+        val dirt = gameItemsHolder.getBlock("dirt")
+        val stone = gameItemsHolder.getBlock("stone")
 
-        foreMap[x][surfaceHeight] = grassId
-        foreMap[x][config.height - 1] = bedrockId
-        backMap[x][surfaceHeight] = grassId
-        backMap[x][config.height - 1] = bedrockId
+        foreMap[x][surfaceHeight] = grass
+        foreMap[x][config.height - 1] = bedrock
+        backMap[x][surfaceHeight] = grass
+        backMap[x][config.height - 1] = bedrock
 
         for (y in surfaceHeight + 1 ..< config.height - 1) {
             foreMap[x][y] = when {
-                y < surfaceHeight + random.nextInt(5, 8) -> dirtId
-                else -> stoneId
+                y < surfaceHeight + random.nextInt(5, 8) -> dirt
+                else -> stone
             }
             backMap[x][y] = foreMap[x][y]
         }
@@ -86,22 +88,22 @@ class GameWorldGenerator(
 
         val surfaceHeight = heights[x]
 
-        val sandId = GameItems.getBlockId("sand")
-        val bedrockId = GameItems.getBlockId("bedrock")
-        val sandstoneId = GameItems.getBlockId("sandstone")
-        val stoneId = GameItems.getBlockId("stone")
+        val sand = gameItemsHolder.getBlock("sand")
+        val bedrock = gameItemsHolder.getBlock("bedrock")
+        val sandstone = gameItemsHolder.getBlock("sandstone")
+        val stone = gameItemsHolder.getBlock("stone")
 
 
-        foreMap[x][surfaceHeight] = sandId
-        foreMap[x][config.height - 1] = bedrockId
-        backMap[x][surfaceHeight] = sandId
-        backMap[x][config.height - 1] = bedrockId
+        foreMap[x][surfaceHeight] = sand
+        foreMap[x][config.height - 1] = bedrock
+        backMap[x][surfaceHeight] = sand
+        backMap[x][config.height - 1] = bedrock
 
         for (y in surfaceHeight + 1 ..< config.height - 1) {
             foreMap[x][y] = when {
-                y < surfaceHeight + random.nextInt(5, 8) -> sandId
-                y < surfaceHeight + random.nextInt(0, 2) -> sandstoneId
-                else -> stoneId
+                y < surfaceHeight + random.nextInt(5, 8) -> sand
+                y < surfaceHeight + random.nextInt(0, 2) -> sandstone
+                else -> stone
             }
             backMap[x][y] = foreMap[x][y]
         }
@@ -112,37 +114,36 @@ class GameWorldGenerator(
     }
 
     private fun fillWater() {
-        val waterId = GameItems.getBlockId("water")
+        val water = gameItemsHolder.getBlock("water")
 
         for (x in 0 ..< config.width) {
             for (y in config.seaLevel ..< config.height) {
-                if (foreMap[x][y] != 0) {
+                if (foreMap[x][y] != gameItemsHolder.fallbackBlock) {
                     break
                 }
 
-                foreMap[x][y] = waterId
+                foreMap[x][y] = water
             }
         }
     }
 
     private fun generateCactus(x: Int) {
-        val cactusId = GameItems.getBlockId("cactus")
-        val cactusHeight = random.nextInt(5)
+        val cactus = gameItemsHolder.getBlock("cactus")
+        val cactusHeight = random.nextInt(3)
         val h = heights[x] - 1
 
         for (y in h downTo max(0, h - cactusHeight)) {
-            foreMap[x][y] = cactusId
+            foreMap[x][y] = cactus
         }
     }
 
     /**
      * Generate world
      */
-    fun generate(): Pair<Array<IntArray>, Array<IntArray>> {
+    fun generate(): Pair<Array<Array<Block>>, Array<Array<Block>>> {
         var biome = Biome.PLAINS
 
         for (x in 0 until config.width) {
-            val xHeight = heights[x]
             biome = biomesMap[x] ?: biome
 
             when (biome) {