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 import wow.doge.mygame.utils.MovementDirection 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.HandleWalk(curStats, pressed, direction) => if (curStats.stamina.toInt > 0) { children.movementActor ! (direction match { case MovementDirection.Forward => ImMovementActor.MoveUp(pressed) case MovementDirection.Backward => ImMovementActor.MoveDown(pressed) case MovementDirection.Left => ImMovementActor.MoveLeft(pressed) case MovementDirection.Right => ImMovementActor.MoveRight(pressed) }) if (pressed) nextStateFn(AliveSubstate.Moving(Walking)) else nextStateFn(AliveSubstate.Idle) } else { children.movementActor ! ImMovementActor.StopMoving Behaviors.same } // case PlayerActor.Walk(pressed, Direction.Up) => Behaviors.same // case PlayerActor.Walk(pressed, Direction.Down) => Behaviors.same // case PlayerActor.Walk(pressed, Direction.Left) => Behaviors.same // case PlayerActor.Walk(pressed, Direction.Right) => Behaviors.same case PlayerActor.StopMoving => children.movementActor ! ImMovementActor.StopMoving Behaviors.same case PlayerActor.Walk(pressed, direction) => implicit val ec = props.scheduler.value for { curStats <- children.statsActor.ask(StatsActor.CurrentStats) _ <- Future.successful( ctx.self ! PlayerActor.HandleWalk(curStats, pressed, direction) ) } yield () Behaviors.same 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) => 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])