DEADSOFTWARE

Add mobs damage and initial spawn
authorfredboy <fredboy@protonmail.com>
Sun, 12 May 2024 16:46:18 +0000 (23:46 +0700)
committerfredboy <fredboy@protonmail.com>
Sun, 12 May 2024 16:46:18 +0000 (23:46 +0700)
core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java
core/src/ru/deadsoftware/cavedroid/game/actions/useitem/UsePigSpawnEggAction.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/Player.java
core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java
core/src/ru/deadsoftware/cavedroid/game/world/GameWorldMobDamageControllerTask.kt
core/src/ru/deadsoftware/cavedroid/misc/utils/SpriteUtils.kt

index d9bf21fe5dec302fad3e1cf984e7d44eae22852e..cb5b37e35a5d5a0c157c225cfac938e4154dbdfc 100644 (file)
@@ -354,7 +354,7 @@ public class GamePhysics {
 
         for (Iterator<Mob> it = mMobsController.getMobs().iterator(); it.hasNext(); ) {
             Mob mob = it.next();
-            mob.ai(mGameWorld, mGameItemsHolder, delta);
+            mob.ai(mGameWorld, mGameItemsHolder, mMobsController, delta);
             mobPhy(mob, delta);
             if (mob.isDead()) {
                 it.remove();
@@ -362,7 +362,7 @@ public class GamePhysics {
         }
 
         playerPhy(player, delta);
-        player.ai(mGameWorld, mGameItemsHolder, delta);
+        player.ai(mGameWorld, mGameItemsHolder, mMobsController, delta);
         if (player.isDead()) {
             player.respawn(mGameWorld, mGameItemsHolder);
         }
index 0c50d15eda6434e7da11e4c9fbeb5de455895f3d..6c0c3ba714b098fca8390fc85649a91733d71cb4 100644 (file)
@@ -1,5 +1,6 @@
 package ru.deadsoftware.cavedroid.game.actions.useitem
 
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
 import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.mobs.Pig
@@ -10,6 +11,7 @@ import javax.inject.Inject
 @GameScope
 class UsePigSpawnEggAction @Inject constructor(
     private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
 ) : IUseItemAction {
 
     override fun perform(item: Item.Usable, x: Int, y: Int) {
@@ -18,7 +20,7 @@ class UsePigSpawnEggAction @Inject constructor(
                 attachToController(mobsController)
             }
 
-        mobsController.player.inventory.decreaseCurrentItemAmount()
+        mobsController.player.decreaseCurrentItemCount(gameItemsHolder)
     }
 
     companion object {
index 641ffe554ec84255bbe0b1099b3f67a9aded0464..2ad4287577e770758ab76bb2a7db3da59cb6616e 100644 (file)
@@ -40,7 +40,7 @@ public class FallingGravel extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta) {
         if (mVelocity.isZero()) {
             gameWorld.setForeMap(getMapX(), getUpperMapY(), gameItemsHolder.getBlock("gravel"));
             kill();
@@ -51,6 +51,11 @@ public class FallingGravel extends Mob {
     public void changeDir() {
     }
 
+    @Override
+    public void damage(int damage) {
+        // no-op
+    }
+
     @Override
     public void draw(SpriteBatch spriteBatch, float x, float y, float delta) {
         @CheckForNull final Texture texture = Assets.blockTextures.get("gravel");
index f41da96872c12aa7fc32ce6dffc885822d13935d..970ff538c0b762bf88bcae6949e7e95c96355a5b 100644 (file)
@@ -41,7 +41,7 @@ public class FallingSand extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta) {
         if (mVelocity.isZero()) {
             gameWorld.setForeMap(getMapX(), getUpperMapY(), gameItemsHolder.getBlock("sand"));
             kill();
@@ -52,6 +52,11 @@ public class FallingSand extends Mob {
     public void changeDir() {
     }
 
+    @Override
+    public void damage(int damage) {
+        // no-op
+    }
+
     @Override
     public void draw(SpriteBatch spriteBatch, float x, float y, float delta) {
         @CheckForNull final Texture texture = Assets.blockTextures.get("sand");
index fc9d61bbe778e37f1c1186fa1387f0be207fb8d6..62ddc50d4df7a6e00217a3a1f161cee8e745cb44 100644 (file)
@@ -1,13 +1,16 @@
 package ru.deadsoftware.cavedroid.game.mobs;
 
 import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.graphics.Color;
 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
 import com.badlogic.gdx.math.MathUtils;
 import com.badlogic.gdx.math.Rectangle;
 import com.badlogic.gdx.math.Vector2;
+import com.badlogic.gdx.utils.Timer;
 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.world.GameWorld;
 
+import javax.annotation.CheckForNull;
 import java.io.Serializable;
 
 /**
@@ -15,6 +18,11 @@ import java.io.Serializable;
  */
 public abstract class Mob extends Rectangle implements Serializable {
 
+    private static final float DAMAGE_TINT_TIMEOUT_S = 0.5f;
+    private static final Color DAMAGE_TINT_COLOR = new Color(0xff8080ff);
+
+    private static final float HIT_RANGE = 8f;
+
     protected static int ANIMATION_SPEED = 360;
 
     public enum Type {
@@ -51,6 +59,14 @@ public abstract class Mob extends Rectangle implements Serializable {
         }
     }
 
+    private class ResetTakeDamageTask extends Timer.Task {
+
+        @Override
+        public void run() {
+            mTakingDamage = false;
+        }
+    }
+
     protected Vector2 mVelocity;
     protected Type mType;
     protected int mAnimDelta = ANIMATION_SPEED;
@@ -64,6 +80,9 @@ public abstract class Mob extends Rectangle implements Serializable {
     private final int mMaxHealth;
     private int mHealth;
 
+    private transient boolean mTakingDamage = false;
+    @CheckForNull private transient ResetTakeDamageTask mResetTakeDamageTask = null;
+
     /**
      * @param x          in pixels
      * @param y          in pixels
@@ -261,6 +280,8 @@ public abstract class Mob extends Rectangle implements Serializable {
 
         mHealth -= damage;
         checkHealth();
+
+        setTakingDamage(true);
     }
 
     public void heal(int heal) {
@@ -277,9 +298,35 @@ public abstract class Mob extends Rectangle implements Serializable {
         checkHealth();
     }
 
+    public Rectangle getHitBox() {
+        return new Rectangle(x - HIT_RANGE, y - HIT_RANGE, width + HIT_RANGE, height + HIT_RANGE);
+    }
+
+    public boolean isTakingDamage() {
+        return mTakingDamage;
+    }
+
+    public void setTakingDamage(boolean takingDamage) {
+        mTakingDamage = takingDamage;
+
+        if (takingDamage) {
+            if (mResetTakeDamageTask != null && mResetTakeDamageTask.isScheduled()) {
+                mResetTakeDamageTask.cancel();
+            } else if (mResetTakeDamageTask == null) {
+                mResetTakeDamageTask = new ResetTakeDamageTask();
+            }
+
+            Timer.schedule(mResetTakeDamageTask, DAMAGE_TINT_TIMEOUT_S);
+        }
+    }
+
+    protected Color getTintColor() {
+        return isTakingDamage() ? DAMAGE_TINT_COLOR : Color.WHITE;
+    }
+
     public abstract void draw(SpriteBatch spriteBatch, float x, float y, float delta);
 
-    public abstract void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta);
+    public abstract void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta);
 
     public abstract void changeDir();
 
index b55f42934be9f2d8d100d72d7068aa6e055c2951..68c457ee05887ee81521ac93bffe0740e8e84688 100644 (file)
@@ -27,8 +27,18 @@ class Pig(x: Float, y: Float) : Mob(x, y, WIDTH, HEIGHT, randomDir(), Type.MOB,
     override fun jump() {
         velocity.y = JUMP_VELOCITY
     }
-    
-    override fun ai(world: GameWorld, gameItemsHolder: GameItemsHolder, delta: Float) {
+
+    override fun damage(damage: Int) {
+        super.damage(damage)
+
+        if (damage > 0) {
+            if (canJump()) {
+                jump()
+            }
+        }
+    }
+
+    override fun ai(world: GameWorld, gameItemsHolder: GameItemsHolder, mobsController: MobsController, delta: Float) {
         if (MathUtils.randomBoolean(delta)) {
             if (velocity.x != 0f) {
                 velocity.x = 0f
@@ -45,19 +55,19 @@ class Pig(x: Float, y: Float) : Mob(x, y, WIDTH, HEIGHT, randomDir(), Type.MOB,
         val rightLegX = x + getRightLegRelativeX(direction)
         val legY = y + getLegsRelativeY()
 
-        spriteBatch.drawSprite(getBackgroundLeg(), leftLegX, legY, -anim)
-        spriteBatch.drawSprite(getBackgroundLeg(), rightLegX, legY, -anim)
-        spriteBatch.drawSprite(getBody(direction), x, y)
-        spriteBatch.drawSprite(getForegroundLeg(), leftLegX, legY, anim)
-        spriteBatch.drawSprite(getForegroundLeg(), rightLegX, legY, anim)
+        spriteBatch.drawSprite(getBackgroundLeg(), leftLegX, legY, -anim, tint = tintColor)
+        spriteBatch.drawSprite(getBackgroundLeg(), rightLegX, legY, -anim, tint = tintColor)
+        spriteBatch.drawSprite(getBody(direction), x, y, tint = tintColor)
+        spriteBatch.drawSprite(getForegroundLeg(), leftLegX, legY, anim, tint = tintColor)
+        spriteBatch.drawSprite(getForegroundLeg(), rightLegX, legY, anim, tint = tintColor)
     }
     
     
     private companion object {
         private const val WIDTH = 25f
         private const val HEIGHT = 18f
-        private const val SPEED =  69.072f
+        private const val SPEED =  48f
         private const val JUMP_VELOCITY = -133.332f
-        private const val MAX_HEALTH = 10;
+        private const val MAX_HEALTH = 10
     }
 }
\ No newline at end of file
index 40b7c7954310345167e8a3ffd5551be232980b48..8ea87c11d8b42868a4dc393c9f100689496b4788 100644 (file)
@@ -6,6 +6,7 @@ import com.badlogic.gdx.math.MathUtils;
 import com.badlogic.gdx.math.Vector2;
 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.mobs.Mob;
+import ru.deadsoftware.cavedroid.game.mobs.MobsController;
 import ru.deadsoftware.cavedroid.game.model.block.Block;
 import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
 import ru.deadsoftware.cavedroid.game.model.item.Item;
@@ -151,6 +152,29 @@ public class Player extends Mob {
         return !block.isNone() && block.getParams().getHitPoints() >= 0;
     }
 
+    /**
+     * @return true if any mob fas hit
+     */
+    private boolean hitMobs(GameItemsHolder gameItemsHolder, MobsController mobsController) {
+        if (!hitting || !hittingWithDamage) {
+            return false;
+        }
+
+        boolean result = false;
+        for (Mob mob : mobsController.getMobs()) {
+            if (overlaps(mob.getHitBox())) {
+                final Item activeItem = inventory.getActiveItem().getItem();
+                final Item.Tool tool = activeItem.isTool() ? (Item.Tool) activeItem : null;
+                if (tool != null) {
+                    decreaseCurrentItemCount(gameItemsHolder);
+                }
+                result = true;
+                mob.damage(MathUtils.floor(tool != null ? tool.getMobDamageMultiplier() : 1));
+            }
+        }
+        return result;
+    }
+
     private void hitBlock(GameWorld gameWorld, GameItemsHolder gameItemsHolder) {
         if (!hitting || !hittingWithDamage) {
             return;
@@ -188,9 +212,14 @@ public class Player extends Mob {
     }
 
     @Override
-    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, float delta) {
+    public void ai(GameWorld gameWorld, GameItemsHolder gameItemsHolder, MobsController mobsController, float delta) {
         updateAnimation(delta);
-        hitBlock(gameWorld, gameItemsHolder);
+
+        if (!hitMobs(gameItemsHolder, mobsController)) {
+            hitBlock(gameWorld, gameItemsHolder);
+        } else {
+            stopHitting();
+        }
 
         if (gameMode == 1) {
             return;
@@ -385,6 +414,13 @@ public class Player extends Mob {
             frontHandAnim = -rightHandAnim;
         }
 
+        backHand.setColor(getTintColor());
+        backLeg.setColor(getTintColor());
+        frontLeg.setColor(getTintColor());
+        head.setColor(getTintColor());
+        body.setColor(getTintColor());
+        frontHand.setColor(getTintColor());
+
         SpriteUtilsKt.drawSprite(spriteBatch, backHand, x + 2, y + 8, backHandAnim);
 
         if (looksLeft()) {
index d52b50f672039328773896ee12f11072949db368..05a7d5f0f6aa14802eb11fc7b05dcd8b96ba95a4 100644 (file)
@@ -1,9 +1,11 @@
 package ru.deadsoftware.cavedroid.game.world;
 
+import com.badlogic.gdx.math.MathUtils;
 import kotlin.Pair;
 import ru.deadsoftware.cavedroid.game.GameItemsHolder;
 import ru.deadsoftware.cavedroid.game.GameScope;
 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
+import ru.deadsoftware.cavedroid.game.mobs.Pig;
 import ru.deadsoftware.cavedroid.game.model.block.Block;
 import ru.deadsoftware.cavedroid.game.model.item.InventoryItem;
 import ru.deadsoftware.cavedroid.game.model.item.Item;
@@ -55,6 +57,7 @@ public class GameWorld {
             Pair<Block[][], Block[][]> maps = new GameWorldGenerator(mWorldConfig, mGameItemsHolder).generate();
             mForeMap = maps.getFirst();
             mBackMap = maps.getSecond();
+            spawnInitialMobs();
             mMobsController.getPlayer().respawn(this, mGameItemsHolder);
         } else {
             mForeMap = foreMap;
@@ -223,10 +226,25 @@ public class GameWorld {
         return toolLevel >= block.getParams().getToolLevel();
     }
 
+    private void spawnInitialMobs() {
+        for (int x = 0; x < getWidth(); x++) {
+            int y = 0;
+            while (y < getWorldConfig().getSeaLevel()) {
+                if (getForeMap(x, y) == mGameItemsHolder.getBlock("grass")) {
+                    if (MathUtils.randomBoolean(.125f)) {
+                        mMobsController.addMob(new Pig(MeasureUnitsUtilsKt.getPx(x), MeasureUnitsUtilsKt.getPx(y)));
+                    }
+                    break;
+                }
+                y++;
+            }
+        }
+    }
+
     public void destroyForeMap(int x, int y) {
         Block block = getForeMap(x, y);
         if (block.isContainer()) {
-            mContainerController.destroyContainer(x, y, FOREGROUND_Z);
+            mContainerController.destroyContainer(x, y, FOREGROUND_Z, true);
         }
         if (block.hasDrop() && shouldDrop(block)) {
             for (int i = 0; i < block.getParams().getDropInfo().getCount(); i++) {
@@ -243,6 +261,9 @@ public class GameWorld {
 
     public void destroyBackMap(int x, int y) {
         Block block = getBackMap(x, y);
+        if (block.isContainer()) {
+            mContainerController.destroyContainer(x, y, BACKGROUND_Z, true);
+        }
         if (block.hasDrop() && shouldDrop(block)) {
             for (int i = 0; i < block.getParams().getDropInfo().getCount(); i++) {
                 mDropController.addDrop(transformX(x) * 16 + 4, y * 16 + 4, mGameItemsHolder.getItem(block.getDrop()));
index 8dc39b93b8b8142261db7886e72d46a244bb8a6a..f016c678a484b35e530058a1e2bc7a5107ea9596 100644 (file)
@@ -24,7 +24,10 @@ class GameWorldMobDamageControllerTask @Inject constructor(
                 val foregroundBlock = gameWorld.getForeMap(x, y)
                 val backgroundBlock = gameWorld.getBackMap(x, y)
 
-                mob.damage(max(foregroundBlock.params.damage, backgroundBlock.params.damage))
+                val damage = max(foregroundBlock.params.damage, backgroundBlock.params.damage)
+                if (damage > 0) {
+                    mob.damage(damage)
+                }
             }
         }
 
index 7b75e3893be9f6a49b7e588aa67eb565738e62ad..a26c5b95dd8b3699fb4bf8362b8e7c6ed890520d 100644 (file)
@@ -1,5 +1,6 @@
 package ru.deadsoftware.cavedroid.misc.utils
 
+import com.badlogic.gdx.graphics.Color
 import com.badlogic.gdx.graphics.g2d.Sprite
 import com.badlogic.gdx.graphics.g2d.SpriteBatch
 
@@ -14,14 +15,20 @@ fun SpriteBatch.drawSprite(
     rotation: Float = 0f,
     width: Float = sprite.regionWidth.toFloat(),
     height: Float = sprite.regionHeight.toFloat(),
+    tint: Color? = null,
 ) {
+    val oldColor = sprite.color
+
     sprite.setPosition(x, y)
     sprite.setSize(width, height)
     sprite.rotation = rotation
+    tint?.let(sprite::setColor)
+
     sprite.draw(this)
 
     sprite.setSize(sprite.regionWidth.toFloat(), sprite.regionHeight.toFloat())
     sprite.rotation = 0f
+    sprite.color = oldColor
 }
 
 fun Sprite.applyOrigin(origin: SpriteOrigin) {