DEADSOFTWARE

Prettier world + ores
[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 private var animation: Array<Sprite>? = null
25 private var _sprite: Sprite? = null
26 get() {
27 return animation?.get(currentAnimationFrame) ?: field
28 }
30 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 isNone(): Boolean {
115 contract { returns(true) implies (this@Block is None) }
116 return this is None
119 fun getRectangle(x: Int, y: Int): Rectangle {
120 return Rectangle(
121 /* x = */ x * 16f + params.collisionMargins.left,
122 /* y = */ y * 16f + params.collisionMargins.top,
123 /* width = */ width,
124 /* height = */ height
128 data class None(
129 override val params: CommonBlockParams
130 ) : Block()
132 data class Normal(
133 override val params: CommonBlockParams,
134 ) : Block()
136 data class Slab(
137 override val params: CommonBlockParams,
138 val fullBlockKey: String,
139 val otherPartBlockKey: String,
140 ): Block()
142 sealed class Fluid: Block() {
143 abstract val state: Int
146 data class Water(
147 override val params: CommonBlockParams,
148 override val state: Int,
149 ) : Fluid()
151 data class Lava(
152 override val params: CommonBlockParams,
153 override val state: Int,
154 ) : Fluid()
156 /* Legacy accessors below */
158 // collision margins
159 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val left: Int get() = params.collisionMargins.left
160 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val right: Int get() = params.collisionMargins.left
161 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val top: Int get() = params.collisionMargins.left
162 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val bottom: Int get() = params.collisionMargins.left
164 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val hp: Int get() = params.hitPoints
165 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val collision: Boolean get() = params.hasCollision
166 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val animated: Boolean get() = params.animationInfo != null
167 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val frames: Int get() = params.animationInfo?.framesCount ?: 0
168 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) val drop: String get() = params.dropInfo?.itemKey ?: "none"
169 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun hasDrop() = params.dropInfo != null
170 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun toJump() = params.hasCollision && params.collisionMargins.top < 8
171 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun hasCollision() = params.hasCollision
172 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun isBackground() = params.isBackground
173 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun isTransparent() = params.isTransparent
174 @Deprecated(LEGACY_ACCESSOR_DEPRECATION) fun getTexture() = sprite
176 companion object {
177 private const val LEGACY_ACCESSOR_DEPRECATION = "legacy accessors will be removed"
178 private const val ANIMATION_FRAME_DURATION_MS = 100L