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