Player actor changes

Rename PlayerActorSupervisor to PlayerActor
Make PlayerEventListener able to handle multiple inputs
This commit is contained in:
Rohan Sircar 2021-03-06 19:33:27 +05:30
parent 67201c8f7e
commit be9acf81d5
6 changed files with 104 additions and 61 deletions

View File

@ -52,7 +52,7 @@ import wow.doge.mygame.game.entities.CharacterStats
import wow.doge.mygame.game.entities.EntityIds
import wow.doge.mygame.game.entities.NpcActorSupervisor
import wow.doge.mygame.game.entities.NpcMovementActor
import wow.doge.mygame.game.entities.player.PlayerActorSupervisor
import wow.doge.mygame.game.entities.player.PlayerActor
import wow.doge.mygame.game.entities.player.PlayerController
import wow.doge.mygame.game.subsystems.input.GameInputHandler
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
@ -203,7 +203,7 @@ class MainApp(
.use(_ => launchSignal.get)
.hideErrors
tickEventBus <-
eventsModule.tickEventBus.hideErrorsWith(e => DummyException(e.toString))
eventsModule.tickEventBus.hideErrorsWith(e => new Exception(e.toString))
_ <-
/**
* User chose to quit
@ -288,7 +288,7 @@ class MainAppDelegate(
(loggerL.debug(show"Received Damage Event $event") >>
(if (event.victimName === "PlayerNode")
playerActor
.askL(PlayerActorSupervisor.TakeDamage(event.amount, _))
.askL(PlayerActor.TakeDamage(event.amount, _))
.onErrorHandle { case ex: TimeoutException => () }
else IO.unit)).toTask
)
@ -377,7 +377,7 @@ class MainAppDelegate(
// .startAndForget
statsObs <-
playerActor
.askL(PlayerActorSupervisor.GetStatsObservable(_))
.askL(PlayerActor.GetStatsObservable(_))
.onErrorHandleWith(TimeoutError.from)
.flatten
playerHud <-
@ -480,7 +480,7 @@ class MainAppDelegate(
def createPlayerController(
// appScheduler: monix.execution.Scheduler
): IO[AppError, PlayerActorSupervisor.Ref] = {
): IO[AppError, PlayerActor.Ref] = {
val playerPos = ImVector3f.Zero
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
// val modelPath = os.rel / "Models" / "Oto" / "Oto.mesh.xml"

View File

@ -30,10 +30,9 @@ import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
import wow.doge.mygame.subsystems.movement.ImMovementActor
//TODO: Change name to PlayerActor
object PlayerActorSupervisor {
object PlayerActor {
type Ref = ActorRef[PlayerActorSupervisor.Command]
type Ref = ActorRef[PlayerActor.Command]
sealed trait Status
object Status {
@ -71,6 +70,7 @@ object PlayerActorSupervisor {
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]
@ -79,20 +79,14 @@ object PlayerActorSupervisor {
Behaviors.logMessages(
LogOptions()
.withLevel(Level.DEBUG)
.withLogger(Logger[PlayerActorSupervisor].underlying),
.withLogger(Logger[PlayerActor].underlying),
Behaviors
.setup[Command] { ctx =>
ctx.log.infoP("Starting PlayerActor")
// spawn children actors
val playerStatsActor =
ctx.spawnN(
new StatsActor.Props(
CharacterStats.Health(100),
CharacterStats.Stamina(100)
).behavior
)
val playerStatsActor = ctx.spawnN(statsActorBehavior)
val playerMovementActor =
ctx.spawnN(
@ -141,7 +135,7 @@ object PlayerActorSupervisor {
playerEventBus ! EventBus.Subscribe(playerMovementEl)
tickEventBus ! EventBus.Subscribe(renderTickEl)
new PlayerActorSupervisor(
new PlayerActor(
ctx,
this,
Children(playerMovementActor, playerStatsActor)
@ -157,18 +151,18 @@ object PlayerActorSupervisor {
)
final case class Env(
ctx: ActorContext[PlayerActorSupervisor.Command],
props: PlayerActorSupervisor.Props,
children: PlayerActorSupervisor.Children
ctx: ActorContext[PlayerActor.Command],
props: PlayerActor.Props,
children: PlayerActor.Children
)
}
class PlayerActorSupervisor(
ctx: ActorContext[PlayerActorSupervisor.Command],
props: PlayerActorSupervisor.Props,
children: PlayerActorSupervisor.Children
class PlayerActor(
ctx: ActorContext[PlayerActor.Command],
props: PlayerActor.Props,
children: PlayerActor.Children
) {
import PlayerActorSupervisor._
import PlayerActor._
implicit val timeout = Timeout(1.second)
val env = Env(ctx, props, children)

View File

@ -26,6 +26,8 @@ import wow.doge.mygame.subsystems.events.TickEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.types._
import wow.doge.mygame.utils.wrappers.jme._
import wow.doge.mygame.game.entities.StatsActor
import wow.doge.mygame.game.entities.CharacterStats
object PlayerController {
sealed trait Error
@ -65,10 +67,15 @@ object PlayerController {
enqueueR,
camera
).behavior(playerPhysicsControl)
new PlayerActorSupervisor.Props(
val statsActorBeh = new StatsActor.Props(
CharacterStats.Health(100),
CharacterStats.Stamina(100)
).behavior
new PlayerActor.Props(
playerEventBus,
tickEventBus,
movementActorBeh,
statsActorBeh,
scheduler,
fxScheduler,
AsyncQueue.bounded(10)(scheduler.value)
@ -77,10 +84,9 @@ object PlayerController {
val playerActor =
gameApp.spawnGameActor(
playerActorBehavior,
// Some("playerActorSupervisor"),
props = DispatcherSelector.default()
)
val create: IO[AppError, ActorRef[PlayerActorSupervisor.Command]] =
val create: IO[AppError, ActorRef[PlayerActor.Command]] =
(for {
// playerActor <-
// // AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")

View File

@ -18,19 +18,20 @@ import wow.doge.mygame.game.entities.CharacterStats
import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor
import cats.syntax.eq._
object PlayerMovementEventListener {
final case class State(staminaTimer: CancelableFuture[Unit])
final case class State(keysPressed: Int, staminaTimer: CancelableFuture[Unit])
class Props(
val movementActor: ActorRef[ImMovementActor.Command],
val playerActor: PlayerActorSupervisor.Ref,
val playerActor: PlayerActor.Ref,
val asyncScheduler: Schedulers.AsyncScheduler
) {
def behavior =
Behaviors.setup[PlayerMovementEvent] { ctx =>
new PlayerMovementEventListener(ctx, this)
.receive(State(CancelableFuture.unit))
.receive(State(0, CancelableFuture.unit))
}
}
}
@ -40,6 +41,7 @@ class PlayerMovementEventListener(
) {
import PlayerMovementEventListener._
import PlayerMovementEvent._
import com.softwaremill.quicklens._
def receive(state: State): Behavior[PlayerMovementEvent] =
Behaviors.logMessages(
LogOptions()
@ -57,28 +59,42 @@ class PlayerMovementEventListener(
.interval(250.millis)
.doOnNextF(_ =>
props.playerActor.askL(
PlayerActorSupervisor
PlayerActor
.ConsumeStamina(CharacterStats.DamageStamina(1), _)
)
)
.completedL
)
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
def handleStamina(pressed: Boolean) =
if (pressed)
if (state.staminaTimer == CancelableFuture.unit)
State(makeStaminaTimer.runToFuture(props.asyncScheduler.value))
else state
else {
state.staminaTimer.cancel()
State(CancelableFuture.unit)
if (pressed) {
val nextState1 =
if (state.keysPressed === 0)
state
.modify(_.staminaTimer)
.setTo(
makeStaminaTimer.runToFuture(props.asyncScheduler.value)
)
else state
val nextState2 = nextState1
.modify(_.keysPressed)
.using(_ + 1)
nextState2
} else {
val nextState1 = state
.modify(_.keysPressed)
.using(_ - 1)
if (nextState1.keysPressed === 0) {
nextState1.staminaTimer.cancel()
nextState1
} else
nextState1
}
Behaviors.receiveMessage {
case PlayerMovedLeft(pressed) =>
// props.movementActor ! ImMovementActor.MoveLeft(pressed)
props.playerActor ! PlayerActorSupervisor.MoveLeft(pressed)
props.playerActor ! PlayerActor.MoveLeft(pressed)
receive(handleStamina(pressed))
case PlayerMovedRight(pressed) =>
props.movementActor ! ImMovementActor.MoveRight(pressed)

View File

@ -13,14 +13,14 @@ import monix.reactive.Observable
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.PlayerActorSupervisor
import wow.doge.mygame.game.entities.player.PlayerActor
import wow.doge.mygame.implicits._
import wow.doge.mygame.subsystems.movement.ImMovementActor
class IdleBehaviorFactory(
env: PlayerActorSupervisor.Env,
nextStateFn: AliveSubstate => Behavior[PlayerActorSupervisor.Command],
deadState: Behavior[PlayerActorSupervisor.Command],
env: PlayerActor.Env,
nextStateFn: AliveSubstate => Behavior[PlayerActor.Command],
deadState: Behavior[PlayerActor.Command],
consumptionMultiplier: Int => Int
)(implicit timeout: Timeout) {
import IdleBehaviorFactory.IdleBehavior
@ -31,23 +31,23 @@ class IdleBehaviorFactory(
def behavior =
IdleBehavior(
Behaviors
.receiveMessage[PlayerActorSupervisor.Command] {
case PlayerActorSupervisor.MoveLeft(pressed) =>
.receiveMessage[PlayerActor.Command] {
case PlayerActor.MoveLeft(pressed) =>
children.movementActor ! ImMovementActor.MoveLeft(pressed)
if (pressed)
nextStateFn(AliveSubstate.Moving(Walking))
else nextStateFn(AliveSubstate.Idle)
case PlayerActorSupervisor.TakeDamage(value, replyTo) =>
case PlayerActor.TakeDamage(value, replyTo) =>
implicit val ec = props.scheduler.value
for {
res <-
children.statsActor.ask(StatsActor.TakeDamageResult(value, _))
_ <- Future.successful(
ctx.self ! PlayerActorSupervisor.StatsResponse(res, replyTo)
ctx.self ! PlayerActor.StatsResponse(res, replyTo)
)
} yield ()
Behaviors.same
case PlayerActorSupervisor.ConsumeStamina(value, replyTo) =>
case PlayerActor.ConsumeStamina(value, replyTo) =>
implicit val ec = props.scheduler.value
val newValue =
CharacterStats.DamageStamina(consumptionMultiplier(value.toInt))
@ -56,20 +56,20 @@ class IdleBehaviorFactory(
StatsActor.ConsumeStaminaResult(newValue, _)
)
_ =
ctx.self ! PlayerActorSupervisor
ctx.self ! PlayerActor
.StatsResponse(response, replyTo)
} yield ()
Behaviors.same
case PlayerActorSupervisor.CurrentStats(replyTo) =>
case PlayerActor.CurrentStats(replyTo) =>
children.statsActor ! StatsActor.CurrentStats(replyTo)
Behaviors.same
case PlayerActorSupervisor.Heal(value) =>
case PlayerActor.Heal(value) =>
children.statsActor ! StatsActor.HealResult(value)
Behaviors.same
case PlayerActorSupervisor.GetStatus(replyTo) =>
replyTo ! PlayerActorSupervisor.Status.Alive
case PlayerActor.GetStatus(replyTo) =>
replyTo ! PlayerActor.Status.Alive
Behaviors.same
case PlayerActorSupervisor.GetStatsObservable(replyTo) =>
case PlayerActor.GetStatsObservable(replyTo) =>
import monix.{eval => me}
replyTo !
UIO(
@ -82,10 +82,10 @@ class IdleBehaviorFactory(
)
Behaviors.same
case PlayerActorSupervisor.StatsResponse(response, replyTo) =>
case PlayerActor.StatsResponse(response, replyTo) =>
response match {
case (dead, stats) =>
if (dead) ctx.self ! PlayerActorSupervisor.Die
if (dead) ctx.self ! PlayerActor.Die
props.statsQueue
.offer(stats)
.foreach { _ =>
@ -95,8 +95,8 @@ class IdleBehaviorFactory(
}
Behaviors.same
// nextStateFn(InCombat(Moving(Walking)))
case PlayerActorSupervisor.Die => deadState
case PlayerActorSupervisor.LogError(ex) =>
case PlayerActor.Die => deadState
case PlayerActor.LogError(ex) =>
ctx.log.error(ex.getMessage)
Behaviors.same
}
@ -110,6 +110,6 @@ class IdleBehaviorFactory(
object IdleBehaviorFactory {
final case class IdleBehavior(
value: Behavior[PlayerActorSupervisor.Command]
value: Behavior[PlayerActor.Command]
)
}

View File

@ -0,0 +1,27 @@
package wow.doge.mygame
import sttp.capabilities.monix.MonixStreams
import sttp.client3._
import sttp.client3.asynchttpclient.monix._
import monix.eval.Task
import monix.reactive.Observable
import scala.concurrent.duration.Duration
class WebsocketTest {
// : Task[Response[Either[String, Observable[Array[Byte]]]]]
val s = AsyncHttpClientMonixBackend().flatMap { backend =>
val response =
basicRequest
.post(uri"...")
.response(
asStream(MonixStreams)(
_.doOnNext(i => Task(println(s"$i"))).completedL
)
)
.readTimeout(Duration.Inf)
.send(backend)
response
}
}