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.Label
import scalafx.scene.control.TextArea import scalafx.scene.control.TextArea
import scalafx.scene.layout.HBox import scalafx.scene.layout.HBox
import scalafx.scene.layout.Priority
import scalafx.scene.layout.VBox import scalafx.scene.layout.VBox
import scalafx.scene.paint.Color import scalafx.scene.paint.Color
import wow.doge.mygame.AppError.TimeoutError import wow.doge.mygame.AppError.TimeoutError
@ -381,6 +382,7 @@ class MainAppDelegate(
.deferAction(implicit s => .deferAction(implicit s =>
UIO(new VBox { UIO(new VBox {
implicit val c = CompositeCancelable() implicit val c = CompositeCancelable()
hgrow = Priority.Always
spacing = 10 spacing = 10
style = """-fx-background-color: rgba(0,0,0,0.7);""" style = """-fx-background-color: rgba(0,0,0,0.7);"""
children = List( children = List(
@ -419,8 +421,7 @@ class MainAppDelegate(
new JFXProgressBar { new JFXProgressBar {
progress = 100 progress = 100
minHeight = 10 minHeight = 10
progress <-- statsObs progress <-- statsObs.map(_.stamina.toInt.toDouble / 100)
.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.SpawnProtocol
import akka.actor.typed.scaladsl.AskPattern._ import akka.actor.typed.scaladsl.AskPattern._
import akka.util.Timeout import akka.util.Timeout
import cats.Show
import cats.effect.Resource import cats.effect.Resource
import cats.effect.concurrent.Deferred
import cats.syntax.show._
import com.jme3.bullet.BulletAppState import com.jme3.bullet.BulletAppState
import com.jme3.input.InputManager import com.jme3.input.InputManager
import com.jme3.scene.Node
import com.jme3.scene.Spatial
import com.jme3.system.AppSettings import com.jme3.system.AppSettings
import com.softwaremill.tagging._ import com.softwaremill.tagging._
import com.typesafe.scalalogging.{Logger => SLogger}
import io.odin.Logger import io.odin.Logger
import monix.bio.Fiber import monix.bio.Fiber
import monix.bio.IO import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import monix.bio.UIO 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
import wow.doge.mygame.AppError.TimeoutError import wow.doge.mygame.AppError.TimeoutError
import wow.doge.mygame.Dispatchers import wow.doge.mygame.Dispatchers
@ -137,116 +128,3 @@ class GameAppResource(
case Left(error) => IO.terminate(new Exception(error.toString)) 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( val playerMovementEl = ctx.spawnN(
Behaviors Behaviors
.supervise(PlayerMovementEventListener(playerMovementActor)) .supervise(
new PlayerMovementEventListener.Props(
playerMovementActor,
ctx.self,
scheduler
).behavior
)
.onFailure[Exception]( .onFailure[Exception](
SupervisorStrategy.restart.withLimit(2, 100.millis) SupervisorStrategy.restart.withLimit(2, 100.millis)
) )
@ -186,7 +192,6 @@ class PlayerActorSupervisor(
case Failure(ex) => LogError(ex) case Failure(ex) => LogError(ex)
} }
Behaviors.same Behaviors.same
Behaviors.same
case CurrentStats(replyTo) => case CurrentStats(replyTo) =>
// ctx.ask(children.statsActor, StatsActor.CurrentStats()) // ctx.ask(children.statsActor, StatsActor.CurrentStats())
children.statsActor ! StatsActor.CurrentStats(replyTo) 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 package wow.doge.mygame.game.entities
import scala.concurrent.duration._
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.LogOptions import akka.actor.typed.LogOptions
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout
import com.typesafe.scalalogging.Logger import com.typesafe.scalalogging.Logger
import monix.eval.Task
import monix.execution.CancelableFuture
import monix.reactive.Observable
import org.slf4j.event.Level 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.PlayerCameraEvent
import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.subsystems.events.PlayerMovementEvent
import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.subsystems.movement.ImMovementActor
object PlayerMovementEventListener { 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._ import PlayerMovementEvent._
def apply(movementActor: ActorRef[ImMovementActor.Command]) = def receive(state: State): Behavior[PlayerMovementEvent] =
Behaviors.logMessages( Behaviors.logMessages(
LogOptions() LogOptions()
.withLevel(Level.TRACE) .withLevel(Level.TRACE)
.withLogger( .withLogger(
Logger[PlayerMovementEventListener.type].underlying 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 { Behaviors.receiveMessage {
case PlayerMovedLeft(pressed) => case PlayerMovedLeft(pressed) =>
movementActor ! ImMovementActor.MoveLeft(pressed) props.movementActor ! ImMovementActor.MoveLeft(pressed)
Behaviors.same receive(handleStamina(pressed))
case PlayerMovedRight(pressed) => case PlayerMovedRight(pressed) =>
movementActor ! ImMovementActor.MoveRight(pressed) props.movementActor ! ImMovementActor.MoveRight(pressed)
Behaviors.same receive(handleStamina(pressed))
case PlayerMovedForward(pressed) => case PlayerMovedForward(pressed) =>
movementActor ! ImMovementActor.MoveUp(pressed) props.movementActor ! ImMovementActor.MoveUp(pressed)
Behaviors.same receive(handleStamina(pressed))
case PlayerMovedBackward(pressed) => case PlayerMovedBackward(pressed) =>
movementActor ! ImMovementActor.MoveDown(pressed) props.movementActor ! ImMovementActor.MoveDown(pressed)
Behaviors.same receive(handleStamina(pressed))
case PlayerJumped => case PlayerJumped =>
movementActor ! ImMovementActor.Jump props.movementActor ! ImMovementActor.Jump
Behaviors.same Behaviors.same
// case PlayerTurnedRight => // case PlayerTurnedRight =>
// movementActor ! ImMovementActor.RotateRight // movementActor ! ImMovementActor.RotateRight
@ -42,7 +95,7 @@ object PlayerMovementEventListener {
// movementActor ! ImMovementActor.RotateLeft // movementActor ! ImMovementActor.RotateLeft
// Behaviors.same // Behaviors.same
} }
) }
) )
} }

View File

@ -169,7 +169,7 @@ class ScriptActor(
case l @ Left(err) => l.map(_.taggedWith[ScriptTag]) 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( def compileAll(
paths: Seq[os.Path] paths: Seq[os.Path]
@ -179,7 +179,7 @@ class ScriptActor(
paths: Seq[os.Path], paths: Seq[os.Path],
scriptsMap: Map[ scriptsMap: Map[
os.Path, os.Path,
Either[wow.doge.mygame.state.ScriptActor.Error, Any] Either[ScriptActor.Error, Any]
] ]
): LOL = ): LOL =
paths match { paths match {