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
250 lines
8.1 KiB
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
|
|
}
|
|
|
|
}
|