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.

250 lines
8.1 KiB

4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
  1. package wow.doge.mygame.game.entities.player
  2. import scala.concurrent.duration._
  3. import akka.actor.typed.ActorRef
  4. import akka.actor.typed.Behavior
  5. import akka.actor.typed.LogOptions
  6. import akka.actor.typed.PostStop
  7. import akka.actor.typed.SupervisorStrategy
  8. import akka.actor.typed.scaladsl.ActorContext
  9. import akka.actor.typed.scaladsl.Behaviors
  10. import akka.util.Timeout
  11. import com.typesafe.scalalogging.Logger
  12. import monix.bio.UIO
  13. import monix.execution.AsyncQueue
  14. import monix.reactive.Observable
  15. import org.slf4j.event.Level
  16. import wow.doge.mygame.Dispatchers
  17. import wow.doge.mygame.executors.Schedulers
  18. import wow.doge.mygame.executors.Schedulers.AsyncScheduler
  19. import wow.doge.mygame.game.entities.CharacterStats
  20. import wow.doge.mygame.game.entities.StatsActor
  21. import wow.doge.mygame.game.entities.character.CharacterStates._
  22. import wow.doge.mygame.game.entities.player.behaviors.IdleBehaviorFactory
  23. import wow.doge.mygame.implicits._
  24. import wow.doge.mygame.subsystems.events.EventBus
  25. import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
  26. import wow.doge.mygame.subsystems.events.PlayerEvent
  27. import wow.doge.mygame.subsystems.events.TickEvent
  28. import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
  29. import wow.doge.mygame.subsystems.movement.ImMovementActor
  30. import wow.doge.mygame.utils.MovementDirection
  31. object PlayerActor {
  32. type Ref = ActorRef[PlayerActor.Command]
  33. sealed trait Status
  34. object Status {
  35. case object Alive extends Status
  36. case object Dead extends Status
  37. }
  38. sealed trait Command extends Product with Serializable
  39. final case class TakeDamage(
  40. value: CharacterStats.DamageHealth,
  41. replyTo: ActorRef[CharacterStats]
  42. ) extends Command
  43. final case class ConsumeStamina(
  44. value: CharacterStats.DamageStamina,
  45. replyTo: ActorRef[CharacterStats]
  46. ) extends Command
  47. final case class HealHealth(
  48. value: CharacterStats.HealHealth,
  49. replyTo: ActorRef[CharacterStats]
  50. ) extends Command
  51. final case class HealStamina(
  52. value: CharacterStats.HealStamina,
  53. replyTo: ActorRef[CharacterStats]
  54. ) extends Command
  55. final case class CurrentStats(replyTo: ActorRef[CharacterStats])
  56. extends Command
  57. final case class GetStatus(replyTo: ActorRef[Status]) extends Command
  58. final case class GetStatsObservable(
  59. replyTo: ActorRef[UIO[Observable[CharacterStats]]]
  60. ) extends Command
  61. final case class Walk(pressed: Boolean, dir: MovementDirection)
  62. extends Command
  63. case object StopMoving extends Command
  64. case object Jump extends Command
  65. private[player] final case class HandleWalk(
  66. b: CharacterStats,
  67. pressed: Boolean,
  68. direction: MovementDirection
  69. ) extends Command
  70. private[player] case object Die extends Command
  71. private[player] final case class StatsResponse(
  72. response: (StatsActor.Status, CharacterStats),
  73. replyTo: ActorRef[CharacterStats]
  74. ) extends Command
  75. private[player] final case class LogError(ex: Throwable) extends Command
  76. class Props(
  77. val playerEventBus: GameEventBus[PlayerEvent],
  78. val tickEventBus: GameEventBus[TickEvent],
  79. val imMovementActorBehavior: Behavior[ImMovementActor.Command],
  80. val statsActorBehavior: Behavior[StatsActor.Command],
  81. val scheduler: AsyncScheduler,
  82. val fxScheduler: Schedulers.FxScheduler,
  83. val statsQueue: AsyncQueue[CharacterStats]
  84. ) {
  85. def behavior =
  86. Behaviors.logMessages(
  87. LogOptions()
  88. .withLevel(Level.DEBUG)
  89. .withLogger(Logger[PlayerActor].underlying),
  90. Behaviors
  91. .setup[Command] { ctx =>
  92. ctx.log.infoP("Starting PlayerActor")
  93. // spawn children actors
  94. val playerStatsActor = ctx.spawnN(statsActorBehavior)
  95. val playerMovementActor =
  96. ctx.spawnN(
  97. Behaviors
  98. .supervise(imMovementActorBehavior)
  99. .onFailure[Exception](
  100. SupervisorStrategy.restart.withLimit(2, 100.millis)
  101. ),
  102. Dispatchers.jmeDispatcher
  103. )
  104. // val playerMovementEl = ctx.spawnN(
  105. // Behaviors
  106. // .supervise(
  107. // new PlayerMovementEventListener.Props(
  108. // ctx.self,
  109. // scheduler
  110. // ).behavior
  111. // )
  112. // .onFailure[Exception](
  113. // SupervisorStrategy.restart.withLimit(2, 100.millis)
  114. // )
  115. // )
  116. val renderTickEl = {
  117. val behavior: Behavior[RenderTick.type] =
  118. Behaviors.setup(ctx =>
  119. Behaviors
  120. .receiveMessage[RenderTick.type] {
  121. case RenderTick =>
  122. playerMovementActor ! ImMovementActor.Tick
  123. // playerCameraActor ! PlayerCameraActor.Tick
  124. Behaviors.same
  125. }
  126. .receiveSignal {
  127. case (_, PostStop) =>
  128. ctx.log.infoP("stopped")
  129. Behaviors.same
  130. }
  131. )
  132. ctx.spawn(behavior, "playerMovementTickListener")
  133. }
  134. //init listeners
  135. // playerEventBus ! EventBus.Subscribe(playerMovementEl)
  136. tickEventBus ! EventBus.Subscribe(renderTickEl)
  137. new PlayerActor(
  138. ctx,
  139. this,
  140. Children(playerMovementActor, playerStatsActor)
  141. ).aliveState(AliveSubstate.Idle)
  142. }
  143. )
  144. }
  145. final case class Children(
  146. movementActor: ActorRef[ImMovementActor.Command],
  147. statsActor: ActorRef[StatsActor.Command]
  148. )
  149. final case class Env(
  150. ctx: ActorContext[PlayerActor.Command],
  151. props: PlayerActor.Props,
  152. children: PlayerActor.Children
  153. )
  154. }
  155. class PlayerActor(
  156. ctx: ActorContext[PlayerActor.Command],
  157. props: PlayerActor.Props,
  158. children: PlayerActor.Children
  159. ) {
  160. import PlayerActor._
  161. implicit val timeout = Timeout(1.second)
  162. val env = Env(ctx, props, children)
  163. val deadState = Behaviors
  164. .receiveMessage[Command] {
  165. // case TakeDamage(value) =>
  166. // children.statsActor ! StatsActor.TakeDamage(value)
  167. // // children.movementActor ! ImMovementActor.MovedDown(true)
  168. // Behaviors.same
  169. // case CurrentStats(replyTo) =>
  170. // // ctx.ask(children.statsActor, StatsActor.CurrentStats())
  171. // children.statsActor ! StatsActor.CurrentStats(replyTo)
  172. // Behaviors.same
  173. // case Heal(_) =>
  174. // Behaviors.same
  175. case CurrentStats(replyTo) =>
  176. // ctx.ask(children.statsActor, StatsActor.CurrentStats())
  177. children.statsActor ! StatsActor.CurrentStats(replyTo)
  178. Behaviors.same
  179. case GetStatus(replyTo) =>
  180. replyTo ! Status.Dead
  181. Behaviors.same
  182. case _ => Behaviors.unhandled
  183. }
  184. .receiveSignal {
  185. case (_, PostStop) =>
  186. ctx.log.infoP("stopped")
  187. Behaviors.same
  188. }
  189. def idleBehavior(
  190. nextStateFn: AliveSubstate => Behavior[Command],
  191. consumptionMultiplier: Int => Int
  192. ) =
  193. new IdleBehaviorFactory(
  194. env,
  195. nextStateFn,
  196. deadState,
  197. consumptionMultiplier
  198. ).behavior
  199. def aliveState(substate: AliveSubstate): Behavior[Command] =
  200. substate match {
  201. case AliveSubstate.InCombat(substate) =>
  202. substate match {
  203. case CombatSubstate.Moving(substate) =>
  204. substate match {
  205. case Walking =>
  206. Behaviors.receiveMessage[Command](_ => Behaviors.same)
  207. case Running =>
  208. Behaviors.receiveMessage[Command](_ => Behaviors.same)
  209. }
  210. case CombatSubstate.Attacking(victimName) =>
  211. Behaviors.receiveMessage[Command](_ => Behaviors.same)
  212. }
  213. case AliveSubstate.Moving(Walking) =>
  214. ctx.log.debugP("In Walking State")
  215. idleBehavior(aliveState, _ * 2).value
  216. case AliveSubstate.Moving(Running) =>
  217. idleBehavior(aliveState, _ * 3).value
  218. case AliveSubstate.Idle =>
  219. ctx.log.debugP("In Idle State")
  220. idleBehavior(aliveState, identity).value
  221. }
  222. }