Browse Source

Use monix observable based player movement reducer

development
Rohan Sircar 3 years ago
parent
commit
4ff54c1373
  1. 3
      src/main/scala/wow/doge/mygame/Main.scala
  2. 12
      src/main/scala/wow/doge/mygame/game/GameAppActor.scala
  3. 26
      src/main/scala/wow/doge/mygame/game/entities/player/PlayerActor.scala
  4. 139
      src/main/scala/wow/doge/mygame/game/entities/player/PlayerMovementReducer.scala
  5. 1
      src/main/scala/wow/doge/mygame/game/entities/player/behaviors/IdleBehavior.scala
  6. 4
      src/main/scala/wow/doge/mygame/utils/MovementDirection.scala

3
src/main/scala/wow/doge/mygame/Main.scala

@ -19,6 +19,7 @@ import wow.doge.mygame.ActorSystemResource
import wow.doge.mygame.executors.ExecutorsModule
import wow.doge.mygame.types.AkkaScheduler
import wow.doge.mygame.utils.GenericConsoleStream
import io.odin
object Main extends BIOApp with ExecutorsModule {
import java.util.logging.{Logger => JLogger, Level}
JLogger.getLogger("").setLevel(Level.SEVERE)
@ -29,7 +30,7 @@ object Main extends BIOApp with ExecutorsModule {
def appResource(consoleStream: GenericConsoleStream[TextArea]) =
for {
logger <-
consoleLogger().withAsync(
consoleLogger(minLevel = odin.Level.Debug).withAsync(
timeWindow = 1.milliseconds,
maxBufferSize = Some(100)
) |+|

12
src/main/scala/wow/doge/mygame/game/GameAppActor.scala

@ -49,12 +49,12 @@ object GameAppActor {
val sp = ctx.spawn(SpawnProtocol(), "gameSpawnProtocol")
ctx.spawn(
GenericTimerActor
.Props(ctx.self, Ping, 1000.millis)
.behavior,
"pingTimer"
) ! GenericTimerActor.Start
// ctx.spawn(
// GenericTimerActor
// .Props(ctx.self, Ping, 1000.millis)
// .behavior,
// "pingTimer"
// ) ! GenericTimerActor.Start
val stopPromise = CancelablePromise[Unit]()

26
src/main/scala/wow/doge/mygame/game/entities/player/PlayerActor.scala

@ -114,18 +114,18 @@ object PlayerActor {
Dispatchers.jmeDispatcher
)
val playerMovementEl = ctx.spawnN(
Behaviors
.supervise(
new PlayerMovementEventListener.Props(
ctx.self,
scheduler
).behavior
)
.onFailure[Exception](
SupervisorStrategy.restart.withLimit(2, 100.millis)
)
)
// val playerMovementEl = ctx.spawnN(
// Behaviors
// .supervise(
// new PlayerMovementEventListener.Props(
// ctx.self,
// scheduler
// ).behavior
// )
// .onFailure[Exception](
// SupervisorStrategy.restart.withLimit(2, 100.millis)
// )
// )
val renderTickEl = {
val behavior: Behavior[RenderTick.type] =
@ -147,7 +147,7 @@ object PlayerActor {
}
//init listeners
playerEventBus ! EventBus.Subscribe(playerMovementEl)
// playerEventBus ! EventBus.Subscribe(playerMovementEl)
tickEventBus ! EventBus.Subscribe(renderTickEl)
new PlayerActor(

139
src/main/scala/wow/doge/mygame/game/entities/player/PlayerMovementReducer.scala

@ -0,0 +1,139 @@
package wow.doge.mygame.game.entities.player
import scala.concurrent.duration._
import akka.util.Timeout
import cats.syntax.eq._
import monix.eval.Fiber
import monix.eval.Task
import monix.reactive.Observable
import monix.{eval => me}
import wow.doge.mygame.EnumActionEvent
import wow.doge.mygame.game.entities.CharacterStats
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.Jump
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkBackward
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkForward
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkLeft
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput.WalkRight
import wow.doge.mygame.implicits._
import wow.doge.mygame.types.AkkaScheduler
import wow.doge.mygame.utils.MovementDirection
import io.odin.Logger
class PlayerMovementReducer(
val playerActor: PlayerActor.Ref,
logger: Logger[monix.bio.Task]
)(implicit
akkaSched: AkkaScheduler
) {
import PlayerMovementReducer._
import com.softwaremill.quicklens._
implicit val timeout = Timeout(1.second)
implicit val sched = akkaSched.value
def staminaTimer(multiplier: Int) =
Task.deferAction(implicit s =>
Observable
.interval(250.millis)
.doOnNextF(_ => logger.trace("Sending Stamina Consume Item"))
.mapEvalF(_ =>
playerActor
.askL(
PlayerActor
.ConsumeStamina(CharacterStats.DamageStamina(1 * multiplier), _)
)
)
.doOnNext(stats =>
if (stats.stamina.toInt === 0)
Task(playerActor ! PlayerActor.StopMoving)
else Task.unit
)
.takeWhile(_.stamina.toInt >= 0)
.completedL
)
def staminaRegenTimer(multiplier: Int) =
Task.deferAction(implicit s =>
Observable
.interval(500.millis)
.doOnNextF(_ => logger.trace("Sending Stamina Regen Item"))
.mapEvalF(_ =>
playerActor.askL(
PlayerActor
.HealStamina(CharacterStats.HealStamina(1 * multiplier), _)
)
)
.takeWhile(_.stamina.toInt =!= 100)
.delayExecution(1.second)
.completedL
)
def handleStamina(
state: State,
pressed: Boolean,
consumptionMultiplier: Int,
regenMultiplier: Int
): Task[State] =
state.staminaRegenTimer.cancel >>
(if (pressed) {
val nextState1 =
if (state.keysPressed === 0)
staminaTimer(consumptionMultiplier).start.map(
state.modify(_.staminaTimer).setTo
)
else Task.pure(state)
val nextState2 =
nextState1.map(_.modify(_.keysPressed).using(_ + 1))
nextState2
} else {
val nextState1 = state
.modify(_.keysPressed)
.using(_ - 1)
if (nextState1.keysPressed === 0)
nextState1.staminaTimer.cancel >>
staminaRegenTimer(regenMultiplier).start.map(
nextState1.modify(_.staminaRegenTimer).setTo
)
else
Task.pure(nextState1)
})
def value(
state: State,
action: EnumActionEvent[PlayerMovementInput]
): Task[State] =
action match {
case EnumActionEvent(WalkForward, pressed, tpf) =>
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Forward)
handleStamina(state, pressed, 1, 1)
case EnumActionEvent(WalkRight, pressed, tpf) =>
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Right)
handleStamina(state, pressed, 1, 1)
case EnumActionEvent(WalkLeft, pressed, tpf) =>
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Left)
handleStamina(state, pressed, 1, 1)
case EnumActionEvent(WalkBackward, pressed, tpf) =>
playerActor ! PlayerActor.Walk(pressed, MovementDirection.Backward)
handleStamina(state, pressed, 1, 1)
case EnumActionEvent(Jump, pressed, tpf) =>
if (pressed) playerActor ! PlayerActor.Jump else ()
handleStamina(state, pressed, 10, 1)
}
}
object PlayerMovementReducer {
final case class State(
keysPressed: Int,
staminaTimer: Fiber[Unit],
staminaRegenTimer: Fiber[Unit]
)
object State {
val empty = State(
0,
me.Fiber(me.Task.unit, me.Task.unit),
me.Fiber(me.Task.unit, me.Task.unit)
)
}
}

1
src/main/scala/wow/doge/mygame/game/entities/player/behaviors/IdleBehavior.scala

@ -119,7 +119,6 @@ class IdleBehaviorFactory(
Behaviors.same
case PlayerActor.HealStamina(value, replyTo) =>
ctx.log.debugP("Received heal stamina")
implicit val ec = props.scheduler.value
for {
response <- children.statsActor.ask(

4
src/main/scala/wow/doge/mygame/utils/MovementDirection.scala

@ -1,8 +1,8 @@
package wow.doge.mygame.utils
import enumeratum._
import cats.kernel.Eq
import cats.Show
import cats.kernel.Eq
import enumeratum._
sealed trait MovementDirection extends EnumEntry

Loading…
Cancel
Save