Testing out JmonkeyEngine to make a game in Scala with Akka Actors within a pure FP layer
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.
 
 

180 lines
6.1 KiB

package wow.doge.mygame.game.entities.player.behaviors
import scala.concurrent.Future
import akka.actor.typed.Behavior
import akka.actor.typed.PostStop
import akka.actor.typed.scaladsl.AskPattern._
import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout
import cats.syntax.show._
import monix.bio.UIO
import monix.reactive.Observable
import wow.doge.mygame.game.entities.CharacterStats
import wow.doge.mygame.game.entities.StatsActor
import wow.doge.mygame.game.entities.character.CharacterStates._
import wow.doge.mygame.game.entities.player.PlayerActor
import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.movement.ImMovementActor
class IdleBehaviorFactory(
env: PlayerActor.Env,
nextStateFn: AliveSubstate => Behavior[PlayerActor.Command],
deadState: Behavior[PlayerActor.Command],
consumptionMultiplier: Int => Int
)(implicit timeout: Timeout) {
import env._
implicit val sched = ctx.system.scheduler
def behavior =
IdleBehavior(
Behaviors
.receiveMessage[PlayerActor.Command] {
case PlayerActor.HandleMovement(curStats, pressed) =>
if (curStats.stamina.toInt > 0) {
children.movementActor ! ImMovementActor.MoveLeft(pressed)
if (pressed) nextStateFn(AliveSubstate.Moving(Walking))
else nextStateFn(AliveSubstate.Idle)
} else {
children.movementActor ! ImMovementActor.MoveLeft(false)
Behaviors.same
}
case PlayerActor.MoveLeft(pressed) =>
implicit val ec = props.scheduler.value
for {
curStats <- children.statsActor.ask(StatsActor.CurrentStats)
res <- Future.successful(
ctx.self ! PlayerActor.HandleMovement(curStats, pressed)
)
} yield res
Behaviors.same
case PlayerActor.MoveRight(pressed) =>
children.movementActor ! ImMovementActor.MoveRight(pressed)
if (pressed) nextStateFn(AliveSubstate.Moving(Walking))
else nextStateFn(AliveSubstate.Idle)
case PlayerActor.MoveUp(pressed) =>
children.movementActor ! ImMovementActor.MoveUp(pressed)
if (pressed) nextStateFn(AliveSubstate.Moving(Walking))
else nextStateFn(AliveSubstate.Idle)
case PlayerActor.MoveDown(pressed) =>
children.movementActor ! ImMovementActor.MoveDown(pressed)
if (pressed) nextStateFn(AliveSubstate.Moving(Walking))
else nextStateFn(AliveSubstate.Idle)
case PlayerActor.Jump =>
children.movementActor ! ImMovementActor.Jump
ctx.self.ask(
PlayerActor.ConsumeStamina(CharacterStats.DamageStamina(10), _)
)
Behaviors.same
case PlayerActor.TakeDamage(value, replyTo) =>
implicit val ec = props.scheduler.value
for {
res <-
children.statsActor.ask(StatsActor.TakeDamageResult(value, _))
_ = ctx.self ! PlayerActor.StatsResponse(res, replyTo)
} yield ()
Behaviors.same
case PlayerActor.ConsumeStamina(value, replyTo) =>
implicit val ec = props.scheduler.value
val newValue =
CharacterStats.DamageStamina(consumptionMultiplier(value.toInt))
for {
response <- children.statsActor.ask(
StatsActor.ConsumeStaminaResult(newValue, _)
)
_ =
ctx.self ! PlayerActor
.StatsResponse(response, replyTo)
} yield ()
Behaviors.same
case PlayerActor.CurrentStats(replyTo) =>
children.statsActor ! StatsActor.CurrentStats(replyTo)
Behaviors.same
case PlayerActor.HealHealth(value, replyTo) =>
implicit val ec = props.scheduler.value
for {
response <- children.statsActor.ask(
StatsActor.HealHealthResult(value, _)
)
_ =
ctx.self ! PlayerActor
.StatsResponse(StatsActor.Status.Alive -> response, replyTo)
} yield ()
Behaviors.same
case PlayerActor.HealStamina(value, replyTo) =>
ctx.log.debugP("Received heal stamina")
implicit val ec = props.scheduler.value
for {
response <- children.statsActor.ask(
StatsActor.HealStaminaResult(value, _)
)
_ =
ctx.self ! PlayerActor
.StatsResponse(StatsActor.Status.Alive -> response, replyTo)
} yield ()
Behaviors.same
case PlayerActor.GetStatus(replyTo) =>
replyTo ! PlayerActor.Status.Alive
Behaviors.same
case PlayerActor.GetStatsObservable(replyTo) =>
import monix.{eval => me}
replyTo !
UIO(
Observable
.repeatEvalF(
me.Task.deferFuture(props.statsQueue.poll())
)
.publish(props.fxScheduler.value)
.refCount
)
Behaviors.same
case PlayerActor.StatsResponse(response, replyTo) =>
response match {
case (status, stats) =>
status match {
case StatsActor.Status.Dead => ctx.self ! PlayerActor.Die
case StatsActor.Status.Alive =>
props.statsQueue
.offer(stats)
.foreach { _ =>
pprint.log(show"Published stats $stats")
replyTo ! stats
}(props.scheduler.value)
}
}
Behaviors.same
case PlayerActor.Die => deadState
case PlayerActor.LogError(ex) =>
ctx.log.error(ex.getMessage)
Behaviors.same
}
.receiveSignal {
case (_, PostStop) =>
ctx.log.infoP("stopped")
Behaviors.same
}
)
}
final case class IdleBehavior(value: Behavior[PlayerActor.Command])