DEADSOFTWARE

CaveGame in kotlin
[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 ru.deadsoftware.cavedroid.misc.utils.colorFromHexString
9 import kotlin.contracts.ExperimentalContracts
10 import kotlin.contracts.contract
12 @OptIn(ExperimentalContracts::class)
13 sealed class Block {
15 abstract val params: CommonBlockParams
17 val width: Float get() = 16f - params.collisionMargins.left - params.collisionMargins.right
18 val height: Float get() = 16f - params.collisionMargins.top - params.collisionMargins.bottom
20 val spriteWidth: Float get() = 16f - params.spriteMargins.left - params.spriteMargins.right
21 val spriteHeight: Float get() = 16f - params.spriteMargins.top - params.spriteMargins.bottom
23 protected var animation: Array<Sprite>? = null
25 private var _sprite: Sprite? = null
26 get() {
27 return animation?.get(currentAnimationFrame) ?: field
28 }
30 open val sprite: Sprite
31 get() = requireNotNull(_sprite) { "null sprite for block '${params.key}'" }
33 private val currentAnimationFrame: Int
34 get() {
35 return params.animationInfo?.let { animInfo ->
36 ((TimeUtils.millis() / ANIMATION_FRAME_DURATION_MS) % animInfo.framesCount).toInt()
37 } ?: 0
38 }
40 override fun hashCode(): Int {
41 return params.key.hashCode()
42 }
44 override fun equals(other: Any?): Boolean {
45 return params.key == (other as Item).params.key
46 }
48 fun initialize() {
49 initAnimation()
50 initSprite()
51 }
53 private fun initAnimation() {
54 animation = params.animationInfo?.let { animInfo ->
55 requireNotNull(params.texture) { "Cannot derive animation frames from null sprite" }
56 Array(animInfo.framesCount) { y ->
57 val width = 16 - params.spriteMargins.left - params.spriteMargins.right
58 val height = 16 - params.spriteMargins.top - params.spriteMargins.bottom
59 Sprite(params.texture, params.spriteMargins.left, 16 * y + params.spriteMargins.top, width, height)
60 .apply {
61 flip(false, true)
62 params.tint?.let { tint -> color = colorFromHexString(tint) }
63 }
64 }
65 }
66 }
68 private fun initSprite() {
69 _sprite = animation?.get(0) ?: params.texture?.let { tex ->
70 val width = 16 - params.spriteMargins.left - params.spriteMargins.right
71 val height = 16 - params.spriteMargins.top - params.spriteMargins.bottom
72 Sprite(tex, params.spriteMargins.left, params.spriteMargins.top, width, height)
73 .apply {
74 flip(false, true)
75 params.tint?.let { tint -> color = colorFromHexString(tint) }
76 }
77 }
78 }
80 fun requireSprite() = requireNotNull(sprite)
82 fun draw(spriter: SpriteBatch, x: Float, y: Float) {
83 sprite.apply {
84 setBounds(
85 /* x = */ x + params.spriteMargins.left,
86 /* y = */ y + params.spriteMargins.top,
87 /* width = */ spriteWidth,
88 /* height = */ spriteHeight
89 )
90 draw(spriter)
91 }
92 }
94 fun isFluid(): Boolean {
95 contract { returns(true) implies (this@Block is Fluid) }
96 return this is Fluid
97 }
99 fun isWater(): Boolean {
100 contract { returns(true) implies (this@Block is Water) }
101 return this is Water
104 fun isLava(): Boolean {
105 contract { returns(true) implies (this@Block is Lava) }
106 return this is Lava
109 fun isSlab(): Boolean {
110 contract { returns(true) implies (this@Block is Slab) }
111 return this is Slab
114 fun isContainer(): Boolean {
115 contract { returns(true) implies (this@Block is Container) }
116 return this is Container
119 fun isFurnace(): Boolean {
120 contract { returns(true) implies (this@Block is Furnace) }
121 return this is Furnace
124 fun isChest(): Boolean {
125 contract { returns(true) implies (this@Block is Chest) }
126 return this is Chest
129 fun isNone(): Boolean {
130 contract { returns(true) implies (this@Block is None) }
131 return this is None
134 fun getRectangle(x: Int, y: Int): Rectangle {
135 return Rectangle(
136 /* x = */ x * 16f + params.collisionMargins.left,
137 /* y = */ y * 16f + params.collisionMargins.top,
138 /* width = */ width,
139 /* height = */ height
143 sealed class Container() : Block()
145 data class None(
146 override val params: CommonBlockParams
147 ) : Block()
149 data class Normal(
150 override val params: CommonBlockParams,
151 ) : Block()
153 data class Furnace(
154 override val params: CommonBlockParams,
155 ): Container() {
157 override val sprite: Sprite
158 get() = getSprite(false)
160 private fun getSprite(isActive: Boolean): Sprite {
161 return animation?.let { animation ->
162 if (isActive) {
163 animation[1]
164 } else {
165 animation[0]
167 } ?: sprite
170 fun draw(spriter: SpriteBatch, x: Float, y: Float, isActive: Boolean) {
171 getSprite(isActive).apply {
172 setBounds(
173 /* x = */ x + params.spriteMargins.left,
174 /* y = */ y + params.spriteMargins.top,
175 /* width = */ spriteWidth,
176 /* height = */ spriteHeight
178 draw(spriter)
184 data class Chest(
185 override val params: CommonBlockParams
186 ): Container()
188 data class Slab(
189 override val params: CommonBlockParams,
190 val fullBlockKey: String,
191 val otherPartBlockKey: String,
192 ): Block()
194 sealed class Fluid: Block() {
195 abstract val state: Int
198 data class Water(
199 override val params: CommonBlockParams,
200 override val state: Int,
201 ) : Fluid()
203 data class Lava(
204 override val params: CommonBlockParams,
205 override val state: Int,
206 ) : Fluid()
208 /* Legacy accessors below */
210 // collision margins
211 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val left: Int get() = params.collisionMargins.left
212 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val right: Int get() = params.collisionMargins.left
213 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val top: Int get() = params.collisionMargins.left
214 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val bottom: Int get() = params.collisionMargins.left
216 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val hp: Int get() = params.hitPoints
217 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val collision: Boolean get() = params.hasCollision
218 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val animated: Boolean get() = params.animationInfo != null
219 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val frames: Int get() = params.animationInfo?.framesCount ?: 0
220 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val drop: String get() = params.dropInfo?.itemKey ?: "none"
221 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun hasDrop() = params.dropInfo != null
222 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun toJump() = params.hasCollision && params.collisionMargins.top < 8
223 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun hasCollision() = params.hasCollision
224 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun isBackground() = params.isBackground
225 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun isTransparent() = params.isTransparent
226 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun getTexture() = sprite
228 companion object {
229 private const val LEGACY_ACCESSOR_DEPRECATION = "legacy accessors will be removed"
230 private const val ANIMATION_FRAME_DURATION_MS = 100L