DEADSOFTWARE

Add top slabs
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / model / block / Block.kt
1 package ru.deadsoftware.cavedroid.game.model.block
3 import com.badlogic.gdx.graphics.g2d.Sprite
4 import com.badlogic.gdx.graphics.g2d.SpriteBatch
5 import com.badlogic.gdx.math.Rectangle
6 import com.badlogic.gdx.utils.TimeUtils
7 import ru.deadsoftware.cavedroid.game.model.item.Item
8 import kotlin.contracts.ExperimentalContracts
9 import kotlin.contracts.contract
11 @OptIn(ExperimentalContracts::class)
12 sealed class Block {
14 abstract val params: CommonBlockParams
16 val width: Float get() = 16f - params.collisionMargins.left - params.collisionMargins.right
17 val height: Float get() = 16f - params.collisionMargins.top - params.collisionMargins.bottom
19 val spriteWidth: Float get() = 16f - params.spriteMargins.left - params.spriteMargins.right
20 val spriteHeight: Float get() = 16f - params.spriteMargins.top - params.spriteMargins.bottom
22 private var animation: Array<Sprite>? = null
24 private var _sprite: Sprite? = null
25 get() {
26 return animation?.get(currentAnimationFrame) ?: field
27 }
29 val sprite: Sprite
30 get() = requireNotNull(_sprite) { "null sprite for block '${params.key}'" }
32 private val currentAnimationFrame: Int
33 get() {
34 return params.animationInfo?.let { animInfo ->
35 ((TimeUtils.millis() / ANIMATION_FRAME_DURATION_MS) % animInfo.framesCount).toInt()
36 } ?: 0
37 }
39 override fun hashCode(): Int {
40 return params.key.hashCode()
41 }
43 override fun equals(other: Any?): Boolean {
44 return params.key == (other as Item).params.key
45 }
47 fun initialize() {
48 initAnimation()
49 initSprite()
50 }
52 private fun initAnimation() {
53 animation = params.animationInfo?.let { animInfo ->
54 requireNotNull(params.texture) { "Cannot derive animation frames from null sprite" }
55 Array(animInfo.framesCount) { y ->
56 val width = 16 - params.spriteMargins.left - params.spriteMargins.right
57 val height = 16 - params.spriteMargins.top - params.spriteMargins.bottom
58 Sprite(params.texture, params.spriteMargins.left, 16 * y + params.spriteMargins.top, width, height)
59 .apply { flip(false, true) }
60 }
61 }
62 }
64 private fun initSprite() {
65 _sprite = animation?.get(0) ?: params.texture?.let { tex ->
66 val width = 16 - params.spriteMargins.left - params.spriteMargins.right
67 val height = 16 - params.spriteMargins.top - params.spriteMargins.bottom
68 Sprite(tex, params.spriteMargins.left, params.spriteMargins.top, width, height)
69 .apply { flip(false, true) }
70 }
71 }
73 fun requireSprite() = requireNotNull(sprite)
75 fun draw(spriter: SpriteBatch, x: Float, y: Float) {
76 sprite.apply {
77 setBounds(
78 /* x = */ x + params.spriteMargins.left,
79 /* y = */ y + params.spriteMargins.top,
80 /* width = */ spriteWidth,
81 /* height = */ spriteHeight
82 )
83 draw(spriter)
84 }
85 }
87 fun isFluid(): Boolean {
88 contract { returns(true) implies (this@Block is Fluid) }
89 return this is Fluid
90 }
92 fun isWater(): Boolean {
93 contract { returns(true) implies (this@Block is Water) }
94 return this is Water
95 }
97 fun isLava(): Boolean {
98 contract { returns(true) implies (this@Block is Lava) }
99 return this is Lava
102 fun isSlab(): Boolean {
103 contract { returns(true) implies (this@Block is Slab) }
104 return this is Slab
107 fun isNone(): Boolean {
108 contract { returns(true) implies (this@Block is None) }
109 return this is None
112 fun getRectangle(x: Int, y: Int): Rectangle {
113 return Rectangle(
114 /* x = */ x * 16f + params.collisionMargins.left,
115 /* y = */ y * 16f + params.collisionMargins.top,
116 /* width = */ width,
117 /* height = */ height
121 data class None(
122 override val params: CommonBlockParams
123 ) : Block()
125 data class Normal(
126 override val params: CommonBlockParams,
127 ) : Block()
129 data class Slab(
130 override val params: CommonBlockParams,
131 val fullBlockKey: String,
132 val otherPartBlockKey: String,
133 ): Block()
135 sealed class Fluid: Block() {
136 abstract val state: Int
139 data class Water(
140 override val params: CommonBlockParams,
141 override val state: Int,
142 ) : Fluid()
144 data class Lava(
145 override val params: CommonBlockParams,
146 override val state: Int,
147 ) : Fluid()
149 /* Legacy accessors below */
151 // collision margins
152 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val left: Int get() = params.collisionMargins.left
153 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val right: Int get() = params.collisionMargins.left
154 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val top: Int get() = params.collisionMargins.left
155 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val bottom: Int get() = params.collisionMargins.left
157 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val hp: Int get() = params.hitPoints
158 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val collision: Boolean get() = params.hasCollision
159 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val animated: Boolean get() = params.animationInfo != null
160 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val frames: Int get() = params.animationInfo?.framesCount ?: 0
161 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val drop: String get() = params.dropInfo?.itemKey ?: "none"
162 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun hasDrop() = params.dropInfo != null
163 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun toJump() = params.hasCollision && params.collisionMargins.top < 8
164 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun hasCollision() = params.hasCollision
165 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun isBackground() = params.isBackground
166 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun isTransparent() = params.isTransparent
167 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun getTexture() = sprite
169 companion object {
170 private const val LEGACY_ACCESSOR_DEPRECATION = "legacy accessors will be removed"
171 private const val ANIMATION_FRAME_DURATION_MS = 100L