forked from nova/jmonkey-test
hmph
This commit is contained in:
parent
89fad19d99
commit
67a2bc4385
18
build.sbt
18
build.sbt
@ -19,13 +19,6 @@ lazy val javaFXModules =
|
||||
Seq("base", "controls", "fxml", "graphics", "media", "swing", "web")
|
||||
|
||||
lazy val root = (project in file(".")).settings(
|
||||
inThisBuild(
|
||||
List(
|
||||
scalaVersion := scalaVersion.value, // 2.11.12, or 2.13.3
|
||||
semanticdbEnabled := true, // enable SemanticDB
|
||||
semanticdbVersion := "4.3.24" // use Scalafix compatible version
|
||||
)
|
||||
),
|
||||
name := "mygame",
|
||||
organization := "wow.doge",
|
||||
version := "1.0-SNAPSHOT",
|
||||
@ -71,7 +64,9 @@ lazy val root = (project in file(".")).settings(
|
||||
"org.recast4j" % "recast" % "1.2.5",
|
||||
"org.recast4j" % "detour" % "1.2.5",
|
||||
"com.lihaoyi" %% "pprint" % "0.6.0",
|
||||
"org.scalatest" %% "scalatest" % "3.2.2" % "test"
|
||||
"org.scalatest" %% "scalatest" % "3.2.2" % "test",
|
||||
"org.typelevel" %% "cats-mtl" % "1.1.1",
|
||||
"io.estatico" %% "newtype" % "0.4.4"
|
||||
),
|
||||
// Determine OS version of JavaFX binaries
|
||||
|
||||
@ -135,4 +130,11 @@ addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
|
||||
addCompilerPlugin(
|
||||
"org.typelevel" %% "kind-projector" % "0.11.1" cross CrossVersion.full
|
||||
)
|
||||
inThisBuild(
|
||||
List(
|
||||
scalaVersion := scalaVersion.value, // 2.11.12, or 2.13.3
|
||||
semanticdbEnabled := true, // enable SemanticDB
|
||||
semanticdbVersion := "4.3.24" // use Scalafix compatible version
|
||||
)
|
||||
)
|
||||
ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.4.3"
|
||||
|
@ -1,3 +1,2 @@
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
|
||||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.23")
|
||||
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
|
||||
|
@ -2,7 +2,8 @@ package wow.doge.mygame
|
||||
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import cats.data.Reader
|
||||
import cats.Show
|
||||
import cats.kernel.Eq
|
||||
import monix.bio.IO
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
||||
@ -12,21 +13,10 @@ sealed trait AppError
|
||||
object AppError {
|
||||
final case class TimeoutError(reason: String) extends AppError
|
||||
object TimeoutError {
|
||||
def reader =
|
||||
Reader[Throwable, TimeoutError] {
|
||||
case ex: TimeoutException => TimeoutError(ex.getMessage)
|
||||
}
|
||||
def from: PartialFunction[Throwable, IO[TimeoutError, Nothing]] = {
|
||||
case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
|
||||
}
|
||||
}
|
||||
final case class DummyError(reason: String) extends AppError
|
||||
object DummyError {
|
||||
def reader =
|
||||
Reader[Throwable, DummyError] {
|
||||
case ex: NullPointerException => DummyError(ex.getMessage)
|
||||
}
|
||||
}
|
||||
final case class AssetManagerError(error: AssetManager.Error) extends AppError
|
||||
final case class AppNodeError(error: NodeWrapper2.Error) extends AppError
|
||||
final case class CollisionShapeCreationFailed(
|
||||
@ -37,4 +27,7 @@ object AppError {
|
||||
case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
|
||||
|
||||
}
|
||||
|
||||
implicit val show = Show.fromToString[AppError]
|
||||
implicit val eq = Eq.fromUniversalEquals[AppError]
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package wow.doge.mygame
|
||||
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
@ -19,10 +17,8 @@ import com.jme3.material.MaterialDef
|
||||
import com.jme3.math.ColorRGBA
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.renderer.Camera
|
||||
import com.jme3.renderer.RenderManager
|
||||
import com.jme3.renderer.ViewPort
|
||||
import com.jme3.scene.Node
|
||||
import com.jme3.scene.control.AbstractControl
|
||||
import com.softwaremill.macwire._
|
||||
import com.softwaremill.tagging._
|
||||
import io.odin.Logger
|
||||
@ -33,38 +29,44 @@ import monix.bio.UIO
|
||||
import monix.eval.Coeval
|
||||
import monix.reactive.Observable
|
||||
import scalafx.scene.control.TextArea
|
||||
import wow.doge.mygame.AppError.TimeoutError
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
import wow.doge.mygame.game.GameApp
|
||||
import wow.doge.mygame.game.GameAppActor
|
||||
import wow.doge.mygame.game.GameAppResource
|
||||
import wow.doge.mygame.game.GameAppTags
|
||||
import wow.doge.mygame.game.entities.EntityIds
|
||||
import wow.doge.mygame.game.entities.NpcActorSupervisor
|
||||
import wow.doge.mygame.game.entities.NpcMovementActor
|
||||
import wow.doge.mygame.game.entities.PlayerActorSupervisor
|
||||
import wow.doge.mygame.game.entities.PlayerController
|
||||
import wow.doge.mygame.game.entities.PlayerControllerTags
|
||||
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
||||
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.launcher.Launcher
|
||||
import wow.doge.mygame.launcher.Launcher.LauncherResult
|
||||
import wow.doge.mygame.math.ImVector3f
|
||||
import wow.doge.mygame.subsystems.events.Event
|
||||
import wow.doge.mygame.subsystems.events.EventBus
|
||||
import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription
|
||||
import wow.doge.mygame.subsystems.events.EventsModule
|
||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||
import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
||||
import wow.doge.mygame.subsystems.events.StatsEvent.DamageEvent
|
||||
import wow.doge.mygame.subsystems.events.TickEvent
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
|
||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import wow.doge.mygame.utils.GenericConsoleStream
|
||||
import wow.doge.mygame.utils.IOUtils
|
||||
import wow.doge.mygame.utils.wrappers.jme.AppNode2
|
||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||
|
||||
import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
||||
import com.jme3.math.Quaternion
|
||||
import com.jme3.math.Vector3f
|
||||
import wow.doge.mygame.types._
|
||||
import wow.doge.mygame.game.controls.FollowControl
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
||||
class MainApp(
|
||||
logger: Logger[Task],
|
||||
jmeThread: monix.execution.Scheduler,
|
||||
@ -94,13 +96,8 @@ class MainApp(
|
||||
tickEventBus <- eventsModule.tickEventBus
|
||||
obs <-
|
||||
playerEventBus
|
||||
.askL[Observable[PlayerMovementEvent]](
|
||||
ObservableSubscription(_)
|
||||
)
|
||||
.onErrorHandleWith {
|
||||
case ex: TimeoutException =>
|
||||
IO.raiseError(AppError.TimeoutError(ex.getMessage()))
|
||||
}
|
||||
.askL[Observable[PlayerMovementEvent]](ObservableSubscription(_))
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
_ <-
|
||||
IOUtils
|
||||
.toIO(
|
||||
@ -121,7 +118,7 @@ class MainApp(
|
||||
// jfxUI <- gameApp.jfxUI
|
||||
gameAppActor <- gameApp.spawnGameActor(
|
||||
GameAppActor.Props(tickEventBus).behavior,
|
||||
"gameAppActor"
|
||||
Some("gameAppActor")
|
||||
)
|
||||
_ <- gameAppActor !! GameAppActor.Start
|
||||
consoleTextArea <- UIO(new TextArea {
|
||||
@ -138,7 +135,7 @@ class MainApp(
|
||||
_ <-
|
||||
wire[MainAppDelegate]
|
||||
.init()
|
||||
.executeOn(gameApp.scheduler)
|
||||
.executeOn(gameApp.scheduler.value)
|
||||
} yield fib
|
||||
|
||||
def gameInit: Resource[UIO, Either[AppError, Fiber[Nothing, Unit]]] =
|
||||
@ -184,6 +181,7 @@ class MainApp(
|
||||
class MainAppDelegate(
|
||||
gameApp: GameApp,
|
||||
loggerL: Logger[Task],
|
||||
mainEventBus: GameEventBus[Event],
|
||||
playerEventBus: GameEventBus[PlayerEvent],
|
||||
tickEventBus: GameEventBus[TickEvent],
|
||||
inputManager: InputManager,
|
||||
@ -192,7 +190,8 @@ class MainAppDelegate(
|
||||
camera: Camera,
|
||||
viewPort: ViewPort,
|
||||
enqueueR: Function1[() => Unit, Unit],
|
||||
rootNode: AppNode2 @@ GameAppTags.RootNode
|
||||
rootNode: RootNode,
|
||||
schedulers: Schedulers
|
||||
)(implicit
|
||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
timeout: Timeout,
|
||||
@ -213,17 +212,104 @@ class MainAppDelegate(
|
||||
// _ <- Task(consoleStream.println("text"))
|
||||
level <- DefaultGameLevel(assetManager, viewPort)
|
||||
_ <- level.addToGame(rootNode, physicsSpace)
|
||||
_ <- createPlayerController()
|
||||
playerActor <- createPlayerController()
|
||||
// .onErrorRestart(3)
|
||||
_ <- wire[GameInputHandler.Props].begin
|
||||
// .onErrorRestart(3)
|
||||
johnActor <- createTestNpc("John")
|
||||
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
|
||||
|
||||
// _ <-
|
||||
// johnActor
|
||||
// .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
|
||||
// .delayExecution(2.seconds)
|
||||
_ <-
|
||||
johnActor
|
||||
.tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
|
||||
.delayExecution(2.seconds)
|
||||
rootNode.depthFirstTraversal
|
||||
.doOnNextF(spat => loggerL.debug(spat.getName).toTask)
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
damageObs <-
|
||||
mainEventBus
|
||||
.askL[Observable[DamageEvent]](ObservableSubscription(_))
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
_ <-
|
||||
damageObs
|
||||
.doOnNextF(event =>
|
||||
(loggerL.debug(s"Received Damage Event $event") >>
|
||||
IO(
|
||||
playerActor ! PlayerActorSupervisor.TakeDamage(event.amount)
|
||||
)).toTask
|
||||
)
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
_ <-
|
||||
Observable
|
||||
.interval(1.second)
|
||||
.doOnNextF(_ =>
|
||||
playerActor
|
||||
.askL(PlayerActorSupervisor.GetStatus)
|
||||
.flatMap(s =>
|
||||
loggerL.debug(s"Player actor status: $s") >> UIO.pure(s)
|
||||
)
|
||||
.void
|
||||
// .flatMap(s =>
|
||||
// if (s == Status.Alive)
|
||||
// playerActor
|
||||
// .askL(PlayerActorSupervisor.CurrentStats )
|
||||
// .flatMap(s => loggerL.debug(s"Got state $s"))
|
||||
// else IO.unit
|
||||
// )
|
||||
.toTask
|
||||
)
|
||||
// .doOnNextF(_ =>
|
||||
// playerActor
|
||||
// .askL(PlayerActorSupervisor.GetStatus )
|
||||
// .flatMap(s => loggerL.debug(s"Player actor status: $s"))
|
||||
// .toTask
|
||||
// )
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
_ <-
|
||||
physicsSpace.collisionObservable
|
||||
.filter(event =>
|
||||
(for {
|
||||
nodeA <- event.nodeA
|
||||
nodeB <- event.nodeB
|
||||
} yield nodeA.getName === "PlayerNode" && nodeB.getName === "John" ||
|
||||
nodeB.getName === "PlayerNode" && nodeA.getName === "John")
|
||||
.getOrElse(false)
|
||||
)
|
||||
.doOnNextF(event =>
|
||||
loggerL
|
||||
.debug(s"$event ${event.appliedImpulse()}")
|
||||
.toTask
|
||||
)
|
||||
.doOnNextF(event =>
|
||||
(for {
|
||||
victim <- Coeval(for {
|
||||
nodeA <- event.nodeA
|
||||
nodeB <- event.nodeB
|
||||
} yield if (nodeB.getName === "John") nodeA else nodeB)
|
||||
_ <- Coeval(
|
||||
victim.foreach { v =>
|
||||
pprint.log(s"emitted event ${v.getName}")
|
||||
mainEventBus ! EventBus.Publish(
|
||||
DamageEvent("John", v.getName, 10),
|
||||
"damageHandler"
|
||||
)
|
||||
}
|
||||
)
|
||||
} yield ()).void
|
||||
)
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
// _ <-
|
||||
// IOUtils
|
||||
// .toIO(
|
||||
@ -238,12 +324,12 @@ class MainAppDelegate(
|
||||
|
||||
def createPlayerController(
|
||||
// appScheduler: monix.execution.Scheduler
|
||||
): IO[AppError, ActorRef[PlayerActorSupervisor.Command]] = {
|
||||
): IO[AppError, PlayerActorSupervisor.Ref] = {
|
||||
val playerPos = ImVector3f.Zero
|
||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||
val playerPhysicsControl =
|
||||
PlayerController.Defaults.defaultPlayerPhysicsControl
|
||||
.taggedWith[PlayerControllerTags.PlayerTag]
|
||||
|
||||
for {
|
||||
playerModel <-
|
||||
assetManager
|
||||
@ -260,42 +346,115 @@ class MainAppDelegate(
|
||||
)
|
||||
cameraPivotNode <- UIO(
|
||||
new Node(EntityIds.CameraPivot.value)
|
||||
.withControl(new FollowControl(playerNode))
|
||||
.taggedWith[PlayerControllerTags.PlayerCameraPivotNode]
|
||||
.withControl(
|
||||
new FollowControl(playerNode)
|
||||
)
|
||||
.taggedWith[PlayerController.Tags.PlayerCameraPivotNode]
|
||||
)
|
||||
camNode <- UIO(
|
||||
PlayerController.Defaults
|
||||
.defaultCamerNode(camera, playerPos)
|
||||
.taggedWith[PlayerControllerTags.PlayerCameraNode]
|
||||
.taggedWith[PlayerController.Tags.PlayerCameraNode]
|
||||
)
|
||||
playerCameraEvents <-
|
||||
playerEventBus
|
||||
.askL[Observable[PlayerCameraEvent]](ObservableSubscription(_))
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
_ <-
|
||||
inputManager
|
||||
.enumAnalogObservable(PlayerCameraInput)
|
||||
.sample(1.millis)
|
||||
.scan(new Quaternion) {
|
||||
case (rotationBuf, action) =>
|
||||
action.binding match {
|
||||
// case PlayerCameraEvent.CameraLeft =>
|
||||
case PlayerCameraInput.CameraRotateLeft =>
|
||||
// me.Task {
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
cameraPivotNode.rotate(rot)
|
||||
rotationBuf
|
||||
// }
|
||||
// case PlayerCameraEvent.CameraRight =>
|
||||
case PlayerCameraInput.CameraRotateRight =>
|
||||
// me.Task {
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
cameraPivotNode.rotate(rot)
|
||||
rotationBuf
|
||||
// }
|
||||
// case PlayerCameraEvent.CameraMovedUp =>
|
||||
case PlayerCameraInput.CameraRotateUp =>
|
||||
// me.Task {
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||
cameraPivotNode.rotate(rot)
|
||||
rotationBuf
|
||||
// }
|
||||
// case PlayerCameraEvent.CameraMovedDown =>
|
||||
case PlayerCameraInput.CameraRotateDown =>
|
||||
// me.Task {
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||
cameraPivotNode.rotate(rot)
|
||||
rotationBuf
|
||||
// }
|
||||
}
|
||||
}
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
_ <-
|
||||
Observable
|
||||
.interval(10.millis)
|
||||
.doOnNextF(_ =>
|
||||
Coeval {
|
||||
val location = playerNode.getWorldTranslation()
|
||||
cameraPivotNode.setLocalTranslation(location)
|
||||
}
|
||||
)
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
sched <- UIO.pure(schedulers.async)
|
||||
playerActor <- wire[PlayerController.Props].create
|
||||
obs <-
|
||||
playerActor
|
||||
.askL(PlayerActorSupervisor.GetStatsObservable)
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
_ <-
|
||||
obs
|
||||
.doOnNext(s => loggerL.debug(s"Got state $s").toTask)
|
||||
.completedL
|
||||
.toIO
|
||||
.hideErrors
|
||||
.startAndForget
|
||||
} yield playerActor
|
||||
}
|
||||
|
||||
def createTestNpc(
|
||||
// appScheduler: monix.execution.Scheduler,
|
||||
npcName: String
|
||||
): IO[AppError, ActorRef[NpcActorSupervisor.Command]] = {
|
||||
): IO[AppError, NpcActorSupervisor.Ref] = {
|
||||
val initialPos = ImVector3f(50, 5, 0)
|
||||
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
||||
// (1f, 2.1f, 10f)
|
||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||
|
||||
val npcActorTask = AkkaUtils.spawnActorL(
|
||||
NpcActorSupervisor
|
||||
.Props(
|
||||
new NpcMovementActor.Props(
|
||||
enqueueR,
|
||||
initialPos,
|
||||
// tickEventBus,
|
||||
npcName,
|
||||
npcPhysicsControl
|
||||
).behavior,
|
||||
new NpcActorSupervisor.Props(
|
||||
new NpcMovementActor.Props(
|
||||
enqueueR,
|
||||
initialPos,
|
||||
npcName,
|
||||
initialPos
|
||||
)
|
||||
.behavior,
|
||||
s"${npcName}-npcActorSupervisor"
|
||||
npcPhysicsControl
|
||||
).behavior,
|
||||
npcName,
|
||||
initialPos
|
||||
).behavior,
|
||||
actorName = Some(s"${npcName}-npcActorSupervisor")
|
||||
)
|
||||
|
||||
(for {
|
||||
@ -325,12 +484,3 @@ class MainAppDelegate(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FollowControl(playerNode: Node) extends AbstractControl {
|
||||
override def controlUpdate(tpf: Float): Unit =
|
||||
this.spatial.setLocalTranslation(playerNode.getLocalTranslation())
|
||||
override def controlRender(
|
||||
rm: RenderManager,
|
||||
vp: ViewPort
|
||||
): Unit = {}
|
||||
}
|
||||
|
@ -38,10 +38,9 @@ import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.utils.AkkaUtils
|
||||
import wow.doge.mygame.utils.GenericTimerActor
|
||||
import wow.doge.mygame.utils.wrappers.jme._
|
||||
|
||||
import wow.doge.mygame.types._
|
||||
object GameAppTags {
|
||||
sealed trait RootNode
|
||||
|
||||
sealed trait GuiNode
|
||||
}
|
||||
|
||||
@ -49,34 +48,39 @@ class GameApp private[game] (
|
||||
logger: Logger[Task],
|
||||
app: SimpleAppExt,
|
||||
gameActor: ActorRef[TestGameActor.Command],
|
||||
gameSpawnProtocol: ActorRef[SpawnProtocol.Command]
|
||||
gameSpawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||
scheduler: akka.actor.typed.Scheduler
|
||||
) {
|
||||
def inputManager: UIO[InputManager] = UIO(app.getInputManager())
|
||||
def assetManager = new AssetManager(app.getAssetManager())
|
||||
def guiNode = UIO(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
|
||||
val guiNode2 = AppNode2(app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
|
||||
def flyCam = Option(app.getFlyByCamera())
|
||||
val assetManager = new AssetManager(app.getAssetManager())
|
||||
val guiNode: GuiNode =
|
||||
AppNode2(app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
|
||||
// def flyCam = Option(app.getFlyByCamera())
|
||||
def camera = UIO(app.getCamera())
|
||||
def viewPort = UIO(app.getViewPort())
|
||||
// def rootNode = UIO(app.getRootNode().taggedWith[GameAppTags.RootNode])
|
||||
val rootNode =
|
||||
val rootNode: RootNode =
|
||||
AppNode2(app.getRootNode()).taggedWith[GameAppTags.RootNode]
|
||||
val physicsSpace = new PhysicsSpace(app.bulletAppState.physicsSpace)
|
||||
val physicsSpace =
|
||||
new PhysicsSpace(app.bulletAppState.physicsSpace)
|
||||
def enqueue(cb: () => Unit) = app.enqueueR(() => cb())
|
||||
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(cb)
|
||||
|
||||
def whenTerminated: IO[AppError, Unit] =
|
||||
IO.deferFuture(app.whenTerminated).onErrorHandleWith(TimeoutError.from)
|
||||
|
||||
def spawnGameActor[T](
|
||||
behavior: Behavior[T],
|
||||
actorName: String,
|
||||
actorName: Option[String] = None,
|
||||
props: Props = Dispatchers.jmeDispatcher
|
||||
)(implicit scheduler: akka.actor.typed.Scheduler) =
|
||||
)(implicit name: sourcecode.Name) =
|
||||
AkkaUtils.spawnActorL(behavior, actorName, props)(
|
||||
2.seconds,
|
||||
scheduler,
|
||||
gameSpawnProtocol
|
||||
gameSpawnProtocol,
|
||||
name
|
||||
)
|
||||
|
||||
def scheduler = app.scheduler
|
||||
def scheduler = JmeScheduler(app.scheduler)
|
||||
def jfxUI = JFxUI(app)
|
||||
|
||||
}
|
||||
@ -92,10 +96,9 @@ class GameAppResource(
|
||||
) {
|
||||
def resource
|
||||
: Resource[UIO, Either[AppError, (GameApp, Fiber[Nothing, Unit])]] =
|
||||
Resource.make {
|
||||
lazy val bullet = new BulletAppState
|
||||
Resource.make(
|
||||
(for {
|
||||
app <- UIO(new SimpleAppExt(schedulers, bullet))
|
||||
app <- UIO(new SimpleAppExt(schedulers, new BulletAppState))
|
||||
_ <- UIO(JMERunner.runner = Some(app.enqueue _))
|
||||
_ <- UIO {
|
||||
val settings = new AppSettings(true)
|
||||
@ -113,23 +116,22 @@ class GameAppResource(
|
||||
_ <- Task.deferFuture(app.started).onErrorHandleWith(TimeoutError.from)
|
||||
testGameActor <- AkkaUtils.spawnActorL(
|
||||
new TestGameActor.Props().create,
|
||||
"testGameActor",
|
||||
Dispatchers.jmeDispatcher
|
||||
Some("testGameActor"),
|
||||
props = Dispatchers.jmeDispatcher
|
||||
)
|
||||
sp <-
|
||||
testGameActor
|
||||
.askL(TestGameActor.GetSpawnProtocol(_))
|
||||
.askL(TestGameActor.GetSpawnProtocol)
|
||||
.onErrorHandleWith(TimeoutError.from)
|
||||
gameApp <- UIO(new GameApp(logger, app, testGameActor, sp))
|
||||
gameApp <- UIO(new GameApp(logger, app, testGameActor, sp, scheduler))
|
||||
_ <- UIO {
|
||||
val fut = () => testGameActor.ask(TestGameActor.Stop).flatten
|
||||
app.cancelToken = Some(fut)
|
||||
}
|
||||
} yield (gameApp, fib)).attempt
|
||||
} {
|
||||
case Right(gameApp -> fib) =>
|
||||
fib.cancel >> UIO(JMERunner.runner = None)
|
||||
case Left(error) => IO.terminate(new Exception(error.toString))
|
||||
) {
|
||||
case Right(gameApp -> fib) => fib.cancel
|
||||
case Left(error) => IO.terminate(new Exception(error.toString))
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,10 +215,10 @@ object Ops {
|
||||
|
||||
object SpawnSystem {
|
||||
sealed trait Result
|
||||
final case object Ok extends Result
|
||||
case object Ok extends Result
|
||||
|
||||
sealed trait Complete
|
||||
final case object Complete extends Complete
|
||||
case object Complete extends Complete
|
||||
|
||||
sealed trait SpawnRequest
|
||||
final case class SpawnSpatial(nodeTask: Task[Node]) extends SpawnRequest
|
||||
|
@ -24,18 +24,18 @@ object GameAppActor {
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
ctx.log.infoP("Hello from GameAppActor")
|
||||
val renderTickGenerator =
|
||||
ctx.spawn(
|
||||
ctx.spawnN(
|
||||
Behaviors
|
||||
.supervise(renderTickGeneratorBehavior)
|
||||
.onFailure[Exception](SupervisorStrategy.restart),
|
||||
"tickGeneratorActor"
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
)
|
||||
)
|
||||
|
||||
val tickGeneratorTimer = ctx.spawn(
|
||||
val tickGeneratorTimer = ctx.spawnN(
|
||||
GenericTimerActor
|
||||
.Props(renderTickGenerator, TickGenerator.Send, 10.millis)
|
||||
.behavior,
|
||||
"tickGeneratorTimer"
|
||||
.behavior
|
||||
)
|
||||
|
||||
Behaviors.receiveMessage {
|
||||
|
@ -14,7 +14,6 @@ import monix.execution.Scheduler
|
||||
import monix.execution.atomic.Atomic
|
||||
import wow.doge.mygame.executors.GUIExecutorService
|
||||
import wow.doge.mygame.executors.Schedulers
|
||||
// import wow.doge.mygame.implicits._
|
||||
class SimpleAppExt(
|
||||
schedulers: Schedulers,
|
||||
val bulletAppState: BulletAppState,
|
||||
@ -28,11 +27,16 @@ class SimpleAppExt(
|
||||
private val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
|
||||
|
||||
private val startSignal: CancelablePromise[Unit] = CancelablePromise()
|
||||
private val terminationSignal: CancelablePromise[Unit] = CancelablePromise()
|
||||
|
||||
var cancelToken: Option[() => Future[Unit]] = None
|
||||
|
||||
def started: CancelableFuture[Unit] = startSignal.future
|
||||
|
||||
def whenTerminated: CancelableFuture[Unit] = terminationSignal.future
|
||||
|
||||
// override def physicsSpace: PhysicsSpace = ???
|
||||
|
||||
override def simpleInitApp(): Unit = {
|
||||
stateManager.attach(bulletAppState)
|
||||
startSignal.success(())
|
||||
@ -40,18 +44,19 @@ class SimpleAppExt(
|
||||
|
||||
override def simpleUpdate(tpf: Float): Unit = {}
|
||||
|
||||
override def stop(waitFor: Boolean): Unit = {
|
||||
override def stop(waitFor: Boolean): Unit =
|
||||
cancelToken match {
|
||||
case Some(value) =>
|
||||
value().foreach { _ =>
|
||||
pprint.log("Called cancel in simpleapp")
|
||||
super.stop(true)
|
||||
terminationSignal.success(())
|
||||
}
|
||||
case None =>
|
||||
pprint.log("Called cancel in simpleapp")
|
||||
super.stop(true)
|
||||
terminationSignal.success(())
|
||||
}
|
||||
}
|
||||
|
||||
def enqueueFuture[T](cb: () => T): CancelableFuture[T] = {
|
||||
val p = CancelablePromise[T]()
|
||||
|
@ -1,294 +0,0 @@
|
||||
package wow.doge.mygame.state
|
||||
|
||||
import scala.concurrent.duration.DurationInt
|
||||
|
||||
import akka.actor.typed.ActorRef
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.scaladsl.Behaviors
|
||||
import akka.actor.typed.scaladsl.TimerScheduler
|
||||
import com.jme3.input.InputManager
|
||||
import com.jme3.input.KeyInput
|
||||
import com.jme3.input.controls.KeyTrigger
|
||||
import com.jme3.math.Vector3f
|
||||
import com.jme3.scene.Geometry
|
||||
import wow.doge.mygame.implicits._
|
||||
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
|
||||
class PlayerMovementState(
|
||||
// movementActor: ActorRef[MovementActor.Command],
|
||||
// movementActorTimer: ActorRef[MovementActorTimer.Command],
|
||||
imMovementActor: ActorRef[ImMovementActor.Command]
|
||||
// geom: Geometry,
|
||||
// camNode: CameraNode,
|
||||
// playerNode: Node
|
||||
// gameAppActor: ActorRef[GameAppActor.Command]
|
||||
) extends MyBaseState
|
||||
// with ActionListener
|
||||
{
|
||||
|
||||
protected lazy val mat = MyMaterial(
|
||||
assetManager = assetManager,
|
||||
path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
||||
)
|
||||
|
||||
override protected def init(): Unit = {
|
||||
|
||||
// setupKeys(inputManager)
|
||||
// println("playermovementstate " + Thread.currentThread().getName())
|
||||
|
||||
// geom.setMaterial(mat)
|
||||
|
||||
// camNode.setControlDir(ControlDirection.SpatialToCamera)
|
||||
// // lazy val camNode = new CameraNode("CameraNode", simpleApp.getCamera())
|
||||
// // camNode.setCamera(simpleApp.getCamera())
|
||||
// discard {
|
||||
// playerNode
|
||||
// .child(camNode)
|
||||
// .child(geom)
|
||||
// // playerNode.children(Seq(camNode, geom))
|
||||
// }
|
||||
// discard { rootNode.withChild(playerNode) }
|
||||
// camNode.setLocalTranslation(
|
||||
// new Vector3f(0, 1.5f, 10)
|
||||
// )
|
||||
// camNode.lookAt(playerNode.getLocalTranslation(), Vector3f.UNIT_Y)
|
||||
|
||||
// movementActorTimer ! MovementActorTimer.Start(geom, cam)
|
||||
// movementActorTimer ! MovementActorTimer.Start
|
||||
}
|
||||
|
||||
override def update(tpf: Float) = {
|
||||
// movementActor ! MovementActor.Tick(tpf, geom, cam)
|
||||
// imMovementActor ! ImMovementActor.Tick(tpf)
|
||||
|
||||
// movementActorTimer ! MovementActorTimer.Update(tpf)
|
||||
}
|
||||
|
||||
override def stop(): Unit = {}
|
||||
// override protected def cleanup(app: Application): Unit = {
|
||||
// // gameAppActor ! GameAppActor.Stop
|
||||
// super.cleanup(app)
|
||||
// }
|
||||
|
||||
def setupKeys(inputManager: InputManager) = {
|
||||
|
||||
inputManager
|
||||
.withMapping(
|
||||
"Left",
|
||||
// new KeyTrigger(KeyInput.KEY_A),
|
||||
new KeyTrigger(KeyInput.KEY_LEFT)
|
||||
)
|
||||
.withMapping(
|
||||
"Right",
|
||||
// new KeyTrigger(KeyInput.KEY_D),
|
||||
new KeyTrigger(KeyInput.KEY_RIGHT)
|
||||
)
|
||||
.withMapping(
|
||||
"Up",
|
||||
// new KeyTrigger(KeyInput.KEY_W),
|
||||
new KeyTrigger(KeyInput.KEY_UP)
|
||||
)
|
||||
.withMapping(
|
||||
"Down",
|
||||
// new KeyTrigger(KeyInput.KEY_S),
|
||||
new KeyTrigger(KeyInput.KEY_DOWN)
|
||||
)
|
||||
.withMapping(
|
||||
"Space",
|
||||
new KeyTrigger(KeyInput.KEY_SPACE),
|
||||
new KeyTrigger(KeyInput.KEY_H)
|
||||
)
|
||||
.withMapping(
|
||||
"Reset",
|
||||
new KeyTrigger(KeyInput.KEY_R),
|
||||
new KeyTrigger(KeyInput.KEY_RETURN)
|
||||
)
|
||||
// .withListener(this, "Left")
|
||||
// .withListener(this, "Right")
|
||||
// .withListener(this, "Up")
|
||||
// .withListener(this, "Down")
|
||||
// .withListener(this, "Space")
|
||||
// .withListener(this, "Reset")
|
||||
}
|
||||
|
||||
// def onAction(binding: String, value: Boolean, tpf: Float) =
|
||||
// binding match {
|
||||
// case "Left" => imMovementActor ! ImMovementActor.MovedLeft(value)
|
||||
// case "Right" => imMovementActor ! ImMovementActor.MovedRight(value)
|
||||
// case "Up" => imMovementActor ! ImMovementActor.MovedUp(value)
|
||||
// case "Down" => imMovementActor ! ImMovementActor.MovedDown(value)
|
||||
// case "Space" =>
|
||||
// case _ =>
|
||||
// }
|
||||
|
||||
override protected def onEnable(): Unit = {}
|
||||
|
||||
override protected def onDisable(): Unit = {}
|
||||
|
||||
}
|
||||
|
||||
final case class CardinalDirection(
|
||||
left: Boolean = false,
|
||||
right: Boolean = false,
|
||||
up: Boolean = false,
|
||||
down: Boolean = false
|
||||
)
|
||||
|
||||
object MovementActor {
|
||||
sealed trait Command
|
||||
// final case class Tick(tpf: Float, geom: Geometry, cam: Camera) extends Command
|
||||
// final case class Tick(tpf: Float) extends Command
|
||||
final case object Tick extends Command
|
||||
|
||||
sealed trait Movement extends Command
|
||||
final case class MovedLeft(pressed: Boolean) extends Movement
|
||||
final case class MovedUp(pressed: Boolean) extends Movement
|
||||
final case class MovedRight(pressed: Boolean) extends Movement
|
||||
final case class MovedDown(pressed: Boolean) extends Movement
|
||||
|
||||
final case class Props(app: com.jme3.app.Application, geom: Geometry)
|
||||
|
||||
/**
|
||||
* Internal state of the actor
|
||||
*
|
||||
* @param cardinalDir Immutable, can be shared as is
|
||||
* @param walkDirection scratch space to avoid allocations on every tick. Do not share outside the actor
|
||||
*/
|
||||
final case class State(
|
||||
cardinalDir: CardinalDirection = CardinalDirection(),
|
||||
walkDirection: Vector3f = Vector3f.UNIT_X
|
||||
)
|
||||
|
||||
def apply(props: Props): Behavior[Command] =
|
||||
Behaviors.setup(ctx => new MovementActor(ctx, props).receive(State()))
|
||||
|
||||
}
|
||||
class MovementActor(
|
||||
ctx: ActorContext[MovementActor.Command],
|
||||
props: MovementActor.Props
|
||||
) {
|
||||
import MovementActor._
|
||||
import com.softwaremill.quicklens._
|
||||
def receive(state: MovementActor.State): Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
case m: Movement =>
|
||||
m match {
|
||||
case MovedLeft(pressed) =>
|
||||
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
|
||||
case MovedUp(pressed) =>
|
||||
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
|
||||
case MovedRight(pressed) =>
|
||||
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
|
||||
case MovedDown(pressed) =>
|
||||
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
|
||||
}
|
||||
|
||||
case Tick =>
|
||||
val camDir =
|
||||
props.app.getCamera.getDirection().clone().multLocal(0.6f)
|
||||
val camLeft = props.app.getCamera.getLeft().clone().multLocal(0.4f)
|
||||
val walkDir = state.walkDirection.set(0, 0, 0)
|
||||
// val walkDir = new Vector3f
|
||||
val dir = state.cardinalDir
|
||||
if (dir.up) {
|
||||
ctx.log.debugP("up")
|
||||
// ctx.log.debugP(Thread.currentThread().getName())
|
||||
// walkDir.addLocal(0, 0, -1)
|
||||
walkDir += camDir
|
||||
}
|
||||
if (dir.left) {
|
||||
ctx.log.debugP("left")
|
||||
// walkDir.addLocal(-1, 0, 0)
|
||||
walkDir.addLocal(camLeft)
|
||||
}
|
||||
if (dir.right) {
|
||||
ctx.log.debugP("right")
|
||||
// walkDir.addLocal(1, 0, 0)
|
||||
walkDir.addLocal(camLeft.negateLocal())
|
||||
}
|
||||
if (dir.down) {
|
||||
ctx.log.debugP("down")
|
||||
walkDir.addLocal(camDir.negateLocal())
|
||||
// walkDir.addLocal(0, 0, 1)
|
||||
}
|
||||
// (dir.up, dir.down, dir.left, dir.right) match {
|
||||
// case (true, false, true, false) =>
|
||||
// case _ =>
|
||||
// }
|
||||
|
||||
walkDir.multLocal(2f)
|
||||
|
||||
// walkDir.multLocal(100f)
|
||||
// .multLocal(tpf)
|
||||
|
||||
// val v = props.geom.getLocalTranslation()
|
||||
// props.geom.setLocalTranslation(
|
||||
// (v += walkDir)
|
||||
// )
|
||||
props.app.enqueue(new Runnable {
|
||||
override def run(): Unit = {
|
||||
// geom.setLocalTranslation(walkDir)
|
||||
|
||||
val v = props.geom.getLocalTranslation()
|
||||
props.geom.setLocalTranslation(
|
||||
(v += walkDir)
|
||||
)
|
||||
}
|
||||
})
|
||||
Behaviors.same
|
||||
// receive(state = state.modify(_.walkDirection).setTo(walkDir))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MovementActorTimer {
|
||||
sealed trait Command
|
||||
final case object Start extends Command
|
||||
final case object Update extends Command
|
||||
private case object Send extends Command
|
||||
case object TimerKey
|
||||
|
||||
final case class Props(
|
||||
timers: TimerScheduler[MovementActorTimer.Command],
|
||||
target: ActorRef[MovementActor.Command]
|
||||
)
|
||||
final case class State()
|
||||
def apply(target: ActorRef[MovementActor.Command]) =
|
||||
Behaviors.withTimers[Command] { timers =>
|
||||
new MovementActorTimer(Props(timers, target)).idle()
|
||||
}
|
||||
}
|
||||
class MovementActorTimer(
|
||||
props: MovementActorTimer.Props
|
||||
) {
|
||||
import MovementActorTimer._
|
||||
// import com.softwaremill.quicklens._
|
||||
|
||||
def idle(): Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
case Start =>
|
||||
props.timers.startTimerWithFixedDelay(
|
||||
Send,
|
||||
10.millis
|
||||
)
|
||||
active()
|
||||
case _ => Behaviors.unhandled
|
||||
}
|
||||
}
|
||||
|
||||
def active(): Behavior[Command] =
|
||||
Behaviors.receiveMessage { msg =>
|
||||
msg match {
|
||||
case Update => active()
|
||||
case Send =>
|
||||
props.target ! MovementActor.Tick
|
||||
Behaviors.same
|
||||
case _ => Behaviors.unhandled
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package wow.doge.mygame.game.controls
|
||||
|
||||
import com.jme3.math.Quaternion
|
||||
import com.jme3.scene.Node
|
||||
import com.jme3.scene.control.AbstractControl
|
||||
import com.jme3.renderer.RenderManager
|
||||
import com.jme3.renderer.ViewPort
|
||||
import monix.reactive.Observable
|
||||
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
||||
import com.jme3.scene.Spatial
|
||||
import monix.{eval => me}
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.math.Vector3f
|
||||
import monix.execution.Cancelable
|
||||
import monix.execution.Scheduler
|
||||
|
||||
class CameraMovementControl(
|
||||
rotationBuf: Quaternion,
|
||||
obs: Observable[PlayerCameraInput],
|
||||
rotateFn: Quaternion => Unit
|
||||
)(implicit s: Scheduler)
|
||||
extends AbstractControl {
|
||||
private var _event: PlayerCameraInput = null
|
||||
private var _subscriptionToken: Cancelable = null
|
||||
|
||||
override def controlUpdate(tpf: Float): Unit =
|
||||
if (_event != null)
|
||||
_event match {
|
||||
case PlayerCameraInput.CameraRotateLeft =>
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
rotateFn(rot)
|
||||
case PlayerCameraInput.CameraRotateRight =>
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||
rotateFn(rot)
|
||||
case PlayerCameraInput.CameraRotateUp =>
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||
rotateFn(rot)
|
||||
case PlayerCameraInput.CameraRotateDown =>
|
||||
val rot = rotationBuf
|
||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||
rotateFn(rot)
|
||||
}
|
||||
|
||||
override def controlRender(
|
||||
x$1: RenderManager,
|
||||
x$2: ViewPort
|
||||
): Unit = {}
|
||||
override def setSpatial(spatial: Spatial): Unit = {
|
||||
super.setSpatial(spatial)
|
||||
if (this.spatial != null)
|
||||
_subscriptionToken =
|
||||
obs.doOnNext(event => me.Task { _event = event }).subscribe()
|
||||
else {
|
||||
_subscriptionToken.cancel()
|
||||
_subscriptionToken = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FollowControl(playerNode: Node) extends AbstractControl {
|
||||
override def controlUpdate(tpf: Float): Unit =
|
||||
this.spatial.setLocalTranslation(playerNode.getLocalTranslation())
|
||||
override def controlRender(
|
||||
rm: RenderManager,
|
||||
vp: ViewPort
|
||||
): Unit = {}
|
||||
}
|
@ -30,6 +30,8 @@ import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||
import wow.doge.mygame.utils.GenericTimerActor
|
||||
|
||||
object NpcActorSupervisor {
|
||||
type Ref = ActorRef[Command]
|
||||
|
||||
sealed trait Command
|
||||
final case class Move(pos: ImVector3f) extends Command
|
||||
private final case class InternalMove(
|
||||
@ -40,13 +42,13 @@ object NpcActorSupervisor {
|
||||
private case class LogError(err: Throwable) extends Command
|
||||
private case class MovementFailed(err: Throwable) extends Command
|
||||
|
||||
final case class Props(
|
||||
npcMovementActorBehavior: Behavior[NpcMovementActor.Command],
|
||||
npcName: String,
|
||||
initialPos: ImVector3f
|
||||
class Props(
|
||||
val npcMovementActorBehavior: Behavior[NpcMovementActor.Command],
|
||||
val npcName: String,
|
||||
val initialPos: ImVector3f
|
||||
) {
|
||||
def behavior =
|
||||
Behaviors.withMdc(Map("actorName" -> npcName))(
|
||||
Behaviors.withMdc[Command](Map("actorName" -> npcName))(
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
val npcMovementActor = ctx.spawn(
|
||||
(npcMovementActorBehavior),
|
||||
@ -164,7 +166,7 @@ object NpcMovementActor {
|
||||
doneSignal: ActorRef[CancelableFuture[DoneMoving.type]]
|
||||
) extends Command
|
||||
|
||||
final class Props[T: CanMove](
|
||||
class Props[T: CanMove](
|
||||
val enqueueR: Function1[() => Unit, Unit],
|
||||
val initialPos: ImVector3f,
|
||||
// val tickEventBus: GameEventBus[TickEvent],
|
||||
@ -196,7 +198,7 @@ class NpcMovementActor[T](
|
||||
target: ImVector3f,
|
||||
replyTo: ActorRef[CancelableFuture[DoneMoving.type]]
|
||||
) =>
|
||||
props.enqueueR(() => cm.move(props.movable, target - location))
|
||||
props.enqueueR(() => cm.move(props.movable, target - location, 20f))
|
||||
val p = CancelablePromise[DoneMoving.type]()
|
||||
replyTo ! p.future
|
||||
ticking(p, target, state)
|
||||
@ -221,22 +223,22 @@ class NpcMovementActor[T](
|
||||
ctx.self ! StopMoving
|
||||
reachDestination.success(DoneMoving)
|
||||
} else {
|
||||
// format:off
|
||||
ctx.log.traceP(
|
||||
show"npcActor-${props.npcName}: Difference = " + dst.formatted(
|
||||
"%.2f"
|
||||
)
|
||||
show"npcActor-${props.npcName}: Difference = ${dst.formatted("%.2f")}"
|
||||
)
|
||||
ctx.log.traceP(
|
||||
show"npcActor-${props.npcName}: Current pos = " + location
|
||||
show"npcActor-${props.npcName}: Current pos = $location"
|
||||
)
|
||||
}
|
||||
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
|
||||
object NpcMovementActorNotUsed {
|
||||
sealed trait Command
|
||||
final case class Props(
|
||||
class Props(
|
||||
imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||
npcName: String,
|
||||
// movementActor: ActorRef[ImMovementActor.Command],
|
||||
@ -251,7 +253,9 @@ object NpcMovementActorNotUsed {
|
||||
val movementActor = ctx.spawn(
|
||||
Behaviors
|
||||
.supervise(imMovementActorBehavior)
|
||||
.onFailure[Exception](SupervisorStrategy.restart),
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
s"npc-${npcName}-MovementActorChild"
|
||||
)
|
||||
val npcMovementElBehavior = Behaviors.receiveMessagePartial[Event] {
|
||||
@ -259,19 +263,19 @@ object NpcMovementActorNotUsed {
|
||||
event match {
|
||||
case MovedLeft(name, pressed) =>
|
||||
if (name == npcName)
|
||||
movementActor ! ImMovementActor.MovedLeft(pressed)
|
||||
movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||
Behaviors.same
|
||||
case MovedUp(name, pressed) =>
|
||||
if (name == npcName)
|
||||
movementActor ! ImMovementActor.MovedUp(pressed)
|
||||
movementActor ! ImMovementActor.MoveUp(pressed)
|
||||
Behaviors.same
|
||||
case MovedRight(name, pressed) =>
|
||||
if (name == npcName)
|
||||
movementActor ! ImMovementActor.MovedRight(pressed)
|
||||
movementActor ! ImMovementActor.MoveRight(pressed)
|
||||
Behaviors.same
|
||||
case MovedDown(name, pressed) =>
|
||||
if (name == npcName)
|
||||
movementActor ! ImMovementActor.MovedDown(pressed)
|
||||
movementActor ! ImMovementActor.MoveDown(pressed)
|
||||
Behaviors.same
|
||||
}
|
||||
}
|
||||
@ -279,7 +283,9 @@ object NpcMovementActorNotUsed {
|
||||
val npcMovementEl = ctx.spawn(
|
||||
Behaviors
|
||||
.supervise(npcMovementElBehavior)
|
||||
.onFailure[Exception](SupervisorStrategy.restart),
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
s"npc-${npcName}-MovementEventHandler"
|
||||
)
|
||||
val renderTickElBehavior =
|
||||
|
@ -1,13 +1,17 @@
|
||||
package wow.doge.mygame.game.entities
|
||||
|
||||
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.implicits._
|
||||
import wow.doge.mygame.subsystems.events.EventBus
|
||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||
@ -15,125 +19,250 @@ 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
|
||||
import scala.util.Success
|
||||
import scala.util.Failure
|
||||
import akka.util.Timeout
|
||||
import monix.reactive.Observable
|
||||
import monix.reactive.subjects.ConcurrentSubject
|
||||
import monix.execution.Scheduler
|
||||
import monix.reactive.OverflowStrategy
|
||||
object PlayerActorSupervisor {
|
||||
|
||||
type Ref = ActorRef[PlayerActorSupervisor.Command]
|
||||
|
||||
sealed trait Status
|
||||
object Status {
|
||||
case object Alive extends Status
|
||||
case object Dead extends Status
|
||||
}
|
||||
|
||||
sealed trait Command
|
||||
final case class Props(
|
||||
playerEventBus: GameEventBus[PlayerEvent],
|
||||
tickEventBus: GameEventBus[TickEvent],
|
||||
imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||
playerCameraActorBehavior: Behavior[PlayerCameraActor.Command]
|
||||
case class TakeDamage(value: Int) extends Command
|
||||
case class Heal(value: Int) extends Command
|
||||
case class CurrentStats(replyTo: ActorRef[StatsActor.State]) extends Command
|
||||
case class GetStatus(replyTo: ActorRef[Status]) extends Command
|
||||
case class GetStatsObservable(replyTo: ActorRef[Observable[StatsActor.State]])
|
||||
extends Command
|
||||
|
||||
private case object Die extends Command
|
||||
private case class DamageResponse(response: (Boolean, StatsActor.State))
|
||||
extends Command
|
||||
// private case class InternalTakeDamage(old: Int, value: Int) extends Command
|
||||
private case class LogError(ex: Throwable) extends Command
|
||||
class Props(
|
||||
val playerEventBus: GameEventBus[PlayerEvent],
|
||||
val tickEventBus: GameEventBus[TickEvent],
|
||||
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||
val scheduler: Scheduler
|
||||
) {
|
||||
def behavior =
|
||||
Behaviors.logMessages(
|
||||
LogOptions()
|
||||
.withLevel(Level.TRACE)
|
||||
.withLevel(Level.DEBUG)
|
||||
.withLogger(
|
||||
Logger[PlayerActorSupervisor].underlying
|
||||
),
|
||||
Behaviors.setup[Command] { ctx =>
|
||||
ctx.log.infoP("Starting PlayerActor")
|
||||
Behaviors
|
||||
.setup[Command] { ctx =>
|
||||
ctx.log.infoP("Starting PlayerActor")
|
||||
|
||||
// spawn children actors
|
||||
val movementActor =
|
||||
ctx.spawn(
|
||||
// spawn children actors
|
||||
val playerMovementActor =
|
||||
ctx.spawnN(
|
||||
Behaviors
|
||||
.supervise(imMovementActorBehavior)
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
),
|
||||
Dispatchers.jmeDispatcher
|
||||
)
|
||||
|
||||
val playerStatsActor =
|
||||
ctx.spawnN(new StatsActor.Props(100, 100).behavior)
|
||||
|
||||
val playerMovementEl = ctx.spawnN(
|
||||
Behaviors
|
||||
.supervise(imMovementActorBehavior)
|
||||
.onFailure[Exception](SupervisorStrategy.restart),
|
||||
"playerMovementActor"
|
||||
.supervise(PlayerMovementEventListener(playerMovementActor))
|
||||
.onFailure[Exception](
|
||||
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||
)
|
||||
)
|
||||
|
||||
val playerCameraActor =
|
||||
ctx.spawn(playerCameraActorBehavior, "playerCameraActor")
|
||||
val renderTickEl = {
|
||||
val behavior: Behavior[RenderTick.type] =
|
||||
Behaviors.setup(ctx =>
|
||||
Behaviors
|
||||
.receiveMessage[RenderTick.type] {
|
||||
case RenderTick =>
|
||||
playerMovementActor ! ImMovementActor.Tick
|
||||
// playerCameraActor ! PlayerCameraActor.Tick
|
||||
Behaviors.same
|
||||
}
|
||||
.receiveSignal {
|
||||
case (_, PostStop) =>
|
||||
ctx.log.infoP("stopped")
|
||||
Behaviors.same
|
||||
}
|
||||
)
|
||||
ctx.spawn(behavior, "playerMovementTickListener")
|
||||
}
|
||||
|
||||
val playerCameraEl = ctx.spawn(
|
||||
PlayerCameraEventListener(playerCameraActor),
|
||||
"playerCameraActorEl"
|
||||
)
|
||||
//init listeners
|
||||
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||
|
||||
val playerMovementEl = ctx.spawn(
|
||||
Behaviors
|
||||
.supervise(PlayerMovementEventListener(movementActor))
|
||||
.onFailure[Exception](SupervisorStrategy.restart),
|
||||
"playerMovementEventHandler"
|
||||
)
|
||||
|
||||
val renderTickEl = {
|
||||
val behavior =
|
||||
Behaviors.receiveMessage[RenderTick.type] {
|
||||
case RenderTick =>
|
||||
movementActor ! ImMovementActor.Tick
|
||||
// playerCameraActor ! PlayerCameraActor.Tick
|
||||
Behaviors.same
|
||||
}
|
||||
ctx.spawn(behavior, "playerMovementTickListener")
|
||||
new PlayerActorSupervisor(
|
||||
ctx,
|
||||
this,
|
||||
Children(playerMovementActor, playerStatsActor),
|
||||
ConcurrentSubject.publish(OverflowStrategy.DropOld(50))(scheduler)
|
||||
).aliveState
|
||||
}
|
||||
|
||||
//init listeners
|
||||
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||
playerEventBus ! EventBus.Subscribe(playerCameraEl)
|
||||
|
||||
new PlayerActorSupervisor(
|
||||
ctx,
|
||||
this,
|
||||
Children(movementActor)
|
||||
).receive
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
case class Children(
|
||||
movementActor: ActorRef[ImMovementActor.Command]
|
||||
movementActor: ActorRef[ImMovementActor.Command],
|
||||
statsActor: ActorRef[StatsActor.Command]
|
||||
)
|
||||
}
|
||||
class PlayerActorSupervisor(
|
||||
ctx: ActorContext[PlayerActorSupervisor.Command],
|
||||
props: PlayerActorSupervisor.Props,
|
||||
children: PlayerActorSupervisor.Children
|
||||
children: PlayerActorSupervisor.Children,
|
||||
statsSubject: ConcurrentSubject[StatsActor.State, StatsActor.State]
|
||||
) {
|
||||
import PlayerActorSupervisor._
|
||||
def receive =
|
||||
implicit val timeout = Timeout(1.second)
|
||||
val aliveState =
|
||||
Behaviors
|
||||
.receiveMessage[Command] {
|
||||
case _ =>
|
||||
case TakeDamage(value) =>
|
||||
// children.movementActor ! ImMovementActor.MovedDown(true)
|
||||
// ctx.ask(children.statsActor, StatsActor.CurrentStats(_)) {
|
||||
// case Success(status) => InternalTakeDamage(status.hp, value)
|
||||
// case Failure(ex) => LogError(ex)
|
||||
// }
|
||||
ctx.ask(children.statsActor, StatsActor.TakeDamageResult(value, _)) {
|
||||
case Success(response) => DamageResponse(response)
|
||||
case Failure(ex) => LogError(ex)
|
||||
}
|
||||
Behaviors.same
|
||||
case CurrentStats(replyTo) =>
|
||||
// ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||
Behaviors.same
|
||||
case Heal(value) =>
|
||||
children.statsActor ! StatsActor.Heal(value)
|
||||
Behaviors.same
|
||||
case GetStatus(replyTo) =>
|
||||
replyTo ! Status.Alive
|
||||
Behaviors.same
|
||||
// case _ => Behaviors.unhandled
|
||||
// case InternalTakeDamage(hp, damage) =>
|
||||
// if (hp - damage <= 0) dead
|
||||
// else {
|
||||
// children.statsActor ! StatsActor.TakeDamage(damage)
|
||||
// Behaviors.same
|
||||
// }
|
||||
case GetStatsObservable(replyTo) =>
|
||||
replyTo ! statsSubject
|
||||
Behaviors.same
|
||||
case DamageResponse(response) =>
|
||||
response match {
|
||||
case (dead, |