Add stamina consumption logic

This commit is contained in:
Rohan Sircar 2021-02-28 19:32:49 +05:30
parent 12b232fb3c
commit 92aae68254
6 changed files with 77 additions and 289 deletions

View File

@ -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)
}
)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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
}
)
}
)
}

View File

@ -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 {