You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
4.4 KiB
139 lines
4.4 KiB
package wow.doge.mygame.game.entities.player
|
|
|
|
import scala.concurrent.duration._
|
|
|
|
import akka.util.Timeout
|
|
import cats.syntax.eq._
|
|
import monix.eval.Fiber
|
|
import monix.eval.Task
|
|
import monix.reactive.Observable
|
|
import monix.{eval => me}
|
|
import wow.doge.mygame.EnumActionEvent
|
|
import wow.doge.mygame.game.entities.CharacterStats
|
|
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput
|
|
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.Jump
|
|
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkBackward
|
|
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkForward
|
|
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkLeft
|
|
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkRight
|
|
import wow.doge.mygame.implicits._
|
|
import wow.doge.mygame.types.AkkaScheduler
|
|
import wow.doge.mygame.utils.MovementDirection
|
|
import io.odin.Logger
|
|
|
|
class PlayerMovementReducer(
|
|
val playerActor: PlayerActor.Ref,
|
|
logger: Logger[monix.bio.Task]
|
|
)(implicit
|
|
akkaSched: AkkaScheduler
|
|
) {
|
|
import PlayerMovementReducer._
|
|
import com.softwaremill.quicklens._
|
|
|
|
implicit val timeout = Timeout(1.second)
|
|
implicit val sched = akkaSched.value
|
|
|
|
def staminaTimer(multiplier: Int) =
|
|
Task.deferAction(implicit s =>
|
|
Observable
|
|
.interval(250.millis)
|
|
.doOnNextF(_ => logger.trace("Sending Stamina Consume Item"))
|
|
.mapEvalF(_ =>
|
|
playerActor
|
|
.askL(
|
|
PlayerActor
|
|
.ConsumeStamina(CharacterStats.DamageStamina(1 * multiplier), _)
|
|
)
|
|
)
|
|
.doOnNext(stats =>
|
|
if (stats.stamina.toInt === 0)
|
|
Task(playerActor ! PlayerActor.StopMoving)
|
|
else Task.unit
|
|
)
|
|
.takeWhile(_.stamina.toInt >= 0)
|
|
.completedL
|
|
)
|
|
|
|
def staminaRegenTimer(multiplier: Int) =
|
|
Task.deferAction(implicit s =>
|
|
Observable
|
|
.interval(500.millis)
|
|
.doOnNextF(_ => logger.trace("Sending Stamina Regen Item"))
|
|
.mapEvalF(_ =>
|
|
playerActor.askL(
|
|
PlayerActor
|
|
.HealStamina(CharacterStats.HealStamina(1 * multiplier), _)
|
|
)
|
|
)
|
|
.takeWhile(_.stamina.toInt =!= 100)
|
|
.delayExecution(1.second)
|
|
.completedL
|
|
)
|
|
|
|
def handleStamina(
|
|
state: State,
|
|
pressed: Boolean,
|
|
consumptionMultiplier: Int,
|
|
regenMultiplier: Int
|
|
): Task[State] =
|
|
state.staminaRegenTimer.cancel >>
|
|
(if (pressed) {
|
|
val nextState1 =
|
|
if (state.keysPressed === 0)
|
|
staminaTimer(consumptionMultiplier).start.map(
|
|
state.modify(_.staminaTimer).setTo
|
|
)
|
|
else Task.pure(state)
|
|
val nextState2 =
|
|
nextState1.map(_.modify(_.keysPressed).using(_ + 1))
|
|
|
|
nextState2
|
|
} else {
|
|
val nextState1 = state
|
|
.modify(_.keysPressed)
|
|
.using(_ - 1)
|
|
if (nextState1.keysPressed === 0)
|
|
nextState1.staminaTimer.cancel >>
|
|
staminaRegenTimer(regenMultiplier).start.map(
|
|
nextState1.modify(_.staminaRegenTimer).setTo
|
|
)
|
|
else
|
|
Task.pure(nextState1)
|
|
})
|
|
|
|
def value(
|
|
state: State,
|
|
action: EnumActionEvent[PlayerMovementInput]
|
|
): Task[State] =
|
|
action match {
|
|
case EnumActionEvent(WalkForward, pressed, tpf) =>
|
|
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Forward)
|
|
handleStamina(state, pressed, 1, 1)
|
|
case EnumActionEvent(WalkRight, pressed, tpf) =>
|
|
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Right)
|
|
handleStamina(state, pressed, 1, 1)
|
|
case EnumActionEvent(WalkLeft, pressed, tpf) =>
|
|
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Left)
|
|
handleStamina(state, pressed, 1, 1)
|
|
case EnumActionEvent(WalkBackward, pressed, tpf) =>
|
|
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Backward)
|
|
handleStamina(state, pressed, 1, 1)
|
|
case EnumActionEvent(Jump, pressed, tpf) =>
|
|
if (pressed) playerActor ! PlayerActor.Jump else ()
|
|
handleStamina(state, pressed, 10, 1)
|
|
}
|
|
}
|
|
object PlayerMovementReducer {
|
|
final case class State(
|
|
keysPressed: Int,
|
|
staminaTimer: Fiber[Unit],
|
|
staminaRegenTimer: Fiber[Unit]
|
|
)
|
|
object State {
|
|
val empty = State(
|
|
0,
|
|
me.Fiber(me.Task.unit, me.Task.unit),
|
|
me.Fiber(me.Task.unit, me.Task.unit)
|
|
)
|
|
}
|
|
}
|