From 92aae68254374134efcea59df60921abf19ed105 Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Sun, 28 Feb 2021 19:32:49 +0530 Subject: [PATCH] Add stamina consumption logic --- src/main/scala/wow/doge/mygame/MainApp.scala | 5 +- .../scala/wow/doge/mygame/game/GameApp.scala | 122 -------------- .../player/PlayerActorSupervisor.scala | 9 +- .../player/PlayerActorSupervisor2.scala | 149 ------------------ .../player/PlayerEventListeners.scala | 77 +++++++-- .../subsystems/scriptsystem/ScriptActor.scala | 4 +- 6 files changed, 77 insertions(+), 289 deletions(-) delete mode 100644 src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor2.scala diff --git a/src/main/scala/wow/doge/mygame/MainApp.scala b/src/main/scala/wow/doge/mygame/MainApp.scala index dfb389f..44d6dec 100644 --- a/src/main/scala/wow/doge/mygame/MainApp.scala +++ b/src/main/scala/wow/doge/mygame/MainApp.scala @@ -38,6 +38,7 @@ import monix.reactive.Observable import scalafx.scene.control.Label import scalafx.scene.control.TextArea import scalafx.scene.layout.HBox +import scalafx.scene.layout.Priority import scalafx.scene.layout.VBox import scalafx.scene.paint.Color import wow.doge.mygame.AppError.TimeoutError @@ -381,6 +382,7 @@ class MainAppDelegate( .deferAction(implicit s => UIO(new VBox { implicit val c = CompositeCancelable() + hgrow = Priority.Always spacing = 10 style = """-fx-background-color: rgba(0,0,0,0.7);""" children = List( @@ -419,8 +421,7 @@ class MainAppDelegate( new JFXProgressBar { progress = 100 minHeight = 10 - progress <-- statsObs - .map(_.stamina.toInt.toDouble / 100) + progress <-- statsObs.map(_.stamina.toInt.toDouble / 100) } ) } diff --git a/src/main/scala/wow/doge/mygame/game/GameApp.scala b/src/main/scala/wow/doge/mygame/game/GameApp.scala index d2a1d63..3a1c12d 100644 --- a/src/main/scala/wow/doge/mygame/game/GameApp.scala +++ b/src/main/scala/wow/doge/mygame/game/GameApp.scala @@ -8,25 +8,16 @@ import akka.actor.typed.Props import akka.actor.typed.SpawnProtocol import akka.actor.typed.scaladsl.AskPattern._ import akka.util.Timeout -import cats.Show import cats.effect.Resource -import cats.effect.concurrent.Deferred -import cats.syntax.show._ import com.jme3.bullet.BulletAppState import com.jme3.input.InputManager -import com.jme3.scene.Node -import com.jme3.scene.Spatial import com.jme3.system.AppSettings import com.softwaremill.tagging._ -import com.typesafe.scalalogging.{Logger => SLogger} import io.odin.Logger import monix.bio.Fiber import monix.bio.IO import monix.bio.Task import monix.bio.UIO -import monix.catnap.ConcurrentChannel -import monix.catnap.ConsumerF -import monix.eval.Coeval import wow.doge.mygame.AppError import wow.doge.mygame.AppError.TimeoutError import wow.doge.mygame.Dispatchers @@ -137,116 +128,3 @@ class GameAppResource( case Left(error) => IO.terminate(new Exception(error.toString)) } } - -object GameApp {} - -object Ops { - final class AddToNode[T <: Node](private val node: T) extends AnyVal { - - /** - * Pure version - */ - def apply(spatial: Spatial)(implicit logger: Logger[Task]) = - logger.debug( - s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}" - ) >> Task(node.attachChild(spatial)) - - /** - * Impure version - */ - def apply(spatial: Spatial)(implicit logger: SLogger) = - Coeval { - logger.debug( - s"Request to add spatial with name ${spatial.getName()} to node ${node.getName()}" - ) - node.attachChild(spatial) - } - } -} - -object SpawnSystem { - sealed trait Result - case object Ok extends Result - - sealed trait Complete - case object Complete extends Complete - - sealed trait SpawnRequest - final case class SpawnSpatial(nodeTask: Task[Node]) extends SpawnRequest - - final case class SpawnRequestWrapper( - spawnRequest: SpawnRequest, - result: Deferred[Task, Result] - ) - - object SpawnRequestWrapper { - implicit val show = Show.fromToString[SpawnRequestWrapper] - } - - def apply(logger: Logger[Task]) = - for { - spawnChannel <- ConcurrentChannel[Task].of[Complete, SpawnRequestWrapper] - spawnSystem <- Task(new SpawnSystem(logger, spawnChannel)) - consumer <- - spawnChannel.consume - .use(consumer => spawnSystem.receive(consumer)) - .startAndForget - } yield (spawnSystem) -} - -class SpawnSystem( - logger: Logger[Task], - spawnChannel: ConcurrentChannel[ - Task, - SpawnSystem.Complete, - SpawnSystem.SpawnRequestWrapper - ] -) { - import SpawnSystem._ - - for { - spawnSystem <- SpawnSystem(logger) - res <- spawnSystem.request(SpawnSpatial(Task(new Node("Test")))) - } yield () - - // val spawnChannel = ConcurrentChannel[Task].of[Result, SpawnRequest] - - private def receive( - consumer: ConsumerF[Task, Complete, SpawnRequestWrapper] - ): Task[Unit] = - consumer.pull.flatMap { - case Right(message) => - for { - _ <- - logger - .debug(show"Received spawn request $message") - _ <- handleSpawn(message) - } yield receive(consumer) - case Left(r) => - logger.info("Closing Spawn System") - } - - private def handleSpawn(spawnRequestWrapper: SpawnRequestWrapper) = - spawnRequestWrapper match { - case SpawnRequestWrapper(spawnRequest, result) => - spawnRequest match { - case SpawnSpatial(spatialTask) => - spatialTask.flatMap(spatial => - logger.debug( - s"Spawning spatial with name ${spatial.getName()}" - ) >> result - .complete(Ok) - ) - - } - } - - def request(spawnRequest: SpawnRequest) = - for { - d <- Deferred[Task, Result] - _ <- spawnChannel.push(SpawnRequestWrapper(spawnRequest, d)) - res <- d.get - } yield (res) - - def stop = spawnChannel.halt(Complete) -} diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala index 77b71a0..c732d81 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor.scala @@ -103,7 +103,13 @@ object PlayerActorSupervisor { val playerMovementEl = ctx.spawnN( Behaviors - .supervise(PlayerMovementEventListener(playerMovementActor)) + .supervise( + new PlayerMovementEventListener.Props( + playerMovementActor, + ctx.self, + scheduler + ).behavior + ) .onFailure[Exception]( SupervisorStrategy.restart.withLimit(2, 100.millis) ) @@ -186,7 +192,6 @@ class PlayerActorSupervisor( case Failure(ex) => LogError(ex) } Behaviors.same - Behaviors.same case CurrentStats(replyTo) => // ctx.ask(children.statsActor, StatsActor.CurrentStats()) children.statsActor ! StatsActor.CurrentStats(replyTo) diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor2.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor2.scala deleted file mode 100644 index f1dac69..0000000 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerActorSupervisor2.scala +++ /dev/null @@ -1,149 +0,0 @@ -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 com.typesafe.scalalogging.Logger -import org.slf4j.event.Level -import wow.doge.mygame.Dispatchers -import wow.doge.mygame.game.entities.PlayerCameraActor -import wow.doge.mygame.game.entities.PlayerCameraEventListener -import wow.doge.mygame.game.entities.PlayerMovementEventListener -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 - -object PlayerActorSupervisor2 { - sealed trait Command - case object Tick extends Command - sealed trait Movement extends Command - final case class MoveLeft(pressed: Boolean) extends Movement - final case class MoveUp(pressed: Boolean) extends Movement - final case class MoveRight(pressed: Boolean) extends Movement - final case class MoveDown(pressed: Boolean) extends Movement - case object Jump extends Movement - sealed trait Camera - case object RotateLeft extends Camera - case object RotateRight extends Camera - case object RotateUp extends Camera - case object RotateDown extends Camera - class Props( - val playerEventBus: GameEventBus[PlayerEvent], - val tickEventBus: GameEventBus[TickEvent], - val imMovementActorBehavior: Behavior[ImMovementActor.Command], - val playerCameraActorBehavior: Behavior[PlayerCameraActor.Command] - ) { - def behavior = - Behaviors.logMessages( - LogOptions() - .withLevel(Level.TRACE) - .withLogger( - Logger[PlayerActorSupervisor2].underlying - ), - Behaviors - .setup[Command] { ctx => - ctx.log.infoP("Starting PlayerActor") - - // spawn children actors - val movementActor = - ctx.spawn( - Behaviors - .supervise(imMovementActorBehavior) - .onFailure[Exception]( - SupervisorStrategy.restart.withLimit(2, 100.millis) - ), - "playerMovementActor", - Dispatchers.jmeDispatcher - ) - - val playerCameraActor = - ctx.spawn( - playerCameraActorBehavior, - "playerCameraActor", - Dispatchers.jmeDispatcher - ) - - val playerCameraEl = ctx.spawn( - PlayerCameraEventListener(playerCameraActor), - "playerCameraActorEl" - ) - - val playerMovementEl = ctx.spawn( - Behaviors - .supervise(PlayerMovementEventListener(movementActor)) - .onFailure[Exception]( - SupervisorStrategy.restart.withLimit(2, 100.millis) - ), - "playerMovementEventHandler" - ) - - val renderTickEl = { - val behavior: Behavior[RenderTick.type] = - Behaviors.setup(ctx => - Behaviors - .receiveMessage[RenderTick.type] { - case RenderTick => - movementActor ! 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) - playerEventBus ! EventBus.Subscribe(playerCameraEl) - - new PlayerActorSupervisor2( - ctx, - this, - Children(movementActor) - ).receive - } - ) - - } - - final case class Children( - movementActor: ActorRef[ImMovementActor.Command] - ) -} -class PlayerActorSupervisor2( - ctx: ActorContext[PlayerActorSupervisor2.Command], - props: PlayerActorSupervisor2.Props, - children: PlayerActorSupervisor2.Children -) { - import PlayerActorSupervisor2._ - def receive = - Behaviors - .receiveMessage[Command] { - case m @ MoveDown(pressed) => - // children.movementActor ! m - Behaviors.same - case _ => - // children.movementActor ! ImMovementActor.MovedDown(true) - Behaviors.same - } - .receiveSignal { - case (_, PostStop) => - ctx.log.infoP("stopped") - Behaviors.same - } -} diff --git a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala index 4ee4799..1942a12 100644 --- a/src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala +++ b/src/main/scala/wow/doge/mygame/game/entities/player/PlayerEventListeners.scala @@ -1,39 +1,92 @@ package wow.doge.mygame.game.entities +import scala.concurrent.duration._ + import akka.actor.typed.ActorRef +import akka.actor.typed.Behavior import akka.actor.typed.LogOptions +import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors +import akka.util.Timeout import com.typesafe.scalalogging.Logger +import monix.eval.Task +import monix.execution.CancelableFuture +import monix.reactive.Observable import org.slf4j.event.Level +import wow.doge.mygame.executors.Schedulers +import wow.doge.mygame.implicits._ import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.subsystems.movement.ImMovementActor object PlayerMovementEventListener { + final case class State(staminaTimer: CancelableFuture[Unit]) + + class Props( + val movementActor: ActorRef[ImMovementActor.Command], + val statsActor: PlayerActorSupervisor.Ref, + val asyncScheduler: Schedulers.AsyncScheduler + ) { + def behavior = + Behaviors.setup[PlayerMovementEvent] { ctx => + new PlayerMovementEventListener(ctx, this) + .receive(State(CancelableFuture.unit)) + } + } +} +class PlayerMovementEventListener( + ctx: ActorContext[PlayerMovementEvent], + props: PlayerMovementEventListener.Props +) { + import PlayerMovementEventListener._ import PlayerMovementEvent._ - def apply(movementActor: ActorRef[ImMovementActor.Command]) = + def receive(state: State): Behavior[PlayerMovementEvent] = Behaviors.logMessages( LogOptions() .withLevel(Level.TRACE) .withLogger( Logger[PlayerMovementEventListener.type].underlying ), - Behaviors.setup[PlayerMovementEvent](ctx => + Behaviors.setup[PlayerMovementEvent] { ctx => + implicit val timeout = Timeout(1.second) + implicit val sched = ctx.system.scheduler + + def makeStaminaTimer = + Task.deferAction(implicit s => + Observable + .interval(250.millis) + .doOnNextF(_ => + props.statsActor.askL( + PlayerActorSupervisor + .ConsumeStamina(CharacterStats.DamageStamina(1), _) + ) + ) + .completedL + ) + + def handleStamina(pressed: Boolean) = + if (pressed) + State(makeStaminaTimer.runToFuture(props.asyncScheduler.value)) + else { + state.staminaTimer.cancel() + State(CancelableFuture.unit) + } + Behaviors.receiveMessage { case PlayerMovedLeft(pressed) => - movementActor ! ImMovementActor.MoveLeft(pressed) - Behaviors.same + props.movementActor ! ImMovementActor.MoveLeft(pressed) + receive(handleStamina(pressed)) case PlayerMovedRight(pressed) => - movementActor ! ImMovementActor.MoveRight(pressed) - Behaviors.same + props.movementActor ! ImMovementActor.MoveRight(pressed) + receive(handleStamina(pressed)) case PlayerMovedForward(pressed) => - movementActor ! ImMovementActor.MoveUp(pressed) - Behaviors.same + props.movementActor ! ImMovementActor.MoveUp(pressed) + receive(handleStamina(pressed)) case PlayerMovedBackward(pressed) => - movementActor ! ImMovementActor.MoveDown(pressed) - Behaviors.same + props.movementActor ! ImMovementActor.MoveDown(pressed) + receive(handleStamina(pressed)) case PlayerJumped => - movementActor ! ImMovementActor.Jump + props.movementActor ! ImMovementActor.Jump Behaviors.same // case PlayerTurnedRight => // movementActor ! ImMovementActor.RotateRight @@ -42,7 +95,7 @@ object PlayerMovementEventListener { // movementActor ! ImMovementActor.RotateLeft // Behaviors.same } - ) + } ) } diff --git a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala index 78647bf..6731511 100644 --- a/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala +++ b/src/main/scala/wow/doge/mygame/subsystems/scriptsystem/ScriptActor.scala @@ -169,7 +169,7 @@ class ScriptActor( case l @ Left(err) => l.map(_.taggedWith[ScriptTag]) } - type LOL = Map[os.Path, Either[wow.doge.mygame.state.ScriptActor.Error, Any]] + type LOL = Map[os.Path, Either[ScriptActor.Error, Any]] def compileAll( paths: Seq[os.Path] @@ -179,7 +179,7 @@ class ScriptActor( paths: Seq[os.Path], scriptsMap: Map[ os.Path, - Either[wow.doge.mygame.state.ScriptActor.Error, Any] + Either[ScriptActor.Error, Any] ] ): LOL = paths match {