package wow.doge.mygame.game.nodes import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.ActorContext import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.game.GameApp import akka.actor.typed.ActorRef import wow.doge.mygame.events.EventBus import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import wow.doge.mygame.subsystems.events.EntityMovementEvent import akka.actor.typed.scaladsl.TimerScheduler import akka.actor.typed.Behavior import scala.concurrent.duration._ import akka.actor.typed.LogOptions import org.slf4j.event.Level import com.typesafe.scalalogging.Logger import akka.actor.typed.SupervisorStrategy import com.jme3.scene.CameraNode import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.game.subsystems.movement.CanMove import wow.doge.mygame.implicits._ object PlayerActorSupervisor { sealed trait Command final case class Props( app: GameApp, camNode: CameraNode, playerMovementEventBus: ActorRef[ EventBus.Command[PlayerMovementEvent] ], playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]] ) { def create[T: CanMove](movable: T) = Behaviors.logMessages( LogOptions() .withLevel(Level.TRACE) .withLogger( Logger[PlayerActorSupervisor[T]].underlying ), Behaviors.setup[Command] { ctx => ctx.log.info("Hello from PlayerActor") // spawn children actors lazy val movementActor = ctx.spawn( Behaviors .supervise( ImMovementActor .Props(app, movable, playerMovementEventBus) .create ) .onFailure[Exception](SupervisorStrategy.restart), "playerMovementActor" ) lazy val playerMovementEventHandler = ctx.spawn( Behaviors .supervise(PlayerMovementEventListener(movementActor)) .onFailure[Exception](SupervisorStrategy.restart), "playerMovementEventHandler" ) lazy val movementActorTimer = ctx.spawn( Behaviors .supervise(MovementActorTimer(movementActor)) .onFailure[Exception](SupervisorStrategy.restart), "playerMovementActorTimer" ) lazy val playerCameraHandler = { ctx.spawn( Behaviors .supervise( PlayerCameraEventListener(camNode, app.enqueueR) ) .onFailure[Exception](SupervisorStrategy.restart), "playerCameraHandler" ) } //init actors movementActorTimer ! MovementActorTimer.Start playerMovementEventBus ! EventBus.Subscribe( playerMovementEventHandler ) playerCameraEventBus ! EventBus.Subscribe(playerCameraHandler) new PlayerActorSupervisor( ctx, this, Children(movementActor, playerMovementEventHandler) ).receive } ) } case class Children( movementActor: ActorRef[ImMovementActor.Command], playerMovementEventHandler: ActorRef[ EntityMovementEvent.PlayerMovementEvent ] ) } class PlayerActorSupervisor[T: CanMove]( ctx: ActorContext[PlayerActorSupervisor.Command], props: PlayerActorSupervisor.Props, children: PlayerActorSupervisor.Children ) { import PlayerActorSupervisor._ def receive = Behaviors.receiveMessage[Command] { case _ => // children.movementActor ! ImMovementActor.MovedDown(true) Behaviors.same } } object MovementActorTimer { sealed trait Command final case object Start extends Command final case object Stop extends Command private case object Send extends Command case object TimerKey def apply(target: ActorRef[ImMovementActor.Command]) = Behaviors.withTimers[Command] { timers => new MovementActorTimer(timers, target).idle } } class MovementActorTimer( timers: TimerScheduler[MovementActorTimer.Command], target: ActorRef[ImMovementActor.Command] ) { import MovementActorTimer._ val idle: Behavior[Command] = Behaviors.receiveMessage { msg => msg match { case Start => timers.startTimerWithFixedDelay(TimerKey, Send, (60f / 144).millis) active case _ => Behaviors.unhandled } } val active: Behavior[Command] = Behaviors.receiveMessage { msg => msg match { case Send => target ! ImMovementActor.Tick Behaviors.same case Stop => timers.cancel(TimerKey) idle case _ => Behaviors.unhandled } } } object GenericTimerActor { sealed trait Command final case object Start extends Command final case object Stop extends Command private case object Send extends Command case object TimerKey case class Props[T]( target: ActorRef[T], messageToSend: T, timeInterval: FiniteDuration ) { val create = Behaviors.withTimers[Command] { timers => new GenericTimerActor(timers, this).idle } } } class GenericTimerActor[T]( timers: TimerScheduler[GenericTimerActor.Command], props: GenericTimerActor.Props[T] ) { import GenericTimerActor._ val idle: Behavior[Command] = Behaviors.receiveMessage { case Start => timers.startTimerWithFixedDelay(TimerKey, Send, props.timeInterval) active case _ => Behaviors.unhandled } val active: Behavior[Command] = Behaviors.receiveMessagePartial { case Send => props.target ! props.messageToSend Behaviors.same case Stop => timers.cancel(TimerKey) idle } }