DEADSOFTWARE

Add top slabs
authorfredboy <fredboy@protonmail.com>
Sun, 21 Apr 2024 16:30:28 +0000 (23:30 +0700)
committerfredboy <fredboy@protonmail.com>
Sun, 21 Apr 2024 16:30:28 +0000 (23:30 +0700)
15 files changed:
android/assets/json/game_items.json
android/assets/json/texture_regions.json
core/src/ru/deadsoftware/cavedroid/game/GameItemsHolder.kt
core/src/ru/deadsoftware/cavedroid/game/actions/PlaceBlockActionsModule.kt
core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceBlockItemToForegroundAction.kt
core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceSlabAction.kt [new file with mode: 0644]
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/dto/ItemDto.kt
core/src/ru/deadsoftware/cavedroid/game/model/item/Item.kt
core/src/ru/deadsoftware/cavedroid/game/model/mapper/BlockMapper.kt
core/src/ru/deadsoftware/cavedroid/game/model/mapper/ItemMapper.kt
core/src/ru/deadsoftware/cavedroid/game/render/BlocksRenderer.kt
core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java
core/src/ru/deadsoftware/cavedroid/misc/Assets.java

index 5600fb258485c876e5c08d0bae897eb9873b396f..25b8db339112adec06b1c9bb97ed9bdd3c0b4a96 100644 (file)
       "drop": "iron_block",
       "texture": "iron_block"
     },
