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.

182 lines
6.2 KiB

  1. package wow.doge.mygame.game.entities.player.behaviors
  2. import scala.concurrent.Future
  3. import akka.actor.typed.Behavior
  4. import akka.actor.typed.PostStop
  5. import akka.actor.typed.scaladsl.AskPattern._
  6. import akka.actor.typed.scaladsl.Behaviors
  7. import akka.util.Timeout
  8. import cats.syntax.show._
  9. import monix.bio.UIO
  10. import monix.reactive.Observable
  11. import wow.doge.mygame.game.entities.CharacterStats
  12. import wow.doge.mygame.game.entities.StatsActor
  13. import wow.doge.mygame.game.entities.character.CharacterStates._
  14. import wow.doge.mygame.game.entities.player.PlayerActor
  15. import wow.doge.mygame.implicits._
  16. import wow.doge.mygame.subsystems.movement.ImMovementActor
  17. import wow.doge.mygame.utils.MovementDirection
  18. class IdleBehaviorFactory(
  19. env: PlayerActor.Env,
  20. nextStateFn: AliveSubstate => Behavior[PlayerActor.Command],
  21. deadState: Behavior[PlayerActor.Command],
  22. consumptionMultiplier: Int => Int
  23. )(implicit timeout: Timeout) {
  24. import env._
  25. implicit val sched = ctx.system.scheduler
  26. def behavior =
  27. IdleBehavior(
  28. Behaviors
  29. .receiveMessage[PlayerActor.Command] {
  30. case PlayerActor.HandleWalk(curStats, pressed, direction) =>
  31. if (curStats.stamina.toInt > 0) {
  32. children.movementActor ! (direction match {
  33. case MovementDirection.Forward =>
  34. ImMovementActor.MoveUp(pressed)
  35. case MovementDirection.Backward =>
  36. ImMovementActor.MoveDown(pressed)
  37. case MovementDirection.Left => ImMovementActor.MoveLeft(pressed)
  38. case MovementDirection.Right =>
  39. ImMovementActor.MoveRight(pressed)
  40. })
  41. if (pressed) nextStateFn(AliveSubstate.Moving(Walking))
  42. else nextStateFn(AliveSubstate.Idle)
  43. } else {
  44. children.movementActor ! ImMovementActor.StopMoving
  45. Behaviors.same
  46. }
  47. // case PlayerActor.Walk(pressed, Direction.Up) => Behaviors.same
  48. // case PlayerActor.Walk(pressed, Direction.Down) => Behaviors.same
  49. // case PlayerActor.Walk(pressed, Direction.Left) => Behaviors.same
  50. // case PlayerActor.Walk(pressed, Direction.Right) => Behaviors.same
  51. case PlayerActor.StopMoving =>
  52. children.movementActor ! ImMovementActor.StopMoving
  53. Behaviors.same
  54. case PlayerActor.Walk(pressed, direction) =>
  55. implicit val ec = props.scheduler.value
  56. for {
  57. curStats <- children.statsActor.ask(StatsActor.CurrentStats)
  58. _ <- Future.successful(
  59. ctx.self ! PlayerActor.HandleWalk(curStats, pressed, direction)
  60. )
  61. } yield ()
  62. Behaviors.same
  63. case PlayerActor.Jump =>
  64. children.movementActor ! ImMovementActor.Jump
  65. ctx.self.ask(
  66. PlayerActor.ConsumeStamina(CharacterStats.DamageStamina(10), _)
  67. )
  68. Behaviors.same
  69. case PlayerActor.TakeDamage(value, replyTo) =>
  70. implicit val ec = props.scheduler.value
  71. for {
  72. res <-
  73. children.statsActor.ask(StatsActor.TakeDamageResult(value, _))
  74. _ = ctx.self ! PlayerActor.StatsResponse(res, replyTo)
  75. } yield ()
  76. Behaviors.same
  77. case PlayerActor.ConsumeStamina(value, replyTo) =>
  78. implicit val ec = props.scheduler.value
  79. val newValue =
  80. CharacterStats.DamageStamina(consumptionMultiplier(value.toInt))
  81. for {
  82. response <- children.statsActor.ask(
  83. StatsActor.ConsumeStaminaResult(newValue, _)
  84. )
  85. _ =
  86. ctx.self ! PlayerActor
  87. .StatsResponse(response, replyTo)
  88. } yield ()
  89. Behaviors.same
  90. case PlayerActor.CurrentStats(replyTo) =>
  91. children.statsActor ! StatsActor.CurrentStats(replyTo)
  92. Behaviors.same
  93. case PlayerActor.HealHealth(value, replyTo) =>
  94. implicit val ec = props.scheduler.value
  95. for {
  96. response <- children.statsActor.ask(
  97. StatsActor.HealHealthResult(value, _)
  98. )
  99. _ =
  100. ctx.self ! PlayerActor
  101. .StatsResponse(StatsActor.Status.Alive -> response, replyTo)
  102. } yield ()
  103. Behaviors.same
  104. case PlayerActor.HealStamina(value, replyTo) =>
  105. implicit val ec = props.scheduler.value
  106. for {
  107. response <- children.statsActor.ask(
  108. StatsActor.HealStaminaResult(value, _)
  109. )
  110. _ =
  111. ctx.self ! PlayerActor
  112. .StatsResponse(StatsActor.Status.Alive -> response, replyTo)
  113. } yield ()
  114. Behaviors.same
  115. case PlayerActor.GetStatus(replyTo) =>
  116. replyTo ! PlayerActor.Status.Alive
  117. Behaviors.same
  118. case PlayerActor.GetStatsObservable(replyTo) =>
  119. import monix.{eval => me}
  120. replyTo !
  121. UIO(
  122. Observable
  123. .repeatEvalF(
  124. me.Task.deferFuture(props.statsQueue.poll())
  125. )
  126. .publish(props.fxScheduler.value)
  127. .refCount
  128. )
  129. Behaviors.same
  130. case PlayerActor.StatsResponse(response, replyTo) =>
  131. response match {
  132. case (status, stats) =>
  133. status match {
  134. case StatsActor.Status.Dead => ctx.self ! PlayerActor.Die
  135. case StatsActor.Status.Alive => ()
  136. }
  137. props.statsQueue
  138. .offer(stats)
  139. .foreach { _ =>
  140. pprint.log(show"Published stats $stats")
  141. replyTo ! stats
  142. }(props.scheduler.value)
  143. }
  144. Behaviors.same
  145. case PlayerActor.Die => deadState
  146. case PlayerActor.LogError(ex) =>
  147. ctx.log.error(ex.getMessage)
  148. Behaviors.same
  149. }
  150. .receiveSignal {
  151. case (_, PostStop) =>
  152. ctx.log.infoP("stopped")
  153. Behaviors.same
  154. }
  155. )
  156. }
  157. final case class IdleBehavior(value: Behavior[PlayerActor.Command])