Add stamina regen logic
This commit is contained in:
parent
1b9bb4265f
commit
9b484e895b
@ -285,6 +285,7 @@ class MainAppDelegate(
|
||||
(if (event.victimName === "PlayerNode")
|
||||
playerActor
|
||||
.askL(PlayerActor.TakeDamage(event.amount, _))
|
||||
.void
|
||||
.onErrorHandle { case ex: TimeoutException => () }
|
||||
else IO.unit)).toTask
|
||||
)
|
||||
|
@ -7,7 +7,6 @@ import akka.actor.typed.scaladsl.Behaviors
|
||||
import cats.Show
|
||||
import cats.kernel.Eq
|
||||
import io.estatico.newtype.macros.newtype
|
||||
import wow.doge.mygame.game.entities.CharacterStats.HealHealth
|
||||
|
||||
final case class CharacterStats(
|
||||
hp: CharacterStats.Health,
|
||||
@ -39,17 +38,32 @@ object CharacterStats {
|
||||
}
|
||||
|
||||
object StatsActor {
|
||||
import CharacterStats._
|
||||
|
||||
sealed trait Status
|
||||
object Status {
|
||||
case object Alive extends Status
|
||||
case object Dead extends Status
|
||||
}
|
||||
|
||||
sealed trait Command
|
||||
final case class TakeDamageResult(
|
||||
value: CharacterStats.DamageHealth,
|
||||
replyTo: ActorRef[(Boolean, CharacterStats)]
|
||||
replyTo: ActorRef[(Status, CharacterStats)]
|
||||
) extends Command
|
||||
final case class ConsumeStaminaResult(
|
||||
value: CharacterStats.DamageStamina,
|
||||
replyTo: ActorRef[(Boolean, CharacterStats)]
|
||||
replyTo: ActorRef[(Status, CharacterStats)]
|
||||
) extends Command
|
||||
final case class HealHealthResult(
|
||||
value: HealHealth,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
|
||||
final case class HealStaminaResult(
|
||||
value: HealStamina,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class HealResult(value: HealHealth) extends Command
|
||||
final case class CurrentStats(replyTo: ActorRef[CharacterStats])
|
||||
extends Command
|
||||
|
||||
@ -88,27 +102,35 @@ class StatsActor(
|
||||
case TakeDamageResult(value, replyTo) =>
|
||||
val nextState = if ((state.stats.hp - value).toInt <= 0) {
|
||||
val s = state.modify(_.stats.hp).setTo(Health(0))
|
||||
replyTo ! true -> s.stats
|
||||
replyTo ! Status.Dead -> s.stats
|
||||
s
|
||||
} else {
|
||||
val s = state.modify(_.stats.hp).using(_ - value)
|
||||
replyTo ! false -> s.stats
|
||||
replyTo ! Status.Alive -> s.stats
|
||||
s
|
||||
}
|
||||
receive(nextState)
|
||||
case ConsumeStaminaResult(value, replyTo) =>
|
||||
val nextState = if ((state.stats.stamina - value).toInt <= 0) {
|
||||
val s = state.modify(_.stats.stamina).setTo(Stamina(0))
|
||||
replyTo ! false -> s.stats
|
||||
replyTo ! Status.Alive -> s.stats
|
||||
s
|
||||
} else {
|
||||
val s = state.modify(_.stats.stamina).using(_ - value)
|
||||
replyTo ! false -> s.stats
|
||||
replyTo ! Status.Alive -> s.stats
|
||||
s
|
||||
}
|
||||
receive(nextState)
|
||||
case HealResult(value) =>
|
||||
receive(state.modify(_.stats.hp).using(_ :+ value))
|
||||
case HealHealthResult(value, replyTo) =>
|
||||
val nextState = receive(state.modify(_.stats.hp).using(_ :+ value))
|
||||
replyTo ! state.stats
|
||||
nextState
|
||||
|
||||
case HealStaminaResult(value, replyTo) =>
|
||||
val nextState = receive(state.modify(_.stats.stamina).using(_ :+ value))
|
||||
replyTo ! state.stats
|
||||
nextState
|
||||
|
||||
case CurrentStats(replyTo) =>
|
||||
replyTo ! state.stats
|
||||
Behaviors.same
|
||||
|
@ -43,13 +43,20 @@ object PlayerActor {
|
||||
sealed trait Command extends Product with Serializable
|
||||
final case class TakeDamage(
|
||||
value: CharacterStats.DamageHealth,
|
||||
replyTo: ActorRef[Unit]
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class ConsumeStamina(
|
||||
value: CharacterStats.DamageStamina,
|
||||
replyTo: ActorRef[Unit]
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class HealHealth(
|
||||
value: CharacterStats.HealHealth,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class HealStamina(
|
||||
value: CharacterStats.HealStamina,
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
final case class Heal(value: CharacterStats.HealHealth) extends Command
|
||||
final case class CurrentStats(replyTo: ActorRef[CharacterStats])
|
||||
extends Command
|
||||
final case class GetStatus(replyTo: ActorRef[Status]) extends Command
|
||||
@ -69,8 +76,8 @@ object PlayerActor {
|
||||
|
||||
private[player] case object Die extends Command
|
||||
private[player] final case class StatsResponse(
|
||||
response: (Boolean, CharacterStats),
|
||||
replyTo: ActorRef[Unit]
|
||||
response: (StatsActor.Status, CharacterStats),
|
||||
replyTo: ActorRef[CharacterStats]
|
||||
) extends Command
|
||||
private[player] final case class LogError(ex: Throwable) extends Command
|
||||
class Props(
|
||||
@ -225,14 +232,14 @@ class PlayerActor(
|
||||
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.Moving(Walking) =>
|
||||
ctx.log.debugP("In Walking State")
|
||||
idleBehavior(aliveState, _ * 2).value
|
||||
|
||||
case AliveSubstate.Moving(Running) =>
|
||||
idleBehavior(aliveState, _ * 3).value
|
||||
|
||||
case AliveSubstate.Idle =>
|
||||
ctx.log.debugP("In Idle State")
|
||||
idleBehavior(aliveState, identity).value
|
||||
|
@ -78,7 +78,7 @@ object PlayerController {
|
||||
statsActorBeh,
|
||||
scheduler,
|
||||
fxScheduler,
|
||||
AsyncQueue.bounded(10)(scheduler.value)
|
||||
AsyncQueue.bounded(50)(scheduler.value)
|
||||
).behavior
|
||||
}
|
||||
val playerActor =
|
||||
|
@ -20,7 +20,11 @@ import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
||||
|
||||
object PlayerMovementEventListener {
|
||||
final case class State(keysPressed: Int, staminaTimer: CancelableFuture[Unit])
|
||||
final case class State(
|
||||
keysPressed: Int,
|
||||
staminaTimer: CancelableFuture[Unit],
|
||||
staminaRegenTimer: CancelableFuture[Unit]
|
||||
)
|
||||
|
||||
class Props(
|
||||
val playerActor: PlayerActor.Ref,
|
||||
@ -29,7 +33,7 @@ object PlayerMovementEventListener {
|
||||
def behavior =
|
||||
Behaviors.setup[PlayerMovementEvent] { ctx =>
|
||||
new PlayerMovementEventListener(ctx, this)
|
||||
.receive(State(0, CancelableFuture.unit))
|
||||
.receive(State(0, CancelableFuture.unit, CancelableFuture.unit))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,27 +55,47 @@ class PlayerMovementEventListener(
|
||||
implicit val timeout = Timeout(1.second)
|
||||
implicit val sched = ctx.system.scheduler
|
||||
|
||||
def makeStaminaTimer =
|
||||
val staminaTimer =
|
||||
Task.deferAction(implicit s =>
|
||||
Observable
|
||||
.interval(250.millis)
|
||||
.doOnNext(_ => Task(pprint.log("Sending Stamina Consume Item")))
|
||||
.doOnNextF(_ =>
|
||||
props.playerActor.askL(
|
||||
PlayerActor
|
||||
.ConsumeStamina(CharacterStats.DamageStamina(1), _)
|
||||
)
|
||||
props.playerActor
|
||||
.askL(
|
||||
PlayerActor
|
||||
.ConsumeStamina(CharacterStats.DamageStamina(25), _)
|
||||
)
|
||||
.void
|
||||
)
|
||||
.completedL
|
||||
)
|
||||
|
||||
def handleStamina(pressed: Boolean) =
|
||||
val staminaRegenTimer =
|
||||
Task.deferAction(implicit s =>
|
||||
Observable
|
||||
.interval(500.millis)
|
||||
.doOnNext(_ => Task(pprint.log("Sending Stamina Regen Item")))
|
||||
.mapEvalF(_ =>
|
||||
props.playerActor.askL(
|
||||
PlayerActor
|
||||
.HealStamina(CharacterStats.HealStamina(1), _)
|
||||
)
|
||||
)
|
||||
.takeWhile(_.stamina.toInt =!= 100)
|
||||
.delayExecution(1.second)
|
||||
.completedL
|
||||
)
|
||||
|
||||
def handleStamina(pressed: Boolean) = {
|
||||
state.staminaRegenTimer.cancel()
|
||||
if (pressed) {
|
||||
val nextState1 =
|
||||
if (state.keysPressed === 0)
|
||||
state
|
||||
.modify(_.staminaTimer)
|
||||
.setTo(
|
||||
makeStaminaTimer.runToFuture(props.asyncScheduler.value)
|
||||
staminaTimer.runToFuture(props.asyncScheduler.value)
|
||||
)
|
||||
else state
|
||||
val nextState2 = nextState1
|
||||
@ -85,9 +109,14 @@ class PlayerMovementEventListener(
|
||||
if (nextState1.keysPressed === 0) {
|
||||
nextState1.staminaTimer.cancel()
|
||||
nextState1
|
||||
.modify(_.staminaRegenTimer)
|
||||
.setTo(
|
||||
staminaRegenTimer.runToFuture(props.asyncScheduler.value)
|
||||
)
|
||||
} else
|
||||
nextState1
|
||||
}
|
||||
}
|
||||
|
||||
Behaviors.receiveMessage {
|
||||
case PlayerMovedLeft(pressed) =>
|
||||
|
@ -37,7 +37,10 @@ class IdleBehaviorFactory(
|
||||
children.movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||
if (pressed) nextStateFn(AliveSubstate.Moving(Walking))
|
||||
else nextStateFn(AliveSubstate.Idle)
|
||||
} else Behaviors.same
|
||||
} else {
|
||||
children.movementActor ! ImMovementActor.MoveLeft(false)
|
||||
Behaviors.same
|
||||
}
|
||||
|
||||
case PlayerActor.MoveLeft(pressed) =>
|
||||
implicit val ec = props.scheduler.value
|
||||
@ -76,9 +79,8 @@ class IdleBehaviorFactory(
|
||||
for {
|
||||
res <-
|
||||
children.statsActor.ask(StatsActor.TakeDamageResult(value, _))
|
||||
_ <- Future.successful(
|
||||
ctx.self ! PlayerActor.StatsResponse(res, replyTo)
|
||||
)
|
||||
_ = ctx.self ! PlayerActor.StatsResponse(res, replyTo)
|
||||
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
@ -93,6 +95,7 @@ class IdleBehaviorFactory(
|
||||
_ =
|
||||
ctx.self ! PlayerActor
|
||||
.StatsResponse(response, replyTo)
|
||||
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
@ -100,8 +103,30 @@ class IdleBehaviorFactory(
|
||||
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.Heal(value) =>
|
||||
children.statsActor ! StatsActor.HealResult(value)
|
||||
case PlayerActor.HealHealth(value, replyTo) =>
|
||||
implicit val ec = props.scheduler.value
|
||||
for {
|
||||
response <- children.statsActor.ask(
|
||||
StatsActor.HealHealthResult(value, _)
|
||||
)
|
||||
_ =
|
||||
ctx.self ! PlayerActor
|
||||
.StatsResponse(StatsActor.Status.Alive -> response, replyTo)
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.HealStamina(value, replyTo) =>
|
||||
ctx.log.debugP("Received heal stamina")
|
||||
implicit val ec = props.scheduler.value
|
||||
for {
|
||||
response <- children.statsActor.ask(
|
||||
StatsActor.HealStaminaResult(value, _)
|
||||
)
|
||||
_ =
|
||||
ctx.self ! PlayerActor
|
||||
.StatsResponse(StatsActor.Status.Alive -> response, replyTo)
|
||||
|
||||
} yield ()
|
||||
Behaviors.same
|
||||
|
||||
case PlayerActor.GetStatus(replyTo) =>
|
||||
@ -124,14 +149,17 @@ class IdleBehaviorFactory(
|
||||
|
||||
case PlayerActor.StatsResponse(response, replyTo) =>
|
||||
response match {
|
||||
case (dead, stats) =>
|
||||
if (dead) ctx.self ! PlayerActor.Die
|
||||
props.statsQueue
|
||||
.offer(stats)
|
||||
.foreach { _ =>
|
||||
pprint.log(show"Published stats $stats")
|
||||
replyTo ! ()
|
||||
}(props.scheduler.value)
|
||||
case (status, stats) =>
|
||||
status match {
|
||||
case StatsActor.Status.Dead => ctx.self ! PlayerActor.Die
|
||||
case StatsActor.Status.Alive =>
|
||||
props.statsQueue
|
||||
.offer(stats)
|
||||
.foreach { _ =>
|
||||
pprint.log(show"Published stats $stats")
|
||||
replyTo ! stats
|
||||
}(props.scheduler.value)
|
||||
}
|
||||
}
|
||||
Behaviors.same
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user