|
|
package wow.doge.mygame.game.entities.player
import scala.concurrent.duration._
import akka.actor.typed.ActorRef import akka.actor.typed.Behavior import akka.actor.typed.LogOptions import akka.actor.typed.PostStop import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors import akka.util.Timeout import com.typesafe.scalalogging.Logger import monix.bio.UIO import monix.execution.AsyncQueue import monix.reactive.Observable import org.slf4j.event.Level import wow.doge.mygame.Dispatchers import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.executors.Schedulers.AsyncScheduler 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.behaviors.IdleBehaviorFactory import wow.doge.mygame.implicits._ import wow.doge.mygame.subsystems.events.EventBus import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.events.TickEvent.RenderTick import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.utils.MovementDirection
object PlayerActor {
type Ref = ActorRef[PlayerActor.Command]
sealed trait Status object Status { case object Alive extends Status case object Dead extends Status }
sealed trait Command extends Product with Serializable final case class TakeDamage( value: CharacterStats.DamageHealth, replyTo: ActorRef[CharacterStats] ) extends Command final case class ConsumeStamina( value: CharacterStats.DamageStamina, replyTo: ActorRef[CharacterStats] ) extends Command final case class HealHealth( value: CharacterStats.HealHealth, replyTo: ActorRef[CharacterStats] ) extends Command final case class HealStamina( value: CharacterStats.HealStamina, replyTo: ActorRef[CharacterStats] ) extends Command final case class CurrentStats(replyTo: ActorRef[CharacterStats]) extends Command final case class GetStatus(replyTo: ActorRef[Status]) extends Command final case class GetStatsObservable( replyTo: ActorRef[UIO[Observable[CharacterStats]]] ) extends Command
final case class Walk(pressed: Boolean, dir: MovementDirection) extends Command case object StopMoving extends Command case object Jump extends Command
private[player] final case class HandleWalk( b: CharacterStats, pressed: Boolean, direction: MovementDirection ) extends Command
private[player] case object Die extends Command private[player] final case class StatsResponse( response: (StatsActor.Status, CharacterStats), replyTo: ActorRef[CharacterStats] ) extends Command private[player] final case class LogError(ex: Throwable) extends Command class Props( val playerEventBus: GameEventBus[PlayerEvent], val tickEventBus: GameEventBus[TickEvent], val imMovementActorBehavior: Behavior[ImMovementActor.Command], val statsActorBehavior: Behavior[StatsActor.Command], val scheduler: AsyncScheduler, val fxScheduler: Schedulers.FxScheduler, val statsQueue: AsyncQueue[CharacterStats] ) { def behavior = Behaviors.logMessages( LogOptions() .withLevel(Level.DEBUG) .withLogger(Logger[PlayerActor].underlying), Behaviors .setup[Command] { ctx => ctx.log.infoP("Starting PlayerActor")
// spawn children actors
val playerStatsActor = ctx.spawnN(statsActorBehavior)
val playerMovementActor = ctx.spawnN( Behaviors .supervise(imMovementActorBehavior) .onFailure[Exception]( SupervisorStrategy.restart.withLimit(2, 100.millis) ), Dispatchers.jmeDispatcher )
// val playerMovementEl = ctx.spawnN(
// Behaviors
// .supervise(
// new PlayerMovementEventListener.Props(
// ctx.self,
// scheduler
// ).behavior
// )
// .onFailure[Exception](
// SupervisorStrategy.restart.withLimit(2, 100.millis)
// )
// )
val renderTickEl = { val behavior: Behavior[RenderTick.type] = Behaviors.setup(ctx => Behaviors .receiveMessage[RenderTick.type] { case RenderTick => playerMovementActor ! ImMovementActor.Tick // playerCameraActor ! PlayerCameraActor.Tick
Behaviors.same } .receiveSignal { case (_, PostStop) => ctx.log.infoP("stopped") Behaviors.same } ) ctx.spawn(behavior, "playerMovementTickListener") }
//init listeners
// playerEventBus ! EventBus.Subscribe(playerMovementEl)
tickEventBus ! EventBus.Subscribe(renderTickEl)
new PlayerActor( ctx, this, Children(playerMovementActor, playerStatsActor) ).aliveState(AliveSubstate.Idle) } )
}
final case class Children( movementActor: ActorRef[ImMovementActor.Command], statsActor: ActorRef[StatsActor.Command] )
final case class Env( ctx: ActorContext[PlayerActor.Command], props: PlayerActor.Props, children: PlayerActor.Children ) }
class PlayerActor( ctx: ActorContext[PlayerActor.Command], props: PlayerActor.Props, children: PlayerActor.Children ) { import PlayerActor._ implicit val timeout = Timeout(1.second)
val env = Env(ctx, props, children)
val deadState = Behaviors .receiveMessage[Command] { // case TakeDamage(value) =>
// children.statsActor ! StatsActor.TakeDamage(value)
// // children.movementActor ! ImMovementActor.MovedDown(true)
// Behaviors.same
// case CurrentStats(replyTo) =>
// // ctx.ask(children.statsActor, StatsActor.CurrentStats())
// children.statsActor ! StatsActor.CurrentStats(replyTo)
// Behaviors.same
// case Heal(_) =>
// Behaviors.same
case CurrentStats(replyTo) => // ctx.ask(children.statsActor, StatsActor.CurrentStats())
children.statsActor ! StatsActor.CurrentStats(replyTo) Behaviors.same case GetStatus(replyTo) => replyTo ! Status.Dead Behaviors.same case _ => Behaviors.unhandled } .receiveSignal { case (_, PostStop) => ctx.log.infoP("stopped") Behaviors.same }
def idleBehavior( nextStateFn: AliveSubstate => Behavior[Command], consumptionMultiplier: Int => Int ) = new IdleBehaviorFactory( env, nextStateFn, deadState, consumptionMultiplier ).behavior
def aliveState(substate: AliveSubstate): Behavior[Command] = substate match { case AliveSubstate.InCombat(substate) => substate match { case CombatSubstate.Moving(substate) => substate match { case Walking => Behaviors.receiveMessage[Command](_ => Behaviors.same) case Running => Behaviors.receiveMessage[Command](_ => Behaviors.same) } case CombatSubstate.Attacking(victimName) => Behaviors.receiveMessage[Command](_ => Behaviors.same) }
case AliveSubstate.Moving(Walking) => ctx.log.debugP("In Walking State") idleBehavior(aliveState, _ * 2).value
case AliveSubstate.Moving(Running) => idleBehavior(aliveState, _ * 3).value
case AliveSubstate.Idle => ctx.log.debugP("In Idle State") idleBehavior(aliveState, identity).value }
}
|