Exit Full View

Cavern Quest 2 / src / commonMain / kotlin / Player.kt

import uk.co.nickthecoder.glok.event.Key
import uk.co.nickthecoder.glok.property.boilerplate.intProperty
import uk.co.nickthecoder.kyd.Actor
import uk.co.nickthecoder.kyd.Ticks
import uk.co.nickthecoder.kyd.appearance.*
import uk.co.nickthecoder.kyd.nodes.PlayActView

/**
 * Checks for arrow keys, and moves in that direction.
 * If the path is clear, we can walk freely.
 *
 * If the path is blocked by [Soil], we use up a [blasts] to destroy the soil.
 *
 * We can also collect [Fuel] and [Dynamite]. We can also enter our [Ship], but only if we
 * have collected [Fuel].
 *
 * We cannot move over any other objects (including [Monster]s.
 */
class Player : Item(), Killable {

    /**
     * See [PlayDirector.blastsProperty]
     */
    val blastsProperty by intProperty(0)
    var blasts by blastsProperty

    private var idle = true

    private lateinit var idleAnimation: FlipBook
    private lateinit var walkingAnimation: FlipBook
    private lateinit var blastingAnimation: FlipBook

    override val isEmpty get() = true

    override fun onEnteredStage(actor: Actor) {
        super.onEnteredStage(actor)

        PlayDirector.instance.registerPlayer(actor, this)

        val role = actor.role ?: return
        val a = role.findAppearance("a")
        val b = role.findAppearance("b")
        idleAnimation = flipBook {
            page(a, playerIdleFrameTime)
            page(b, playerIdleFrameTime)
        }
        walkingAnimation = flipBook {
            repetitions = 1
            page(a, playerWalkTime)
            onFinished {
                idle = true
                idleAnimation.applyToActor(actor)
            }
        }
        blastingAnimation = flipBook { // The same timing as Soil's blast animation.
            repetitions = 3
            val pageCount = 2
            page(a, soilBlastTime / repetitions / pageCount)
            page(b, soilBlastTime / repetitions / pageCount)
            onFinished {
                idle = true
                idleAnimation.applyToActor(actor)
            }
        }
        idleAnimation.applyToActor(actor)
    }

    override fun tick(actor: Actor, seconds: Float) {
        // Tick the animation if there is one.
        (actor.appearance as? Ticks)?.tick(actor, seconds)

        if (idle) {
            val view = PlayActView.instance

            var dx = 0
            var dy = 0
            if (view.isKeyDown(Key.LEFT)) {
                dx = - 1
            } else if (view.isKeyDown(Key.RIGHT)) {
                dx = 1
            } else if (view.isKeyDown(Key.UP)) {
                dy = 1
            } else if (view.isKeyDown(Key.DOWN)) {
                dy = - 1
            }
            if (dx != 0 || dy != 0) {
                // Have we collected something (Fuel or Dynamite).
                val otherActor = look(dx, dy)
                (otherActor?.behaviour as? Collectable)?.collect(otherActor)

                (otherActor?.behaviour as? Ship)?.let { ship ->
                    if (PlayDirector.instance.fueled) {
                        ship.blastOff(otherActor)
                        PlayDirector.instance.blastOff(otherActor)
                        actor.parent.removeActor(actor)
                        return
                    }
                }

                if (move(actor, dx, dy)) {
                    walking(actor)
                } else {
                    tryBlast(actor, dx, dy)
                }
            }
        }
    }

    /**
     * Oh dear, we've been hit by a [Rock], or eaten by a [Monster]
     */
    override fun killedBy(killer: Actor, victim: Actor) {
        victim.parent.removeActor(victim)
        PlayDirector.instance.playerDied()
    }

    private fun tryBlast(actor: Actor, dx: Int, dy: Int): Boolean {
        if (blasts > 0) {
            val otherActor = look(dx, dy)
            if (otherActor.isSoil()) {
                (otherActor?.behaviour as? Soil)?.let { soil ->
                    soil.blast(otherActor)
                    blasts --
                    idle = false
                    blastingAnimation.applyToActor(actor)
                    return true
                }
            }
        }
        return false
    }

    private fun walking(actor: Actor) {
        idle = false
        walkingAnimation.applyToActor(actor)
    }

}