-    "stone_slab": {
+    "stone_slab_bottom": {
       "id": 51,
       "top": 8,
       "sprite_top": 8,
       "drop": "stone_slab",
       "meta": "slab",
       "texture": "stone_slab",
-      "full_block": "double_stone_slab"
+      "full_block": "double_stone_slab",
+      "other_part": "stone_slab_top"
+    },
+    "stone_slab_top": {
+      "id": 51,
+      "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"
     },
     "double_stone_slab": {
       "id": 52,
       "hp": 600,
       "drop": "stone_slab",
+      "drop_count": 2,
       "texture": "stone_slab"
     },
-    "sandstone_slab": {
+    "sandstone_slab_bottom": {
       "id": 53,
       "top": 8,
       "sprite_top": 8,
       "drop": "sandstone_slab",
       "meta": "slab",
       "texture": "sandstone",
-      "full_block": "sandstone"
+      "full_block": "sandstone",
+      "other_part": "sandstone_slab_top"
     },
-    "oak_slab": {
+    "sandstone_slab_top": {
+      "id": 53,
+      "bottom": 8,
+      "sprite_bottom": 8,
+      "hp": 600,
+      "transparent": true,
+      "drop": "sandstone_slab",
+      "meta": "slab",
+      "texture": "sandstone",
+      "full_block": "sandstone",
+      "other_part": "sandstone_slab_bottom"
+    },
+    "oak_slab_bottom": {
       "id": 54,
       "top": 8,
       "sprite_top": 8,
       "drop": "oak_slab",
       "meta": "slab",
       "texture": "planks_oak",
-      "full_block": "planks_oak"
+      "full_block": "planks_oak",
+      "other_part": "oak_slab_top"
     },
-    "cobblestone_slab": {
+    "oak_slab_top": {
+      "id": 54,
+      "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"
+    },
+    "cobblestone_slab_bottom": {
       "id": 55,
       "top": 8,
+      "sprite_top": 8,
       "hp": 600,
       "transparent": true,
+      "meta": "slab",
       "drop": "cobblestone_slab",
-      "texture": "cobblestone"
+      "texture": "cobblestone",
+      "full_block": "cobblestone",
+      "other_part": "cobblestone_slab_top"
     },
-    "brick_slab": {
+    "cobblestone_slab_top": {
+      "id": 55,
+      "bottom": 8,
+      "sprite_bottom": 8,
+      "hp": 600,
+      "transparent": true,
+      "meta": "slab",
+      "drop": "cobblestone_slab",
+      "texture": "cobblestone",
+      "full_block": "cobblestone",
+      "other_part": "cobblestone_slab_bottom"
+    },
+    "brick_slab_bottom": {
       "id": 56,
       "top": 8,
       "sprite_top": 8,
       "drop": "brick_slab",
       "meta": "slab",
       "texture": "bricks",
-      "full_block": "bricks"
+      "full_block": "bricks",
+      "other_part": "brick_slab_top"
+    },
+    "brick_slab_top": {
+      "id": 56,
+      "bottom": 8,
+      "sprite_bottom": 8,
+      "hp": 600,
+      "transparent": true,
+      "drop": "brick_slab",
+      "meta": "slab",
+      "texture": "bricks",
+      "full_block": "bricks",
+      "other_part": "brick_slab_bottom"
     },
     "stonebrick": {
       "id": 57,
       "drop": "stonebrick",
       "texture": "stonebrick"
     },
-    "stonebrick_slab": {
+    "stonebrick_slab_bottom": {
       "id": 58,
       "top": 8,
       "sprite_top": 8,
       "drop": "stonebrick_slab",
       "meta": "slab",
       "texture": "stonebrick",
-      "full_block": "stonebrick"
+      "full_block": "stonebrick",
+      "other_part": "stonebrick_slab_top"
+    },
+    "stonebrick_slab_top": {
+      "id": 58,
+      "bottom": 8,
+      "sprite_bottom": 8,
+      "hp": 450,
+      "transparent": true,
+      "drop": "stonebrick_slab",
+      "meta": "slab",
+      "texture": "stonebrick",
+      "full_block": "stonebrick",
+      "other_part": "brick_slab_bottom"
     },
     "cactus": {
       "id": 59,
     "stone_slab": {
       "id": 47,
       "name": "Stone Slab",
-      "type": "block",
-      "texture": "stone_slab"
+      "type": "slab",
+      "texture": "stone_slab",
+      "top_slab_block": "stone_slab_top",
+      "bottom_slab_block": "stone_slab_bottom"
     },
     "sandstone_slab": {
       "id": 48,
       "name": "Sandstone Slab",
-      "type": "block",
-      "texture": "sandstone_slab"
+      "type": "slab",
+      "texture": "sandstone_slab",
+      "top_slab_block": "sandstone_slab_top",
+      "bottom_slab_block": "sandstone_slab_bottom"
     },
     "oak_slab": {
       "id": 49,
       "name": "Oak Slab",
-      "type": "block",
-      "texture": "oak_slab"
+      "type": "slab",
+      "texture": "oak_slab",
+      "top_slab_block": "oak_slab_top",
+      "bottom_slab_block": "oak_slab_bottom"
     },
     "cobblestone_slab": {
       "id": 50,
       "name": "Cobblestone Slab",
-      "type": "block",
-      "texture": "cobblestone_slab"
+      "type": "slab",
+      "texture": "cobblestone_slab",
+      "top_slab_block": "cobblestone_slab_top",
+      "bottom_slab_block": "cobblestone_slab_bottom"
     },
     "brick_slab": {
       "id": 51,
       "name": "Brick Slab",
-      "type": "block",
-      "texture": "brick_slab"
+      "type": "slab",
+      "texture": "brick_slab",
+      "top_slab_block": "brick_slab_top",
+      "bottom_slab_block": "brick_slab_bottom"
     },
     "stonebrick": {
       "id": 52,
     "stonebrick_slab": {
       "id": 53,
       "name": "Stone Brick Slab",
-      "type": "block",
-      "texture": "stonebrick_slab"
+      "type": "slab",
+      "texture": "stonebrick_slab",
+      "top_slab_block": "stonebrick_slab_top",
+      "bottom_slab_block": "stonebrick_slab_bottom"
     },
     "cactus": {
       "id": 54,
index 9254986879a7c3fed8678fee60fdd9214f800f9d..01507d46bb75f6686dfa81eb625ab4f0efce6185 100644 (file)
       "h": 26
     }
   },
-  "break": {
-    "break_0": {
-      "w": 16,
-      "h": 16
-    },
-    "break_1": {
-      "x": 16,
-      "w": 16,
-      "h": 16
-    },
-    "break_2": {
-      "x": 32,
-      "w": 16,
-      "h": 16
-    },
-    "break_3": {
-      "x": 48,
-      "w": 16,
-      "h": 16
-    },
-    "break_4": {
-      "x": 64,
-      "w": 16,
-      "h": 16
-    },
-    "break_5": {
-      "x": 80,
-      "w": 16,
-      "h": 16
-    },
-    "break_6": {
-      "x": 96,
-      "w": 16,
-      "h": 16
-    },
-    "break_7": {
-      "x": 112,
-      "w": 16,
-      "h": 16
-    },
-    "break_8": {
-      "x": 128,
-      "w": 16,
-      "h": 16
-    },
-    "break_9": {
-      "x": 144,
-      "w": 16,
-      "h": 16
-    }
-  },
   "allitems": {
     "creative": {
       "w": 176,
index 075af6f4f39cf707ad9bd48f78c6f21f8fc66b06..7a7ccabb3651cc27b61996a67da991d9ee3f50db 100644 (file)
@@ -49,7 +49,18 @@ class GameItemsHolder @Inject constructor(
         }
 
         dtoMap.forEach { (key, dto) ->
-            itemsMap[key] = itemMapper.map(key, dto, blocksMap[key])
+            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]
index 23f711afcc2c6d35dc1ebb79edef0fff622aa1f2..f2c5a2362c9329e086d598e2fbe8afa0a3d3079f 100644 (file)
@@ -8,6 +8,7 @@ import ru.deadsoftware.cavedroid.game.GameScope
 import ru.deadsoftware.cavedroid.game.actions.placeblock.IPlaceBlockAction
 import ru.deadsoftware.cavedroid.game.actions.placeblock.PlaceBlockItemToBackgroundAction
 import ru.deadsoftware.cavedroid.game.actions.placeblock.PlaceBlockItemToForegroundAction
+import ru.deadsoftware.cavedroid.game.actions.placeblock.PlaceSlabAction
 
 @Module
 class PlaceBlockActionsModule {
@@ -28,4 +29,12 @@ class PlaceBlockActionsModule {
         return action
     }
 
+    @Binds
+    @IntoMap
+    @StringKey(PlaceSlabAction.ACTION_KEY)
+    @GameScope
+    fun bindPlaceSlabAction(action: PlaceSlabAction): IPlaceBlockAction {
+        return action
+    }
+
 }
\ No newline at end of file
index 1f74448ab60dfa012777121d6338e9787593fe9d..d409a803d38efffd2b815cf0536ca41c2caffeb6 100644 (file)
@@ -8,10 +8,15 @@ import javax.inject.Inject
 @GameScope
 class PlaceBlockItemToForegroundAction @Inject constructor(
     private val gameWorld: GameWorld,
+    private val placeSlabAction: PlaceSlabAction,
 ) : IPlaceBlockAction {
 
     override fun place(placeable: Item.Placeable, x: Int, y: Int) {
-        gameWorld.placeToForeground(x, y, placeable.block)
+        if (placeable.isSlab()) {
+            placeSlabAction.place(placeable, x, y)
+        } else {
+            gameWorld.placeToForeground(x, y, placeable.block)
+        }
     }
 
     companion object {
diff --git a/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceSlabAction.kt b/core/src/ru/deadsoftware/cavedroid/game/actions/placeblock/PlaceSlabAction.kt
new file mode 100644 (file)
index 0000000..079d225
--- /dev/null
@@ -0,0 +1,33 @@
+package ru.deadsoftware.cavedroid.game.actions.placeblock
+
+import com.badlogic.gdx.Gdx
+import ru.deadsoftware.cavedroid.game.GameItemsHolder
+import ru.deadsoftware.cavedroid.game.GameScope
+import ru.deadsoftware.cavedroid.game.mobs.MobsController
+import ru.deadsoftware.cavedroid.game.model.item.Item
+import ru.deadsoftware.cavedroid.game.world.GameWorld
+import javax.inject.Inject
+import kotlin.random.Random
+
+@GameScope
+class PlaceSlabAction @Inject constructor(
+    private val gameWorld: GameWorld,
+    private val mobsController: MobsController,
+    private val gameItemsHolder: GameItemsHolder,
+) : IPlaceBlockAction {
+
+    override fun place(placeable: Item.Placeable, x: Int, y: Int) {
+        if (placeable !is Item.Slab) {
+            Gdx.app.debug(TAG, "Place slab action called on ${placeable.params.key} which is not a slab")
+            return
+        }
+
+        val slabPart = if (Random.nextBoolean()) placeable.topPartBlock else placeable.bottomPartBlock
+        gameWorld.placeToForeground(x, y, slabPart)
+    }
+
+    companion object {
+        private const val TAG = "PlaceSlabAction"
+        const val ACTION_KEY = "place_slab"
+    }
+}
\ No newline at end of file
index 4a0c31a4a4fa7e7038284aa5b3b9776412c99c03..9669a5378c5f84dbafd7be4a58bc801522f2a3d7 100644 (file)
@@ -16,8 +16,8 @@ sealed class Block {
     val width: Float get() = 16f - params.collisionMargins.left - params.collisionMargins.right
     val height: Float get() = 16f - params.collisionMargins.top - params.collisionMargins.bottom
 
-    private val spriteWidth: Float get() = 16f - params.spriteMargins.left - params.spriteMargins.right
-    private val spriteHeight: Float get() = 16f - params.spriteMargins.top - params.spriteMargins.bottom
+    val spriteWidth: Float get() = 16f - params.spriteMargins.left - params.spriteMargins.right
+    val spriteHeight: Float get() = 16f - params.spriteMargins.top - params.spriteMargins.bottom
 
     private var animation: Array<Sprite>? = null
 
@@ -129,6 +129,7 @@ sealed class Block {
     data class Slab(
         override val params: CommonBlockParams,
         val fullBlockKey: String,
+        val otherPartBlockKey: String,
     ): Block()
 
     sealed class Fluid: Block() {
index ae1bfba8d768cc9cfe03cf195060f27044cdcc00..e30ae5286f8f183a4c231bb052291cb93de82caf 100644 (file)
@@ -27,4 +27,5 @@ data class BlockDto(
     @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,
 )
index d46edddc0a2b686ce96e044ebaace2140e829068..4d57886372db315bea5d5fa884099c90c0e0aca8 100644 (file)
@@ -14,4 +14,6 @@ data class ItemDto(
     @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,
 )
index 80a96251442ebd6bafb924b22044d4b24df703eb..b56f068892af48aebac980418468edc02d51196d 100644 (file)
@@ -1,8 +1,8 @@
 package ru.deadsoftware.cavedroid.game.model.item
 
-import com.badlogic.gdx.Gdx
 import com.badlogic.gdx.graphics.g2d.Sprite
 import ru.deadsoftware.cavedroid.game.model.block.Block
+import ru.deadsoftware.cavedroid.game.model.block.Block as BlockModel
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 
@@ -30,6 +30,12 @@ sealed class Item {
         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
@@ -54,6 +60,11 @@ sealed class Item {
         abstract val useActionKey: String
     }
 
+    sealed class Placeable : Item() {
+        abstract val block: BlockModel
+        override val sprite: Sprite get() = block.sprite
+    }
+
     data class None(
         override val params: CommonItemParams,
     ): Item() {
@@ -61,11 +72,17 @@ sealed class Item {
             get() = throw IllegalAccessException("Trying to get sprite of None")
     }
 
-    data class Placeable(
+    data class Block(
         override val params: CommonItemParams,
-        val block: Block
-    ) : Item() {
-        override val sprite: Sprite get() = block.sprite
+        override val block: BlockModel
+    ) : Placeable()
+
+    data class Slab(
+        override val params: CommonItemParams,
+        val topPartBlock: BlockModel.Slab,
+        val bottomPartBlock: BlockModel.Slab
+    ) : Placeable() {
+        override val block get() = bottomPartBlock
     }
     
     data class Sword(
index bc2978320d8f2d062889f4c6390a139545f0e029..a122754caad73e972a6cb20f7155c6b76f02a9fd 100644 (file)
@@ -18,7 +18,7 @@ class BlockMapper @Inject constructor() {
         return when (dto.meta) {
             "water" -> Water(commonBlockParams, requireNotNull(dto.state))
             "lava" -> Lava(commonBlockParams, requireNotNull(dto.state))
-            "slab" -> Slab(commonBlockParams, requireNotNull(dto.fullBlock))
+            "slab" -> Slab(commonBlockParams, requireNotNull(dto.fullBlock), requireNotNull(dto.otherPart))
             "none" -> None(commonBlockParams)
             else -> Normal(commonBlockParams)
         }
index ad79cbd824e01f6b0308f3d04e190f9952f16f29..7646e22d70b756cb07082b2f6425b3eff03a0659 100644 (file)
@@ -15,14 +15,15 @@ import javax.inject.Inject
 @Reusable
 class ItemMapper @Inject constructor() {
 
-    fun map(key: String, dto: ItemDto, block: Block?): Item {
+    fun map(key: String, dto: ItemDto, block: Block?, slabTopBlock: Block.Slab?, slabBottomBlock: Block.Slab?): Item {
         val params = mapCommonParams(key, dto)
 
         return when (dto.type) {
             "bucket" -> Bucket(params, requireNotNull(loadSprite(dto)), requireNotNull(dto.actionKey))
             "shovel" -> Shovel(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier)
             "sword" -> Sword(params, requireNotNull(loadSprite(dto)), dto.mobDamageMultiplier, dto.blockDamageMultiplier)
-            "block" -> Placeable(params, requireNotNull(block))
+            "block" -> Block(params, requireNotNull(block))
+            "slab" -> Slab(params, requireNotNull(slabTopBlock), requireNotNull(slabBottomBlock))
             "none" -> None(params)
             else -> throw IllegalArgumentException("Unknown item type ${dto.type}")
         }
index ae243a374ea182a7dce46be95e041661ada6f76f..60a9ef80b1e03a074888379e89565f8216e186e2 100644 (file)
@@ -1,11 +1,11 @@
 package ru.deadsoftware.cavedroid.game.render
 
+import com.badlogic.gdx.graphics.g2d.Sprite
 import com.badlogic.gdx.graphics.g2d.SpriteBatch
 import com.badlogic.gdx.graphics.g2d.TextureRegion
 import com.badlogic.gdx.graphics.glutils.ShapeRenderer
 import com.badlogic.gdx.math.MathUtils
 import com.badlogic.gdx.math.Rectangle
-import ru.deadsoftware.cavedroid.game.GameInput
 import ru.deadsoftware.cavedroid.game.mobs.MobsController
 import ru.deadsoftware.cavedroid.game.model.block.Block
 import ru.deadsoftware.cavedroid.game.world.GameWorld
@@ -22,12 +22,11 @@ abstract class BlocksRenderer(
     private val Block.canSeeThrough
         get() = isNone() || params.isTransparent
 
-    private fun blockDamageTexture(index: Int): TextureRegion? {
+    private fun blockDamageSprite(index: Int): Sprite? {
         if (index !in 0..MAX_BLOCK_DAMAGE_INDEX) {
             return null
         }
-        val textureKey = "$BLOCK_DAMAGE_TEXTURE_PREFIX$index"
-        return Assets.textureRegions[textureKey]
+        return Assets.blockDamageSprites[index]
     }
 
     protected fun drawBlockDamage(spriteBatch: SpriteBatch, viewport: Rectangle) {
@@ -44,10 +43,16 @@ abstract class BlocksRenderer(
 
         val index = (MAX_BLOCK_DAMAGE_INDEX.toFloat() * (blockDamage.toFloat() / block.params.hitPoints.toFloat()))
             .let(MathUtils::floor)
-        val texture = blockDamageTexture(index) ?: return
+        val sprite = blockDamageSprite(index) ?: return
 
         if (gameWorld.hasForeAt(cursorX, cursorY) != background) {
-            spriteBatch.draw(texture, cursorX.px - viewport.x, cursorY.px - viewport.y)
+            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)
         }
     }
 
@@ -97,7 +102,6 @@ abstract class BlocksRenderer(
     }
 
     companion object {
-        private const val BLOCK_DAMAGE_TEXTURE_PREFIX = "break_"
         private const val MAX_BLOCK_DAMAGE_INDEX = 10
     }
 
index 91793c845a23406745876c1fc0cad8ffb013a615..32fef769bed2b89c4e1d71e55d68c2eb37cd04eb 100644 (file)
@@ -114,6 +114,15 @@ public class GameWorld {
         }
     }
 
+    private boolean isSameSlab(Block slab1, Block slab2) {
+        if (!(slab1 instanceof Block.Slab) || !(slab2 instanceof Block.Slab)) {
+            return false;
+        }
+
+        return slab1.getParams().getKey().equals(((Block.Slab) slab2).getOtherPartBlockKey())
+                || slab1.getParams().getKey().equals(slab2.getParams().getKey());
+    }
+
     public boolean hasForeAt(int x, int y) {
         return getMap(x, y, 0) != mGameItemsHolder.getFallbackBlock();
     }
@@ -145,7 +154,7 @@ public class GameWorld {
     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 (value instanceof Block.Slab && getForeMap(x, y) == value) {
+        } else if (value instanceof Block.Slab && isSameSlab(value, getForeMap(x, y))) {
             setForeMap(x, y, mGameItemsHolder.getBlock(((Block.Slab) value).getFullBlockKey()));
         }
     }
index a6318d71e75cb86c5788be83b1937a5f89c90cbf..abec0a0b2f5f0a4ecac1a6128b1a3f622445f1a1 100644 (file)
@@ -19,12 +19,17 @@ import java.util.Map;
 
 public class Assets {
 
+    private static final int BLOCK_DAMAGE_STAGES = 10;
+
     public static final JsonReader jsonReader = new JsonReader();
 
     private static final List<Texture> loadedTextures = new LinkedList<>();
 
     public static final Sprite[][] playerSprite = new Sprite[2][4];
     public static final Sprite[][] pigSprite = new Sprite[2][2];
+
+    public static final Sprite[] blockDamageSprites = new Sprite[10];
+
     public static final HashMap<String, TextureRegion> textureRegions = new HashMap<>();
     public static final ArrayMap<String, TouchButton> guiMap = new ArrayMap<>();
     private static final GlyphLayout glyphLayout = new GlyphLayout();
@@ -71,6 +76,13 @@ public class Assets {
         }
     }
 
+    private static void loadBlockDamage(AssetLoader assetLoader) {
+        final Texture blockDamageTexture = loadTexture(assetLoader.getAssetHandle("break.png"));
+        for (int i = 0; i < BLOCK_DAMAGE_STAGES; i++) {
+            blockDamageSprites[i] = new Sprite(flippedRegion(blockDamageTexture, i * 16, 0, 16, 16));
+        }
+    }
+
     private static void setPlayerHeadOrigin() {
         for (Sprite[] sprites : playerSprite) {
             sprites[0].setOrigin(sprites[0].getWidth() / 2, sprites[0].getHeight());
@@ -119,6 +131,7 @@ public class Assets {
     public static void load(final AssetLoader assetLoader) {
         loadMob(assetLoader, playerSprite, "char");
         loadMob(assetLoader, pigSprite, "pig");
+        loadBlockDamage(assetLoader);
         loadJSON(assetLoader);
         loadBlocks(assetLoader);
         loadItems(assetLoader);