Many changes
Add player actor substates Added color logic to hud stats bars
This commit is contained in:
parent
1422d91b14
commit
67201c8f7e
11
src/main/resources/main.css
Normal file
11
src/main/resources/main.css
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.red-bar > .bar {
|
||||||
|
-fx-background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-bar > .bar {
|
||||||
|
-fx-background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yellow-bar > .bar {
|
||||||
|
-fx-background-color: yellow;
|
||||||
|
}
|
@ -2,6 +2,7 @@ package wow.doge.mygame
|
|||||||
|
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
|
import scala.annotation.switch
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
@ -35,6 +36,7 @@ import monix.eval.Coeval
|
|||||||
import monix.execution.cancelables.CompositeCancelable
|
import monix.execution.cancelables.CompositeCancelable
|
||||||
import monix.execution.exceptions.DummyException
|
import monix.execution.exceptions.DummyException
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
|
import monix.{eval => me}
|
||||||
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
|
||||||
@ -50,8 +52,8 @@ import wow.doge.mygame.game.entities.CharacterStats
|
|||||||
import wow.doge.mygame.game.entities.EntityIds
|
import wow.doge.mygame.game.entities.EntityIds
|
||||||
import wow.doge.mygame.game.entities.NpcActorSupervisor
|
import wow.doge.mygame.game.entities.NpcActorSupervisor
|
||||||
import wow.doge.mygame.game.entities.NpcMovementActor
|
import wow.doge.mygame.game.entities.NpcMovementActor
|
||||||
import wow.doge.mygame.game.entities.PlayerActorSupervisor
|
import wow.doge.mygame.game.entities.player.PlayerActorSupervisor
|
||||||
import wow.doge.mygame.game.entities.PlayerController
|
import wow.doge.mygame.game.entities.player.PlayerController
|
||||||
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
||||||
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
||||||
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
||||||
@ -386,6 +388,8 @@ class MainAppDelegate(
|
|||||||
hgrow = Priority.Always
|
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);"""
|
||||||
|
stylesheets = Seq((os.rel / "main.css").toString)
|
||||||
|
|
||||||
children = List(
|
children = List(
|
||||||
new HBox {
|
new HBox {
|
||||||
spacing = 5
|
spacing = 5
|
||||||
@ -398,10 +402,29 @@ class MainAppDelegate(
|
|||||||
textFill = Color.White
|
textFill = Color.White
|
||||||
},
|
},
|
||||||
new JFXProgressBar {
|
new JFXProgressBar {
|
||||||
|
|
||||||
progress = 100
|
progress = 100
|
||||||
minHeight = 10
|
minHeight = 10
|
||||||
progress <-- statsObs
|
progress <-- statsObs
|
||||||
.map(_.hp.toInt.toDouble / 100)
|
.map(_.hp.toInt.toDouble / 100)
|
||||||
|
c += statsObs
|
||||||
|
.scanEval(me.Task.pure("green-bar")) {
|
||||||
|
case (a, b) =>
|
||||||
|
me.Task {
|
||||||
|
pprint.log(show"Received $a $b")
|
||||||
|
pprint.log(show"${styleClass.toString}")
|
||||||
|
styleClass.removeAll(a)
|
||||||
|
} >>
|
||||||
|
me.Task.pure(
|
||||||
|
(b.hp.toInt: @switch) match {
|
||||||
|
case v if v > 80 => "green-bar"
|
||||||
|
case v if v > 20 && v <= 80 => "yellow-bar"
|
||||||
|
case _ => "red-bar"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.doOnNext(cls => me.Task(styleClass += cls))
|
||||||
|
.subscribe()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -419,9 +442,32 @@ class MainAppDelegate(
|
|||||||
.map(_.stamina.toInt.toString)
|
.map(_.stamina.toInt.toString)
|
||||||
},
|
},
|
||||||
new JFXProgressBar {
|
new JFXProgressBar {
|
||||||
|
|
||||||
progress = 100
|
progress = 100
|
||||||
minHeight = 10
|
minHeight = 10
|
||||||
progress <-- statsObs.map(_.stamina.toInt.toDouble / 100)
|
progress <-- statsObs.map(_.stamina.toInt.toDouble / 100)
|
||||||
|
|
||||||
|
styleClass ++= Seq("green-bar")
|
||||||
|
|
||||||
|
c += statsObs
|
||||||
|
.scanEval(me.Task.pure("green-bar")) {
|
||||||
|
case (a, b) =>
|
||||||
|
me.Task {
|
||||||
|
pprint.log(show"Received $a $b")
|
||||||
|
pprint.log(show"${styleClass.toString}")
|
||||||
|
styleClass.removeAll(a)
|
||||||
|
} >>
|
||||||
|
me.Task.pure(
|
||||||
|
(b.stamina.toInt: @switch) match {
|
||||||
|
case v if v > 80 => "green-bar"
|
||||||
|
case v if v > 20 && v <= 40 => "yellow-bar"
|
||||||
|
case _ => "red-bar"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.doOnNext(cls => me.Task(styleClass += cls))
|
||||||
|
.subscribe()
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import akka.actor.typed.Behavior
|
|||||||
import akka.actor.typed.scaladsl.ActorContext
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import cats.Show
|
import cats.Show
|
||||||
|
import cats.kernel.Eq
|
||||||
import io.estatico.newtype.macros.newtype
|
import io.estatico.newtype.macros.newtype
|
||||||
import wow.doge.mygame.game.entities.CharacterStats.HealHealth
|
import wow.doge.mygame.game.entities.CharacterStats.HealHealth
|
||||||
|
|
||||||
@ -18,10 +19,6 @@ object CharacterStats {
|
|||||||
@newtype final case class Health(toInt: Int)
|
@newtype final case class Health(toInt: Int)
|
||||||
object Health {
|
object Health {
|
||||||
implicit class HealthOps(private val h: Health) extends AnyVal {
|
implicit class HealthOps(private val h: Health) extends AnyVal {
|
||||||
// def +(v: Int): Health = Health(h.toInt + v)
|
|
||||||
// def -(v: Int): Health = Health(h.toInt - v)
|
|
||||||
// def *(v: Int): Health = Health(h.toInt * v)
|
|
||||||
// def /(v: Int): Health = Health(h.toInt / v)
|
|
||||||
def :+(v: HealHealth): Health = Health(h.toInt + v.toInt)
|
def :+(v: HealHealth): Health = Health(h.toInt + v.toInt)
|
||||||
def -(v: DamageHealth): Health = Health(h.toInt - v.toInt)
|
def -(v: DamageHealth): Health = Health(h.toInt - v.toInt)
|
||||||
}
|
}
|
||||||
@ -31,40 +28,19 @@ object CharacterStats {
|
|||||||
@newtype final case class Stamina(toInt: Int)
|
@newtype final case class Stamina(toInt: Int)
|
||||||
object Stamina {
|
object Stamina {
|
||||||
implicit class StaminaOps(private val h: Stamina) extends AnyVal {
|
implicit class StaminaOps(private val h: Stamina) extends AnyVal {
|
||||||
// def +(v: Int): Stamina = Stamina(h.toInt + v)
|
|
||||||
// def -(v: Int): Stamina = Stamina(h.toInt - v)
|
|
||||||
// def *(v: Int): Stamina = Stamina(h.toInt * v)
|
|
||||||
// def /(v: Int): Stamina = Stamina(h.toInt / v)
|
|
||||||
def :+(v: HealStamina): Stamina = Stamina(h.toInt + v.toInt)
|
def :+(v: HealStamina): Stamina = Stamina(h.toInt + v.toInt)
|
||||||
def -(v: DamageStamina): Stamina = Stamina(h.toInt - v.toInt)
|
def -(v: DamageStamina): Stamina = Stamina(h.toInt - v.toInt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// object Stamina {
|
|
||||||
// implicit class StaminaOps(private val h: Stamina) extends AnyVal {
|
|
||||||
// def +(v: Health): Stamina = Stamina(h.toInt + v.toInt)
|
|
||||||
// def -(v: Health): Stamina = Stamina(h.toInt - v.toInt)
|
|
||||||
// def *(v: Health): Stamina = Stamina(h.toInt * v.toInt)
|
|
||||||
// def /(v: Health): Stamina = Stamina(h.toInt / v.toInt)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// object Damage {
|
|
||||||
// implicit class DamageOps(private val h: Damage) extends AnyVal {
|
|
||||||
// def +(v: Health): Damage = Damage(h.toInt + v.toInt)
|
|
||||||
// def -(v: Health): Damage = Damage(h.toInt - v.toInt)
|
|
||||||
// def *(v: Health): Damage = Damage(h.toInt * v.toInt)
|
|
||||||
// def /(v: Health): Damage = Damage(h.toInt / v.toInt)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
implicit val show = Show.fromToString[CharacterStats]
|
implicit val show = Show.fromToString[CharacterStats]
|
||||||
|
implicit val eq = Eq.fromUniversalEquals[CharacterStats]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object StatsActor {
|
object StatsActor {
|
||||||
|
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
// final case class TakeDamage(value: Int) extends Command
|
|
||||||
final case class TakeDamageResult(
|
final case class TakeDamageResult(
|
||||||
value: CharacterStats.DamageHealth,
|
value: CharacterStats.DamageHealth,
|
||||||
replyTo: ActorRef[(Boolean, CharacterStats)]
|
replyTo: ActorRef[(Boolean, CharacterStats)]
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package wow.doge.mygame.game.entities.character
|
||||||
|
|
||||||
|
import cats.Show
|
||||||
|
import cats.kernel.Eq
|
||||||
|
|
||||||
|
object CharacterStates {
|
||||||
|
sealed trait AliveSubstate
|
||||||
|
object AliveSubstate {
|
||||||
|
final case class InCombat(substate: CombatSubstate) extends AliveSubstate
|
||||||
|
final case class Moving(substate: MovementSubstate) extends AliveSubstate
|
||||||
|
case object Idle extends AliveSubstate
|
||||||
|
implicit val eq = Eq.fromUniversalEquals[AliveSubstate]
|
||||||
|
implicit val show = Show.fromToString[AliveSubstate]
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait CombatSubstate
|
||||||
|
object CombatSubstate {
|
||||||
|
final case class Moving(substate: MovementSubstate) extends CombatSubstate
|
||||||
|
final case class Attacking(victimName: String) extends CombatSubstate
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait MovementSubstate
|
||||||
|
case object Walking extends MovementSubstate
|
||||||
|
case object Running extends MovementSubstate
|
||||||
|
|
||||||
|
sealed trait DeadSubstate
|
||||||
|
object DeadSubstate {
|
||||||
|
implicit val eq = Eq.fromUniversalEquals[DeadSubstate]
|
||||||
|
implicit val show = Show.fromToString[DeadSubstate]
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package wow.doge.mygame.game.entities
|
package wow.doge.mygame.game.entities.player
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.util.Failure
|
|
||||||
import scala.util.Success
|
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
@ -12,14 +10,18 @@ import akka.actor.typed.SupervisorStrategy
|
|||||||
import akka.actor.typed.scaladsl.ActorContext
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import cats.syntax.show._
|
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
|
import monix.bio.UIO
|
||||||
import monix.execution.AsyncQueue
|
import monix.execution.AsyncQueue
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import org.slf4j.event.Level
|
import org.slf4j.event.Level
|
||||||
import wow.doge.mygame.Dispatchers
|
import wow.doge.mygame.Dispatchers
|
||||||
|
import wow.doge.mygame.executors.Schedulers
|
||||||
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
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.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.implicits._
|
||||||
import wow.doge.mygame.subsystems.events.EventBus
|
import wow.doge.mygame.subsystems.events.EventBus
|
||||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||||
@ -27,8 +29,8 @@ import wow.doge.mygame.subsystems.events.PlayerEvent
|
|||||||
import wow.doge.mygame.subsystems.events.TickEvent
|
import wow.doge.mygame.subsystems.events.TickEvent
|
||||||
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
import wow.doge.mygame.subsystems.events.TickEvent.RenderTick
|
||||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
import wow.doge.mygame.executors.Schedulers
|
|
||||||
import monix.bio.UIO
|
//TODO: Change name to PlayerActor
|
||||||
object PlayerActorSupervisor {
|
object PlayerActorSupervisor {
|
||||||
|
|
||||||
type Ref = ActorRef[PlayerActorSupervisor.Command]
|
type Ref = ActorRef[PlayerActorSupervisor.Command]
|
||||||
@ -56,26 +58,28 @@ object PlayerActorSupervisor {
|
|||||||
replyTo: ActorRef[UIO[Observable[CharacterStats]]]
|
replyTo: ActorRef[UIO[Observable[CharacterStats]]]
|
||||||
) extends Command
|
) extends Command
|
||||||
|
|
||||||
private case object Die extends Command
|
sealed trait Movement extends Command
|
||||||
private final case class DamageResponse(
|
final case class MoveLeft(pressed: Boolean) extends Movement
|
||||||
|
|
||||||
|
private[player] case object Die extends Command
|
||||||
|
private[player] final case class StatsResponse(
|
||||||
response: (Boolean, CharacterStats),
|
response: (Boolean, CharacterStats),
|
||||||
replyTo: ActorRef[Unit]
|
replyTo: ActorRef[Unit]
|
||||||
) extends Command
|
) extends Command
|
||||||
private final case class LogError(ex: Throwable) extends Command
|
private[player] final case class LogError(ex: Throwable) extends Command
|
||||||
class Props(
|
class Props(
|
||||||
val playerEventBus: GameEventBus[PlayerEvent],
|
val playerEventBus: GameEventBus[PlayerEvent],
|
||||||
val tickEventBus: GameEventBus[TickEvent],
|
val tickEventBus: GameEventBus[TickEvent],
|
||||||
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||||
val scheduler: AsyncScheduler,
|
val scheduler: AsyncScheduler,
|
||||||
val fxScheduler: Schedulers.FxScheduler
|
val fxScheduler: Schedulers.FxScheduler,
|
||||||
|
val statsQueue: AsyncQueue[CharacterStats]
|
||||||
) {
|
) {
|
||||||
def behavior =
|
def behavior =
|
||||||
Behaviors.logMessages(
|
Behaviors.logMessages(
|
||||||
LogOptions()
|
LogOptions()
|
||||||
.withLevel(Level.DEBUG)
|
.withLevel(Level.DEBUG)
|
||||||
.withLogger(
|
.withLogger(Logger[PlayerActorSupervisor].underlying),
|
||||||
Logger[PlayerActorSupervisor].underlying
|
|
||||||
),
|
|
||||||
Behaviors
|
Behaviors
|
||||||
.setup[Command] { ctx =>
|
.setup[Command] { ctx =>
|
||||||
ctx.log.infoP("Starting PlayerActor")
|
ctx.log.infoP("Starting PlayerActor")
|
||||||
@ -140,9 +144,8 @@ object PlayerActorSupervisor {
|
|||||||
new PlayerActorSupervisor(
|
new PlayerActorSupervisor(
|
||||||
ctx,
|
ctx,
|
||||||
this,
|
this,
|
||||||
Children(playerMovementActor, playerStatsActor),
|
Children(playerMovementActor, playerStatsActor)
|
||||||
AsyncQueue.bounded(10)(scheduler.value)
|
).aliveState(AliveSubstate.Idle)
|
||||||
).aliveState
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -152,77 +155,24 @@ object PlayerActorSupervisor {
|
|||||||
movementActor: ActorRef[ImMovementActor.Command],
|
movementActor: ActorRef[ImMovementActor.Command],
|
||||||
statsActor: ActorRef[StatsActor.Command]
|
statsActor: ActorRef[StatsActor.Command]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
final case class Env(
|
||||||
|
ctx: ActorContext[PlayerActorSupervisor.Command],
|
||||||
|
props: PlayerActorSupervisor.Props,
|
||||||
|
children: PlayerActorSupervisor.Children
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlayerActorSupervisor(
|
class PlayerActorSupervisor(
|
||||||
ctx: ActorContext[PlayerActorSupervisor.Command],
|
ctx: ActorContext[PlayerActorSupervisor.Command],
|
||||||
props: PlayerActorSupervisor.Props,
|
props: PlayerActorSupervisor.Props,
|
||||||
children: PlayerActorSupervisor.Children,
|
children: PlayerActorSupervisor.Children
|
||||||
statsQueue: AsyncQueue[CharacterStats]
|
|
||||||
) {
|
) {
|
||||||
import PlayerActorSupervisor._
|
import PlayerActorSupervisor._
|
||||||
implicit val timeout = Timeout(1.second)
|
implicit val timeout = Timeout(1.second)
|
||||||
val aliveState =
|
|
||||||
Behaviors
|
|
||||||
.receiveMessage[Command] {
|
|
||||||
case TakeDamage(value, replyTo) =>
|
|
||||||
ctx.ask(children.statsActor, StatsActor.TakeDamageResult(value, _)) {
|
|
||||||
case Success(response) => DamageResponse(response, replyTo)
|
|
||||||
case Failure(ex) => LogError(ex)
|
|
||||||
}
|
|
||||||
Behaviors.same
|
|
||||||
case ConsumeStamina(value, replyTo) =>
|
|
||||||
ctx.ask(
|
|
||||||
children.statsActor,
|
|
||||||
StatsActor.ConsumeStaminaResult(value, _)
|
|
||||||
) {
|
|
||||||
case Success(response) => DamageResponse(response, replyTo)
|
|
||||||
case Failure(ex) => LogError(ex)
|
|
||||||
}
|
|
||||||
Behaviors.same
|
|
||||||
case CurrentStats(replyTo) =>
|
|
||||||
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
|
||||||
Behaviors.same
|
|
||||||
case Heal(value) =>
|
|
||||||
children.statsActor ! StatsActor.HealResult(value)
|
|
||||||
Behaviors.same
|
|
||||||
case GetStatus(replyTo) =>
|
|
||||||
replyTo ! Status.Alive
|
|
||||||
Behaviors.same
|
|
||||||
case GetStatsObservable(replyTo) =>
|
|
||||||
import monix.{eval => me}
|
|
||||||
replyTo !
|
|
||||||
UIO(
|
|
||||||
Observable
|
|
||||||
.repeatEvalF(
|
|
||||||
me.Task.deferFuture(statsQueue.poll())
|
|
||||||
)
|
|
||||||
.publish(props.fxScheduler.value)
|
|
||||||
.refCount
|
|
||||||
)
|
|
||||||
|
|
||||||
Behaviors.same
|
val env = Env(ctx, props, children)
|
||||||
case DamageResponse(response, replyTo) =>
|
|
||||||
response match {
|
|
||||||
case (dead, stats) =>
|
|
||||||
if (dead) ctx.self ! Die
|
|
||||||
statsQueue
|
|
||||||
.offer(stats)
|
|
||||||
.foreach { _ =>
|
|
||||||
pprint.log(show"Published stats $stats")
|
|
||||||
replyTo ! ()
|
|
||||||
}(props.scheduler.value)
|
|
||||||
}
|
|
||||||
Behaviors.same
|
|
||||||
case Die => deadState
|
|
||||||
case LogError(ex) =>
|
|
||||||
ctx.log.error(ex.getMessage)
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
.receiveSignal {
|
|
||||||
case (_, PostStop) =>
|
|
||||||
ctx.log.infoP("stopped")
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
val deadState = Behaviors
|
val deadState = Behaviors
|
||||||
.receiveMessage[Command] {
|
.receiveMessage[Command] {
|
||||||
// case TakeDamage(value) =>
|
// case TakeDamage(value) =>
|
||||||
@ -249,4 +199,43 @@ class PlayerActorSupervisor(
|
|||||||
ctx.log.infoP("stopped")
|
ctx.log.infoP("stopped")
|
||||||
Behaviors.same
|
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(substate) =>
|
||||||
|
substate match {
|
||||||
|
case Walking =>
|
||||||
|
ctx.log.debugP("In Walking State")
|
||||||
|
idleBehavior(aliveState, _ * 2).value
|
||||||
|
case Running =>
|
||||||
|
idleBehavior(aliveState, _ * 3).value
|
||||||
|
}
|
||||||
|
case AliveSubstate.Idle =>
|
||||||
|
ctx.log.debugP("In Idle State")
|
||||||
|
idleBehavior(aliveState, identity).value
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package wow.doge.mygame.game.entities
|
package wow.doge.mygame.game.entities.player
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.DispatcherSelector
|
import akka.actor.typed.DispatcherSelector
|
||||||
@ -13,7 +13,9 @@ import com.softwaremill.tagging._
|
|||||||
import io.odin.Logger
|
import io.odin.Logger
|
||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
|
import monix.execution.AsyncQueue
|
||||||
import wow.doge.mygame.AppError
|
import wow.doge.mygame.AppError
|
||||||
|
import wow.doge.mygame.executors.Schedulers
|
||||||
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
import wow.doge.mygame.executors.Schedulers.AsyncScheduler
|
||||||
import wow.doge.mygame.game.GameApp
|
import wow.doge.mygame.game.GameApp
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
@ -24,7 +26,6 @@ import wow.doge.mygame.subsystems.events.TickEvent
|
|||||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
import wow.doge.mygame.types._
|
import wow.doge.mygame.types._
|
||||||
import wow.doge.mygame.utils.wrappers.jme._
|
import wow.doge.mygame.utils.wrappers.jme._
|
||||||
import wow.doge.mygame.executors.Schedulers
|
|
||||||
|
|
||||||
object PlayerController {
|
object PlayerController {
|
||||||
sealed trait Error
|
sealed trait Error
|
||||||
@ -69,7 +70,8 @@ object PlayerController {
|
|||||||
tickEventBus,
|
tickEventBus,
|
||||||
movementActorBeh,
|
movementActorBeh,
|
||||||
scheduler,
|
scheduler,
|
||||||
fxScheduler
|
fxScheduler,
|
||||||
|
AsyncQueue.bounded(10)(scheduler.value)
|
||||||
).behavior
|
).behavior
|
||||||
}
|
}
|
||||||
val playerActor =
|
val playerActor =
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package wow.doge.mygame.game.entities
|
package wow.doge.mygame.game.entities.player
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
@ -14,8 +14,8 @@ import monix.execution.CancelableFuture
|
|||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import org.slf4j.event.Level
|
import org.slf4j.event.Level
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
|
import wow.doge.mygame.game.entities.CharacterStats
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
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
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ object PlayerMovementEventListener {
|
|||||||
|
|
||||||
class Props(
|
class Props(
|
||||||
val movementActor: ActorRef[ImMovementActor.Command],
|
val movementActor: ActorRef[ImMovementActor.Command],
|
||||||
val statsActor: PlayerActorSupervisor.Ref,
|
val playerActor: PlayerActorSupervisor.Ref,
|
||||||
val asyncScheduler: Schedulers.AsyncScheduler
|
val asyncScheduler: Schedulers.AsyncScheduler
|
||||||
) {
|
) {
|
||||||
def behavior =
|
def behavior =
|
||||||
@ -56,7 +56,7 @@ class PlayerMovementEventListener(
|
|||||||
Observable
|
Observable
|
||||||
.interval(250.millis)
|
.interval(250.millis)
|
||||||
.doOnNextF(_ =>
|
.doOnNextF(_ =>
|
||||||
props.statsActor.askL(
|
props.playerActor.askL(
|
||||||
PlayerActorSupervisor
|
PlayerActorSupervisor
|
||||||
.ConsumeStamina(CharacterStats.DamageStamina(1), _)
|
.ConsumeStamina(CharacterStats.DamageStamina(1), _)
|
||||||
)
|
)
|
||||||
@ -64,9 +64,12 @@ class PlayerMovementEventListener(
|
|||||||
.completedL
|
.completedL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@SuppressWarnings(Array("org.wartremover.warts.Equals"))
|
||||||
def handleStamina(pressed: Boolean) =
|
def handleStamina(pressed: Boolean) =
|
||||||
if (pressed)
|
if (pressed)
|
||||||
State(makeStaminaTimer.runToFuture(props.asyncScheduler.value))
|
if (state.staminaTimer == CancelableFuture.unit)
|
||||||
|
State(makeStaminaTimer.runToFuture(props.asyncScheduler.value))
|
||||||
|
else state
|
||||||
else {
|
else {
|
||||||
state.staminaTimer.cancel()
|
state.staminaTimer.cancel()
|
||||||
State(CancelableFuture.unit)
|
State(CancelableFuture.unit)
|
||||||
@ -74,7 +77,8 @@ class PlayerMovementEventListener(
|
|||||||
|
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
case PlayerMovedLeft(pressed) =>
|
case PlayerMovedLeft(pressed) =>
|
||||||
props.movementActor ! ImMovementActor.MoveLeft(pressed)
|
// props.movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||||
|
props.playerActor ! PlayerActorSupervisor.MoveLeft(pressed)
|
||||||
receive(handleStamina(pressed))
|
receive(handleStamina(pressed))
|
||||||
case PlayerMovedRight(pressed) =>
|
case PlayerMovedRight(pressed) =>
|
||||||
props.movementActor ! ImMovementActor.MoveRight(pressed)
|
props.movementActor ! ImMovementActor.MoveRight(pressed)
|
||||||
@ -98,32 +102,3 @@ class PlayerMovementEventListener(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//not used
|
|
||||||
object PlayerCameraEventListener {
|
|
||||||
import PlayerCameraEvent._
|
|
||||||
def apply(playerCameraActor: ActorRef[PlayerCameraActor.Command]) =
|
|
||||||
Behaviors.logMessages(
|
|
||||||
LogOptions()
|
|
||||||
.withLevel(Level.TRACE)
|
|
||||||
.withLogger(
|
|
||||||
Logger[PlayerCameraEventListener.type].underlying
|
|
||||||
),
|
|
||||||
Behaviors.setup[PlayerCameraEvent](ctx =>
|
|
||||||
Behaviors.receiveMessagePartial {
|
|
||||||
case CameraMovedUp =>
|
|
||||||
playerCameraActor ! PlayerCameraActor.RotateUp
|
|
||||||
Behaviors.same
|
|
||||||
case CameraMovedDown =>
|
|
||||||
playerCameraActor ! PlayerCameraActor.RotateDown
|
|
||||||
Behaviors.same
|
|
||||||
case CameraLeft =>
|
|
||||||
playerCameraActor ! PlayerCameraActor.RotateLeft
|
|
||||||
Behaviors.same
|
|
||||||
case CameraRight =>
|
|
||||||
playerCameraActor ! PlayerCameraActor.RotateRight
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
package wow.doge.mygame.game.entities.player.behaviors
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.PostStop
|
||||||
|
import akka.actor.typed.scaladsl.AskPattern._
|
||||||
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
|
import akka.util.Timeout
|
||||||
|
import cats.syntax.show._
|
||||||
|
import monix.bio.UIO
|
||||||
|
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.implicits._
|
||||||
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
|
|
||||||
|
class IdleBehaviorFactory(
|
||||||
|
env: PlayerActorSupervisor.Env,
|
||||||
|
nextStateFn: AliveSubstate => Behavior[PlayerActorSupervisor.Command],
|
||||||
|
deadState: Behavior[PlayerActorSupervisor.Command],
|
||||||
|
consumptionMultiplier: Int => Int
|
||||||
|
)(implicit timeout: Timeout) {
|
||||||
|
import IdleBehaviorFactory.IdleBehavior
|
||||||
|
import env._
|
||||||
|
|
||||||
|
implicit val sched = ctx.system.scheduler
|
||||||
|
|
||||||
|
def behavior =
|
||||||
|
IdleBehavior(
|
||||||
|
Behaviors
|
||||||
|
.receiveMessage[PlayerActorSupervisor.Command] {
|
||||||
|
case PlayerActorSupervisor.MoveLeft(pressed) =>
|
||||||
|
children.movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||||
|
if (pressed)
|
||||||
|
nextStateFn(AliveSubstate.Moving(Walking))
|
||||||
|
else nextStateFn(AliveSubstate.Idle)
|
||||||
|
case PlayerActorSupervisor.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)
|
||||||
|
)
|
||||||
|
} yield ()
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerActorSupervisor.ConsumeStamina(value, replyTo) =>
|
||||||
|
implicit val ec = props.scheduler.value
|
||||||
|
val newValue =
|
||||||
|
CharacterStats.DamageStamina(consumptionMultiplier(value.toInt))
|
||||||
|
for {
|
||||||
|
response <- children.statsActor.ask(
|
||||||
|
StatsActor.ConsumeStaminaResult(newValue, _)
|
||||||
|
)
|
||||||
|
_ =
|
||||||
|
ctx.self ! PlayerActorSupervisor
|
||||||
|
.StatsResponse(response, replyTo)
|
||||||
|
} yield ()
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerActorSupervisor.CurrentStats(replyTo) =>
|
||||||
|
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerActorSupervisor.Heal(value) =>
|
||||||
|
children.statsActor ! StatsActor.HealResult(value)
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerActorSupervisor.GetStatus(replyTo) =>
|
||||||
|
replyTo ! PlayerActorSupervisor.Status.Alive
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerActorSupervisor.GetStatsObservable(replyTo) =>
|
||||||
|
import monix.{eval => me}
|
||||||
|
replyTo !
|
||||||
|
UIO(
|
||||||
|
Observable
|
||||||
|
.repeatEvalF(
|
||||||
|
me.Task.deferFuture(props.statsQueue.poll())
|
||||||
|
)
|
||||||
|
.publish(props.fxScheduler.value)
|
||||||
|
.refCount
|
||||||
|
)
|
||||||
|
|
||||||
|
Behaviors.same
|
||||||
|
case PlayerActorSupervisor.StatsResponse(response, replyTo) =>
|
||||||
|
response match {
|
||||||
|
case (dead, stats) =>
|
||||||
|
if (dead) ctx.self ! PlayerActorSupervisor.Die
|
||||||
|
props.statsQueue
|
||||||
|
.offer(stats)
|
||||||
|
.foreach { _ =>
|
||||||
|
pprint.log(show"Published stats $stats")
|
||||||
|
replyTo ! ()
|
||||||
|
}(props.scheduler.value)
|
||||||
|
}
|
||||||
|
Behaviors.same
|
||||||
|
// nextStateFn(InCombat(Moving(Walking)))
|
||||||
|
case PlayerActorSupervisor.Die => deadState
|
||||||
|
case PlayerActorSupervisor.LogError(ex) =>
|
||||||
|
ctx.log.error(ex.getMessage)
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
ctx.log.infoP("stopped")
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object IdleBehaviorFactory {
|
||||||
|
final case class IdleBehavior(
|
||||||
|
value: Behavior[PlayerActorSupervisor.Command]
|
||||||
|
)
|
||||||
|
}
|
@ -12,11 +12,14 @@ import wow.doge.mygame.game.subsystems.movement.CanMove
|
|||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
|
|
||||||
final case class CardinalDirection(
|
final case class CardinalDirection(
|
||||||
left: Boolean = false,
|
left: Boolean,
|
||||||
right: Boolean = false,
|
right: Boolean,
|
||||||
up: Boolean = false,
|
up: Boolean,
|
||||||
down: Boolean = false
|
down: Boolean
|
||||||
)
|
)
|
||||||
|
object CardinalDirection {
|
||||||
|
val default = CardinalDirection(false, false, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
sealed trait RotateDir
|
sealed trait RotateDir
|
||||||
object RotateDir {
|
object RotateDir {
|
||||||
@ -53,7 +56,9 @@ object ImMovementActor {
|
|||||||
*
|
*
|
||||||
* @param cardinalDir The four directions the character can move
|
* @param cardinalDir The four directions the character can move
|
||||||
*/
|
*/
|
||||||
final case class State(cardinalDir: CardinalDirection = CardinalDirection())
|
final case class State(
|
||||||
|
cardinalDir: CardinalDirection = CardinalDirection.default
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +193,7 @@ object MovementActor {
|
|||||||
* @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor
|
* @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor
|
||||||
*/
|
*/
|
||||||
final case class State(
|
final case class State(
|
||||||
cardinalDir: CardinalDirection = CardinalDirection(),
|
cardinalDir: CardinalDirection = CardinalDirection.default,
|
||||||
walkDirection: Vector3f = Vector3f.UNIT_X
|
walkDirection: Vector3f = Vector3f.UNIT_X
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
package wow.doge.mygame.utils.controls
|
package wow.doge.mygame.utils.controls
|
||||||
// import com.jfoenix.controls.JFXProgressBar
|
|
||||||
import com.jfoenix.{controls => jfoenixc}
|
import com.jfoenix.{controls => jfoenixc}
|
||||||
import scalafx.scene.control.ProgressBar
|
import scalafx.scene.control.ProgressBar
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user