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")
|
Seq("base", "controls", "fxml", "graphics", "media", "swing", "web")
|
||||||
|
|
||||||
lazy val root = (project in file(".")).settings(
|
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",
|
name := "mygame",
|
||||||
organization := "wow.doge",
|
organization := "wow.doge",
|
||||||
version := "1.0-SNAPSHOT",
|
version := "1.0-SNAPSHOT",
|
||||||
@ -71,7 +64,9 @@ lazy val root = (project in file(".")).settings(
|
|||||||
"org.recast4j" % "recast" % "1.2.5",
|
"org.recast4j" % "recast" % "1.2.5",
|
||||||
"org.recast4j" % "detour" % "1.2.5",
|
"org.recast4j" % "detour" % "1.2.5",
|
||||||
"com.lihaoyi" %% "pprint" % "0.6.0",
|
"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
|
// Determine OS version of JavaFX binaries
|
||||||
|
|
||||||
@ -135,4 +130,11 @@ addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
|
|||||||
addCompilerPlugin(
|
addCompilerPlugin(
|
||||||
"org.typelevel" %% "kind-projector" % "0.11.1" cross CrossVersion.full
|
"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"
|
ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.4.3"
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
|
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
|
||||||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.23")
|
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 java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
import cats.data.Reader
|
import cats.Show
|
||||||
|
import cats.kernel.Eq
|
||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||||
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
import wow.doge.mygame.utils.wrappers.jme.CollisionShapeFactory
|
||||||
@ -12,21 +13,10 @@ sealed trait AppError
|
|||||||
object AppError {
|
object AppError {
|
||||||
final case class TimeoutError(reason: String) extends AppError
|
final case class TimeoutError(reason: String) extends AppError
|
||||||
object TimeoutError {
|
object TimeoutError {
|
||||||
def reader =
|
|
||||||
Reader[Throwable, TimeoutError] {
|
|
||||||
case ex: TimeoutException => TimeoutError(ex.getMessage)
|
|
||||||
}
|
|
||||||
def from: PartialFunction[Throwable, IO[TimeoutError, Nothing]] = {
|
def from: PartialFunction[Throwable, IO[TimeoutError, Nothing]] = {
|
||||||
case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
|
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 AssetManagerError(error: AssetManager.Error) extends AppError
|
||||||
final case class AppNodeError(error: NodeWrapper2.Error) extends AppError
|
final case class AppNodeError(error: NodeWrapper2.Error) extends AppError
|
||||||
final case class CollisionShapeCreationFailed(
|
final case class CollisionShapeCreationFailed(
|
||||||
@ -37,4 +27,7 @@ object AppError {
|
|||||||
case ex: TimeoutException => IO.raiseError(TimeoutError(ex.getMessage))
|
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
|
package wow.doge.mygame
|
||||||
|
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
@ -19,10 +17,8 @@ import com.jme3.material.MaterialDef
|
|||||||
import com.jme3.math.ColorRGBA
|
import com.jme3.math.ColorRGBA
|
||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
import com.jme3.renderer.Camera
|
import com.jme3.renderer.Camera
|
||||||
import com.jme3.renderer.RenderManager
|
|
||||||
import com.jme3.renderer.ViewPort
|
import com.jme3.renderer.ViewPort
|
||||||
import com.jme3.scene.Node
|
import com.jme3.scene.Node
|
||||||
import com.jme3.scene.control.AbstractControl
|
|
||||||
import com.softwaremill.macwire._
|
import com.softwaremill.macwire._
|
||||||
import com.softwaremill.tagging._
|
import com.softwaremill.tagging._
|
||||||
import io.odin.Logger
|
import io.odin.Logger
|
||||||
@ -33,38 +29,44 @@ import monix.bio.UIO
|
|||||||
import monix.eval.Coeval
|
import monix.eval.Coeval
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
import scalafx.scene.control.TextArea
|
import scalafx.scene.control.TextArea
|
||||||
|
import wow.doge.mygame.AppError.TimeoutError
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
import wow.doge.mygame.game.GameApp
|
import wow.doge.mygame.game.GameApp
|
||||||
import wow.doge.mygame.game.GameAppActor
|
import wow.doge.mygame.game.GameAppActor
|
||||||
import wow.doge.mygame.game.GameAppResource
|
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.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.PlayerActorSupervisor
|
||||||
import wow.doge.mygame.game.entities.PlayerController
|
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.input.GameInputHandler
|
||||||
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import wow.doge.mygame.launcher.Launcher
|
import wow.doge.mygame.launcher.Launcher
|
||||||
import wow.doge.mygame.launcher.Launcher.LauncherResult
|
import wow.doge.mygame.launcher.Launcher.LauncherResult
|
||||||
import wow.doge.mygame.math.ImVector3f
|
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.EventBus.ObservableSubscription
|
||||||
import wow.doge.mygame.subsystems.events.EventsModule
|
import wow.doge.mygame.subsystems.events.EventsModule
|
||||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||||
import wow.doge.mygame.subsystems.events.PlayerEvent
|
import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||||
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
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.events.TickEvent
|
||||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
|
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
|
||||||
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
|
||||||
import wow.doge.mygame.utils.AkkaUtils
|
import wow.doge.mygame.utils.AkkaUtils
|
||||||
import wow.doge.mygame.utils.GenericConsoleStream
|
import wow.doge.mygame.utils.GenericConsoleStream
|
||||||
import wow.doge.mygame.utils.IOUtils
|
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.AssetManager
|
||||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
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(
|
class MainApp(
|
||||||
logger: Logger[Task],
|
logger: Logger[Task],
|
||||||
jmeThread: monix.execution.Scheduler,
|
jmeThread: monix.execution.Scheduler,
|
||||||
@ -94,13 +96,8 @@ class MainApp(
|
|||||||
tickEventBus <- eventsModule.tickEventBus
|
tickEventBus <- eventsModule.tickEventBus
|
||||||
obs <-
|
obs <-
|
||||||
playerEventBus
|
playerEventBus
|
||||||
.askL[Observable[PlayerMovementEvent]](
|
.askL[Observable[PlayerMovementEvent]](ObservableSubscription(_))
|
||||||
ObservableSubscription(_)
|
.onErrorHandleWith(TimeoutError.from)
|
||||||
)
|
|
||||||
.onErrorHandleWith {
|
|
||||||
case ex: TimeoutException =>
|
|
||||||
IO.raiseError(AppError.TimeoutError(ex.getMessage()))
|
|
||||||
}
|
|
||||||
_ <-
|
_ <-
|
||||||
IOUtils
|
IOUtils
|
||||||
.toIO(
|
.toIO(
|
||||||
@ -121,7 +118,7 @@ class MainApp(
|
|||||||
// jfxUI <- gameApp.jfxUI
|
// jfxUI <- gameApp.jfxUI
|
||||||
gameAppActor <- gameApp.spawnGameActor(
|
gameAppActor <- gameApp.spawnGameActor(
|
||||||
GameAppActor.Props(tickEventBus).behavior,
|
GameAppActor.Props(tickEventBus).behavior,
|
||||||
"gameAppActor"
|
Some("gameAppActor")
|
||||||
)
|
)
|
||||||
_ <- gameAppActor !! GameAppActor.Start
|
_ <- gameAppActor !! GameAppActor.Start
|
||||||
consoleTextArea <- UIO(new TextArea {
|
consoleTextArea <- UIO(new TextArea {
|
||||||
@ -138,7 +135,7 @@ class MainApp(
|
|||||||
_ <-
|
_ <-
|
||||||
wire[MainAppDelegate]
|
wire[MainAppDelegate]
|
||||||
.init()
|
.init()
|
||||||
.executeOn(gameApp.scheduler)
|
.executeOn(gameApp.scheduler.value)
|
||||||
} yield fib
|
} yield fib
|
||||||
|
|
||||||
def gameInit: Resource[UIO, Either[AppError, Fiber[Nothing, Unit]]] =
|
def gameInit: Resource[UIO, Either[AppError, Fiber[Nothing, Unit]]] =
|
||||||
@ -184,6 +181,7 @@ class MainApp(
|
|||||||
class MainAppDelegate(
|
class MainAppDelegate(
|
||||||
gameApp: GameApp,
|
gameApp: GameApp,
|
||||||
loggerL: Logger[Task],
|
loggerL: Logger[Task],
|
||||||
|
mainEventBus: GameEventBus[Event],
|
||||||
playerEventBus: GameEventBus[PlayerEvent],
|
playerEventBus: GameEventBus[PlayerEvent],
|
||||||
tickEventBus: GameEventBus[TickEvent],
|
tickEventBus: GameEventBus[TickEvent],
|
||||||
inputManager: InputManager,
|
inputManager: InputManager,
|
||||||
@ -192,7 +190,8 @@ class MainAppDelegate(
|
|||||||
camera: Camera,
|
camera: Camera,
|
||||||
viewPort: ViewPort,
|
viewPort: ViewPort,
|
||||||
enqueueR: Function1[() => Unit, Unit],
|
enqueueR: Function1[() => Unit, Unit],
|
||||||
rootNode: AppNode2 @@ GameAppTags.RootNode
|
rootNode: RootNode,
|
||||||
|
schedulers: Schedulers
|
||||||
)(implicit
|
)(implicit
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
timeout: Timeout,
|
timeout: Timeout,
|
||||||
@ -213,17 +212,104 @@ class MainAppDelegate(
|
|||||||
// _ <- Task(consoleStream.println("text"))
|
// _ <- Task(consoleStream.println("text"))
|
||||||
level <- DefaultGameLevel(assetManager, viewPort)
|
level <- DefaultGameLevel(assetManager, viewPort)
|
||||||
_ <- level.addToGame(rootNode, physicsSpace)
|
_ <- level.addToGame(rootNode, physicsSpace)
|
||||||
_ <- createPlayerController()
|
playerActor <- createPlayerController()
|
||||||
// .onErrorRestart(3)
|
// .onErrorRestart(3)
|
||||||
_ <- wire[GameInputHandler.Props].begin
|
_ <- wire[GameInputHandler.Props].begin
|
||||||
// .onErrorRestart(3)
|
// .onErrorRestart(3)
|
||||||
johnActor <- createTestNpc("John")
|
johnActor <- createTestNpc("John")
|
||||||
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
|
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
|
||||||
|
|
||||||
|
// _ <-
|
||||||
|
// johnActor
|
||||||
|
// .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
|
||||||
|
// .delayExecution(2.seconds)
|
||||||
_ <-
|
_ <-
|
||||||
johnActor
|
rootNode.depthFirstTraversal
|
||||||
.tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10)))
|
.doOnNextF(spat => loggerL.debug(spat.getName).toTask)
|
||||||
.delayExecution(2.seconds)
|
.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
|
// IOUtils
|
||||||
// .toIO(
|
// .toIO(
|
||||||
@ -238,12 +324,12 @@ class MainAppDelegate(
|
|||||||
|
|
||||||
def createPlayerController(
|
def createPlayerController(
|
||||||
// appScheduler: monix.execution.Scheduler
|
// appScheduler: monix.execution.Scheduler
|
||||||
): IO[AppError, ActorRef[PlayerActorSupervisor.Command]] = {
|
): IO[AppError, PlayerActorSupervisor.Ref] = {
|
||||||
val playerPos = ImVector3f.Zero
|
val playerPos = ImVector3f.Zero
|
||||||
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
|
||||||
val playerPhysicsControl =
|
val playerPhysicsControl =
|
||||||
PlayerController.Defaults.defaultPlayerPhysicsControl
|
PlayerController.Defaults.defaultPlayerPhysicsControl
|
||||||
.taggedWith[PlayerControllerTags.PlayerTag]
|
|
||||||
for {
|
for {
|
||||||
playerModel <-
|
playerModel <-
|
||||||
assetManager
|
assetManager
|
||||||
@ -260,42 +346,115 @@ class MainAppDelegate(
|
|||||||
)
|
)
|
||||||
cameraPivotNode <- UIO(
|
cameraPivotNode <- UIO(
|
||||||
new Node(EntityIds.CameraPivot.value)
|
new Node(EntityIds.CameraPivot.value)
|
||||||
.withControl(new FollowControl(playerNode))
|
.withControl(
|
||||||
.taggedWith[PlayerControllerTags.PlayerCameraPivotNode]
|
new FollowControl(playerNode)
|
||||||
|
)
|
||||||
|
.taggedWith[PlayerController.Tags.PlayerCameraPivotNode]
|
||||||
)
|
)
|
||||||
camNode <- UIO(
|
camNode <- UIO(
|
||||||
PlayerController.Defaults
|
PlayerController.Defaults
|
||||||
.defaultCamerNode(camera, playerPos)
|
.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
|
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
|
} yield playerActor
|
||||||
}
|
}
|
||||||
|
|
||||||
def createTestNpc(
|
def createTestNpc(
|
||||||
// appScheduler: monix.execution.Scheduler,
|
// appScheduler: monix.execution.Scheduler,
|
||||||
npcName: String
|
npcName: String
|
||||||
): IO[AppError, ActorRef[NpcActorSupervisor.Command]] = {
|
): IO[AppError, NpcActorSupervisor.Ref] = {
|
||||||
val initialPos = ImVector3f(50, 5, 0)
|
val initialPos = ImVector3f(50, 5, 0)
|
||||||
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
||||||
// (1f, 2.1f, 10f)
|
// (1f, 2.1f, 10f)
|
||||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
.withJumpForce(ImVector3f(0, 5f, 0))
|
||||||
|
|
||||||
val npcActorTask = AkkaUtils.spawnActorL(
|
val npcActorTask = AkkaUtils.spawnActorL(
|
||||||
NpcActorSupervisor
|
new NpcActorSupervisor.Props(
|
||||||
.Props(
|
new NpcMovementActor.Props(
|
||||||
new NpcMovementActor.Props(
|
enqueueR,
|
||||||
enqueueR,
|
initialPos,
|
||||||
initialPos,
|
|
||||||
// tickEventBus,
|
|
||||||
npcName,
|
|
||||||
npcPhysicsControl
|
|
||||||
).behavior,
|
|
||||||
npcName,
|
npcName,
|
||||||
initialPos
|
npcPhysicsControl
|
||||||
)
|
).behavior,
|
||||||
.behavior,
|
npcName,
|
||||||
s"${npcName}-npcActorSupervisor"
|
initialPos
|
||||||
|
).behavior,
|
||||||
|
actorName = Some(s"${npcName}-npcActorSupervisor")
|
||||||
)
|
)
|
||||||
|
|
||||||
(for {
|
(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.AkkaUtils
|
||||||
import wow.doge.mygame.utils.GenericTimerActor
|
import wow.doge.mygame.utils.GenericTimerActor
|
||||||
import wow.doge.mygame.utils.wrappers.jme._
|
import wow.doge.mygame.utils.wrappers.jme._
|
||||||
|
import wow.doge.mygame.types._
|
||||||
object GameAppTags {
|
object GameAppTags {
|
||||||
sealed trait RootNode
|
sealed trait RootNode
|
||||||
|
|
||||||
sealed trait GuiNode
|
sealed trait GuiNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,34 +48,39 @@ class GameApp private[game] (
|
|||||||
logger: Logger[Task],
|
logger: Logger[Task],
|
||||||
app: SimpleAppExt,
|
app: SimpleAppExt,
|
||||||
gameActor: ActorRef[TestGameActor.Command],
|
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 inputManager: UIO[InputManager] = UIO(app.getInputManager())
|
||||||
def assetManager = new AssetManager(app.getAssetManager())
|
val assetManager = new AssetManager(app.getAssetManager())
|
||||||
def guiNode = UIO(app.getGuiNode().taggedWith[GameAppTags.GuiNode])
|
val guiNode: GuiNode =
|
||||||
val guiNode2 = AppNode2(app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
|
AppNode2(app.getGuiNode()).taggedWith[GameAppTags.GuiNode]
|
||||||
def flyCam = Option(app.getFlyByCamera())
|
// def flyCam = Option(app.getFlyByCamera())
|
||||||
def camera = UIO(app.getCamera())
|
def camera = UIO(app.getCamera())
|
||||||
def viewPort = UIO(app.getViewPort())
|
def viewPort = UIO(app.getViewPort())
|
||||||
// def rootNode = UIO(app.getRootNode().taggedWith[GameAppTags.RootNode])
|
val rootNode: RootNode =
|
||||||
val rootNode =
|
|
||||||
AppNode2(app.getRootNode()).taggedWith[GameAppTags.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 enqueue(cb: () => Unit) = app.enqueueR(() => cb())
|
||||||
def enqueueL[T](cb: () => T): Task[T] = app.enqueueL(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](
|
def spawnGameActor[T](
|
||||||
behavior: Behavior[T],
|
behavior: Behavior[T],
|
||||||
actorName: String,
|
actorName: Option[String] = None,
|
||||||
props: Props = Dispatchers.jmeDispatcher
|
props: Props = Dispatchers.jmeDispatcher
|
||||||
)(implicit scheduler: akka.actor.typed.Scheduler) =
|
)(implicit name: sourcecode.Name) =
|
||||||
AkkaUtils.spawnActorL(behavior, actorName, props)(
|
AkkaUtils.spawnActorL(behavior, actorName, props)(
|
||||||
2.seconds,
|
2.seconds,
|
||||||
scheduler,
|
scheduler,
|
||||||
gameSpawnProtocol
|
gameSpawnProtocol,
|
||||||
|
name
|
||||||
)
|
)
|
||||||
|
|
||||||
def scheduler = app.scheduler
|
def scheduler = JmeScheduler(app.scheduler)
|
||||||
def jfxUI = JFxUI(app)
|
def jfxUI = JFxUI(app)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -92,10 +96,9 @@ class GameAppResource(
|
|||||||
) {
|
) {
|
||||||
def resource
|
def resource
|
||||||
: Resource[UIO, Either[AppError, (GameApp, Fiber[Nothing, Unit])]] =
|
: Resource[UIO, Either[AppError, (GameApp, Fiber[Nothing, Unit])]] =
|
||||||
Resource.make {
|
Resource.make(
|
||||||
lazy val bullet = new BulletAppState
|
|
||||||
(for {
|
(for {
|
||||||
app <- UIO(new SimpleAppExt(schedulers, bullet))
|
app <- UIO(new SimpleAppExt(schedulers, new BulletAppState))
|
||||||
_ <- UIO(JMERunner.runner = Some(app.enqueue _))
|
_ <- UIO(JMERunner.runner = Some(app.enqueue _))
|
||||||
_ <- UIO {
|
_ <- UIO {
|
||||||
val settings = new AppSettings(true)
|
val settings = new AppSettings(true)
|
||||||
@ -113,23 +116,22 @@ class GameAppResource(
|
|||||||
_ <- Task.deferFuture(app.started).onErrorHandleWith(TimeoutError.from)
|
_ <- Task.deferFuture(app.started).onErrorHandleWith(TimeoutError.from)
|
||||||
testGameActor <- AkkaUtils.spawnActorL(
|
testGameActor <- AkkaUtils.spawnActorL(
|
||||||
new TestGameActor.Props().create,
|
new TestGameActor.Props().create,
|
||||||
"testGameActor",
|
Some("testGameActor"),
|
||||||
Dispatchers.jmeDispatcher
|
props = Dispatchers.jmeDispatcher
|
||||||
)
|
)
|
||||||
sp <-
|
sp <-
|
||||||
testGameActor
|
testGameActor
|
||||||
.askL(TestGameActor.GetSpawnProtocol(_))
|
.askL(TestGameActor.GetSpawnProtocol)
|
||||||
.onErrorHandleWith(TimeoutError.from)
|
.onErrorHandleWith(TimeoutError.from)
|
||||||
gameApp <- UIO(new GameApp(logger, app, testGameActor, sp))
|
gameApp <- UIO(new GameApp(logger, app, testGameActor, sp, scheduler))
|
||||||
_ <- UIO {
|
_ <- UIO {
|
||||||
val fut = () => testGameActor.ask(TestGameActor.Stop).flatten
|
val fut = () => testGameActor.ask(TestGameActor.Stop).flatten
|
||||||
app.cancelToken = Some(fut)
|
app.cancelToken = Some(fut)
|
||||||
}
|
}
|
||||||
} yield (gameApp, fib)).attempt
|
} yield (gameApp, fib)).attempt
|
||||||
} {
|
) {
|
||||||
case Right(gameApp -> fib) =>
|
case Right(gameApp -> fib) => fib.cancel
|
||||||
fib.cancel >> UIO(JMERunner.runner = None)
|
case Left(error) => IO.terminate(new Exception(error.toString))
|
||||||
case Left(error) => IO.terminate(new Exception(error.toString))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,10 +215,10 @@ object Ops {
|
|||||||
|
|
||||||
object SpawnSystem {
|
object SpawnSystem {
|
||||||
sealed trait Result
|
sealed trait Result
|
||||||
final case object Ok extends Result
|
case object Ok extends Result
|
||||||
|
|
||||||
sealed trait Complete
|
sealed trait Complete
|
||||||
final case object Complete extends Complete
|
case object Complete extends Complete
|
||||||
|
|
||||||
sealed trait SpawnRequest
|
sealed trait SpawnRequest
|
||||||
final case class SpawnSpatial(nodeTask: Task[Node]) extends SpawnRequest
|
final case class SpawnSpatial(nodeTask: Task[Node]) extends SpawnRequest
|
||||||
|
@ -24,18 +24,18 @@ object GameAppActor {
|
|||||||
Behaviors.setup[Command] { ctx =>
|
Behaviors.setup[Command] { ctx =>
|
||||||
ctx.log.infoP("Hello from GameAppActor")
|
ctx.log.infoP("Hello from GameAppActor")
|
||||||
val renderTickGenerator =
|
val renderTickGenerator =
|
||||||
ctx.spawn(
|
ctx.spawnN(
|
||||||
Behaviors
|
Behaviors
|
||||||
.supervise(renderTickGeneratorBehavior)
|
.supervise(renderTickGeneratorBehavior)
|
||||||
.onFailure[Exception](SupervisorStrategy.restart),
|
.onFailure[Exception](
|
||||||
"tickGeneratorActor"
|
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val tickGeneratorTimer = ctx.spawn(
|
val tickGeneratorTimer = ctx.spawnN(
|
||||||
GenericTimerActor
|
GenericTimerActor
|
||||||
.Props(renderTickGenerator, TickGenerator.Send, 10.millis)
|
.Props(renderTickGenerator, TickGenerator.Send, 10.millis)
|
||||||
.behavior,
|
.behavior
|
||||||
"tickGeneratorTimer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
|
@ -14,7 +14,6 @@ import monix.execution.Scheduler
|
|||||||
import monix.execution.atomic.Atomic
|
import monix.execution.atomic.Atomic
|
||||||
import wow.doge.mygame.executors.GUIExecutorService
|
import wow.doge.mygame.executors.GUIExecutorService
|
||||||
import wow.doge.mygame.executors.Schedulers
|
import wow.doge.mygame.executors.Schedulers
|
||||||
// import wow.doge.mygame.implicits._
|
|
||||||
class SimpleAppExt(
|
class SimpleAppExt(
|
||||||
schedulers: Schedulers,
|
schedulers: Schedulers,
|
||||||
val bulletAppState: BulletAppState,
|
val bulletAppState: BulletAppState,
|
||||||
@ -28,11 +27,16 @@ class SimpleAppExt(
|
|||||||
private val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
|
private val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
|
||||||
|
|
||||||
private val startSignal: CancelablePromise[Unit] = CancelablePromise()
|
private val startSignal: CancelablePromise[Unit] = CancelablePromise()
|
||||||
|
private val terminationSignal: CancelablePromise[Unit] = CancelablePromise()
|
||||||
|
|
||||||
var cancelToken: Option[() => Future[Unit]] = None
|
var cancelToken: Option[() => Future[Unit]] = None
|
||||||
|
|
||||||
def started: CancelableFuture[Unit] = startSignal.future
|
def started: CancelableFuture[Unit] = startSignal.future
|
||||||
|
|
||||||
|
def whenTerminated: CancelableFuture[Unit] = terminationSignal.future
|
||||||
|
|
||||||
|
// override def physicsSpace: PhysicsSpace = ???
|
||||||
|
|
||||||
override def simpleInitApp(): Unit = {
|
override def simpleInitApp(): Unit = {
|
||||||
stateManager.attach(bulletAppState)
|
stateManager.attach(bulletAppState)
|
||||||
startSignal.success(())
|
startSignal.success(())
|
||||||
@ -40,18 +44,19 @@ class SimpleAppExt(
|
|||||||
|
|
||||||
override def simpleUpdate(tpf: Float): Unit = {}
|
override def simpleUpdate(tpf: Float): Unit = {}
|
||||||
|
|
||||||
override def stop(waitFor: Boolean): Unit = {
|
override def stop(waitFor: Boolean): Unit =
|
||||||
cancelToken match {
|
cancelToken match {
|
||||||
case Some(value) =>
|
case Some(value) =>
|
||||||
value().foreach { _ =>
|
value().foreach { _ =>
|
||||||
pprint.log("Called cancel in simpleapp")
|
pprint.log("Called cancel in simpleapp")
|
||||||
super.stop(true)
|
super.stop(true)
|
||||||
|
terminationSignal.success(())
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
pprint.log("Called cancel in simpleapp")
|
pprint.log("Called cancel in simpleapp")
|
||||||
super.stop(true)
|
super.stop(true)
|
||||||
|
terminationSignal.success(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def enqueueFuture[T](cb: () => T): CancelableFuture[T] = {
|
def enqueueFuture[T](cb: () => T): CancelableFuture[T] = {
|
||||||
val p = CancelablePromise[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
|
import wow.doge.mygame.utils.GenericTimerActor
|
||||||
|
|
||||||
object NpcActorSupervisor {
|
object NpcActorSupervisor {
|
||||||
|
type Ref = ActorRef[Command]
|
||||||
|
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
final case class Move(pos: ImVector3f) extends Command
|
final case class Move(pos: ImVector3f) extends Command
|
||||||
private final case class InternalMove(
|
private final case class InternalMove(
|
||||||
@ -40,13 +42,13 @@ object NpcActorSupervisor {
|
|||||||
private case class LogError(err: Throwable) extends Command
|
private case class LogError(err: Throwable) extends Command
|
||||||
private case class MovementFailed(err: Throwable) extends Command
|
private case class MovementFailed(err: Throwable) extends Command
|
||||||
|
|
||||||
final case class Props(
|
class Props(
|
||||||
npcMovementActorBehavior: Behavior[NpcMovementActor.Command],
|
val npcMovementActorBehavior: Behavior[NpcMovementActor.Command],
|
||||||
npcName: String,
|
val npcName: String,
|
||||||
initialPos: ImVector3f
|
val initialPos: ImVector3f
|
||||||
) {
|
) {
|
||||||
def behavior =
|
def behavior =
|
||||||
Behaviors.withMdc(Map("actorName" -> npcName))(
|
Behaviors.withMdc[Command](Map("actorName" -> npcName))(
|
||||||
Behaviors.setup[Command] { ctx =>
|
Behaviors.setup[Command] { ctx =>
|
||||||
val npcMovementActor = ctx.spawn(
|
val npcMovementActor = ctx.spawn(
|
||||||
(npcMovementActorBehavior),
|
(npcMovementActorBehavior),
|
||||||
@ -164,7 +166,7 @@ object NpcMovementActor {
|
|||||||
doneSignal: ActorRef[CancelableFuture[DoneMoving.type]]
|
doneSignal: ActorRef[CancelableFuture[DoneMoving.type]]
|
||||||
) extends Command
|
) extends Command
|
||||||
|
|
||||||
final class Props[T: CanMove](
|
class Props[T: CanMove](
|
||||||
val enqueueR: Function1[() => Unit, Unit],
|
val enqueueR: Function1[() => Unit, Unit],
|
||||||
val initialPos: ImVector3f,
|
val initialPos: ImVector3f,
|
||||||
// val tickEventBus: GameEventBus[TickEvent],
|
// val tickEventBus: GameEventBus[TickEvent],
|
||||||
@ -196,7 +198,7 @@ class NpcMovementActor[T](
|
|||||||
target: ImVector3f,
|
target: ImVector3f,
|
||||||
replyTo: ActorRef[CancelableFuture[DoneMoving.type]]
|
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]()
|
val p = CancelablePromise[DoneMoving.type]()
|
||||||
replyTo ! p.future
|
replyTo ! p.future
|
||||||
ticking(p, target, state)
|
ticking(p, target, state)
|
||||||
@ -221,22 +223,22 @@ class NpcMovementActor[T](
|
|||||||
ctx.self ! StopMoving
|
ctx.self ! StopMoving
|
||||||
reachDestination.success(DoneMoving)
|
reachDestination.success(DoneMoving)
|
||||||
} else {
|
} else {
|
||||||
|
// format:off
|
||||||
ctx.log.traceP(
|
ctx.log.traceP(
|
||||||
show"npcActor-${props.npcName}: Difference = " + dst.formatted(
|
show"npcActor-${props.npcName}: Difference = ${dst.formatted("%.2f")}"
|
||||||
"%.2f"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
ctx.log.traceP(
|
ctx.log.traceP(
|
||||||
show"npcActor-${props.npcName}: Current pos = " + location
|
show"npcActor-${props.npcName}: Current pos = $location"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object NpcMovementActorNotUsed {
|
object NpcMovementActorNotUsed {
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
final case class Props(
|
class Props(
|
||||||
imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||||
npcName: String,
|
npcName: String,
|
||||||
// movementActor: ActorRef[ImMovementActor.Command],
|
// movementActor: ActorRef[ImMovementActor.Command],
|
||||||
@ -251,7 +253,9 @@ object NpcMovementActorNotUsed {
|
|||||||
val movementActor = ctx.spawn(
|
val movementActor = ctx.spawn(
|
||||||
Behaviors
|
Behaviors
|
||||||
.supervise(imMovementActorBehavior)
|
.supervise(imMovementActorBehavior)
|
||||||
.onFailure[Exception](SupervisorStrategy.restart),
|
.onFailure[Exception](
|
||||||
|
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
),
|
||||||
s"npc-${npcName}-MovementActorChild"
|
s"npc-${npcName}-MovementActorChild"
|
||||||
)
|
)
|
||||||
val npcMovementElBehavior = Behaviors.receiveMessagePartial[Event] {
|
val npcMovementElBehavior = Behaviors.receiveMessagePartial[Event] {
|
||||||
@ -259,19 +263,19 @@ object NpcMovementActorNotUsed {
|
|||||||
event match {
|
event match {
|
||||||
case MovedLeft(name, pressed) =>
|
case MovedLeft(name, pressed) =>
|
||||||
if (name == npcName)
|
if (name == npcName)
|
||||||
movementActor ! ImMovementActor.MovedLeft(pressed)
|
movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case MovedUp(name, pressed) =>
|
case MovedUp(name, pressed) =>
|
||||||
if (name == npcName)
|
if (name == npcName)
|
||||||
movementActor ! ImMovementActor.MovedUp(pressed)
|
movementActor ! ImMovementActor.MoveUp(pressed)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case MovedRight(name, pressed) =>
|
case MovedRight(name, pressed) =>
|
||||||
if (name == npcName)
|
if (name == npcName)
|
||||||
movementActor ! ImMovementActor.MovedRight(pressed)
|
movementActor ! ImMovementActor.MoveRight(pressed)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case MovedDown(name, pressed) =>
|
case MovedDown(name, pressed) =>
|
||||||
if (name == npcName)
|
if (name == npcName)
|
||||||
movementActor ! ImMovementActor.MovedDown(pressed)
|
movementActor ! ImMovementActor.MoveDown(pressed)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,7 +283,9 @@ object NpcMovementActorNotUsed {
|
|||||||
val npcMovementEl = ctx.spawn(
|
val npcMovementEl = ctx.spawn(
|
||||||
Behaviors
|
Behaviors
|
||||||
.supervise(npcMovementElBehavior)
|
.supervise(npcMovementElBehavior)
|
||||||
.onFailure[Exception](SupervisorStrategy.restart),
|
.onFailure[Exception](
|
||||||
|
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
),
|
||||||
s"npc-${npcName}-MovementEventHandler"
|
s"npc-${npcName}-MovementEventHandler"
|
||||||
)
|
)
|
||||||
val renderTickElBehavior =
|
val renderTickElBehavior =
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
package wow.doge.mygame.game.entities
|
package wow.doge.mygame.game.entities
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.LogOptions
|
import akka.actor.typed.LogOptions
|
||||||
|
import akka.actor.typed.PostStop
|
||||||
import akka.actor.typed.SupervisorStrategy
|
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 com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.slf4j.event.Level
|
import org.slf4j.event.Level
|
||||||
|
import wow.doge.mygame.Dispatchers
|
||||||
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
|
||||||
@ -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
|
||||||
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 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 {
|
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
|
sealed trait Command
|
||||||
final case class Props(
|
case class TakeDamage(value: Int) extends Command
|
||||||
playerEventBus: GameEventBus[PlayerEvent],
|
case class Heal(value: Int) extends Command
|
||||||
tickEventBus: GameEventBus[TickEvent],
|
case class CurrentStats(replyTo: ActorRef[StatsActor.State]) extends Command
|
||||||
imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
case class GetStatus(replyTo: ActorRef[Status]) extends Command
|
||||||
playerCameraActorBehavior: Behavior[PlayerCameraActor.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 =
|
def behavior =
|
||||||
Behaviors.logMessages(
|
Behaviors.logMessages(
|
||||||
LogOptions()
|
LogOptions()
|
||||||
.withLevel(Level.TRACE)
|
.withLevel(Level.DEBUG)
|
||||||
.withLogger(
|
.withLogger(
|
||||||
Logger[PlayerActorSupervisor].underlying
|
Logger[PlayerActorSupervisor].underlying
|
||||||
),
|
),
|
||||||
Behaviors.setup[Command] { ctx =>
|
Behaviors
|
||||||
ctx.log.infoP("Starting PlayerActor")
|
.setup[Command] { ctx =>
|
||||||
|
ctx.log.infoP("Starting PlayerActor")
|
||||||
|
|
||||||
// spawn children actors
|
// spawn children actors
|
||||||
val movementActor =
|
val playerMovementActor =
|
||||||
ctx.spawn(
|
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
|
Behaviors
|
||||||
.supervise(imMovementActorBehavior)
|
.supervise(PlayerMovementEventListener(playerMovementActor))
|
||||||
.onFailure[Exception](SupervisorStrategy.restart),
|
.onFailure[Exception](
|
||||||
"playerMovementActor"
|
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val playerCameraActor =
|
val renderTickEl = {
|
||||||
ctx.spawn(playerCameraActorBehavior, "playerCameraActor")
|
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(
|
//init listeners
|
||||||
PlayerCameraEventListener(playerCameraActor),
|
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
||||||
"playerCameraActorEl"
|
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||||
)
|
|
||||||
|
|
||||||
val playerMovementEl = ctx.spawn(
|
new PlayerActorSupervisor(
|
||||||
Behaviors
|
ctx,
|
||||||
.supervise(PlayerMovementEventListener(movementActor))
|
this,
|
||||||
.onFailure[Exception](SupervisorStrategy.restart),
|
Children(playerMovementActor, playerStatsActor),
|
||||||
"playerMovementEventHandler"
|
ConcurrentSubject.publish(OverflowStrategy.DropOld(50))(scheduler)
|
||||||
)
|
).aliveState
|
||||||
|
|
||||||
val renderTickEl = {
|
|
||||||
val behavior =
|
|
||||||
Behaviors.receiveMessage[RenderTick.type] {
|
|
||||||
case RenderTick =>
|
|
||||||
movementActor ! ImMovementActor.Tick
|
|
||||||
// playerCameraActor ! PlayerCameraActor.Tick
|
|
||||||
Behaviors.same
|
|
||||||
}
|
|
||||||
ctx.spawn(behavior, "playerMovementTickListener")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//init listeners
|
|
||||||
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
|
||||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
|
||||||
playerEventBus ! EventBus.Subscribe(playerCameraEl)
|
|
||||||
|
|
||||||
new PlayerActorSupervisor(
|
|
||||||
ctx,
|
|
||||||
this,
|
|
||||||
Children(movementActor)
|
|
||||||
).receive
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Children(
|
case class Children(
|
||||||
movementActor: ActorRef[ImMovementActor.Command]
|
movementActor: ActorRef[ImMovementActor.Command],
|
||||||
|
statsActor: ActorRef[StatsActor.Command]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
class PlayerActorSupervisor(
|
class PlayerActorSupervisor(
|
||||||
ctx: ActorContext[PlayerActorSupervisor.Command],
|
ctx: ActorContext[PlayerActorSupervisor.Command],
|
||||||
props: PlayerActorSupervisor.Props,
|
props: PlayerActorSupervisor.Props,
|
||||||
children: PlayerActorSupervisor.Children
|
children: PlayerActorSupervisor.Children,
|
||||||
|
statsSubject: ConcurrentSubject[StatsActor.State, StatsActor.State]
|
||||||
) {
|
) {
|
||||||
import PlayerActorSupervisor._
|
import PlayerActorSupervisor._
|
||||||
def receive =
|
implicit val timeout = Timeout(1.second)
|
||||||
|
val aliveState =
|
||||||
Behaviors
|
Behaviors
|
||||||
.receiveMessage[Command] {
|
.receiveMessage[Command] {
|
||||||
case _ =>
|
case TakeDamage(value) =>
|
||||||
// children.movementActor ! ImMovementActor.MovedDown(true)
|
// 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, state) =>
|
||||||
|
if (dead) ctx.self ! Die
|
||||||
|
statsSubject.onNext(state)
|
||||||
|
}
|
||||||
|
Behaviors.same
|
||||||
|
case Die => deadState
|
||||||
|
case LogError(ex) =>
|
||||||
|
ctx.log.error(ex.getMessage)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
ctx.log.infoP("stopped")
|
||||||
|
statsSubject.onComplete()
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
val deadState = Behaviors
|
||||||
|
.receiveMessage[Command] {
|
||||||
|
// case TakeDamage(value) =>
|
||||||
|
// children.statsActor ! StatsActor.TakeDamage(value)
|
||||||
|
// // children.movementActor ! ImMovementActor.MovedDown(true)
|
||||||
|
// Behaviors.same
|
||||||
|
// case CurrentStats(replyTo) =>
|
||||||
|
// // ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||||
|
// children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||||
|
// Behaviors.same
|
||||||
|
// case Heal(_) =>
|
||||||
|
// Behaviors.same
|
||||||
|
case CurrentStats(replyTo) =>
|
||||||
|
// ctx.ask(children.statsActor, StatsActor.CurrentStats())
|
||||||
|
children.statsActor ! StatsActor.CurrentStats(replyTo)
|
||||||
|
Behaviors.same
|
||||||
|
case GetStatus(replyTo) =>
|
||||||
|
replyTo ! Status.Dead
|
||||||
|
Behaviors.same
|
||||||
|
case _ => Behaviors.unhandled
|
||||||
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
ctx.log.infoP("stopped")
|
||||||
|
statsSubject.onComplete()
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object PlayerMovementActor {
|
object StatsActor {
|
||||||
|
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
final case class Props(
|
case class TakeDamage(value: Int) extends Command
|
||||||
movementActor: ActorRef[ImMovementActor.Command],
|
case class TakeDamageResult(value: Int, replyTo: ActorRef[(Boolean, State)])
|
||||||
playerCameraActor: ActorRef[PlayerCameraActor.Command],
|
extends Command
|
||||||
playerEventBus: GameEventBus[PlayerEvent],
|
case class Heal(value: Int) extends Command
|
||||||
tickEventBus: GameEventBus[TickEvent]
|
case class CurrentStats(replyTo: ActorRef[State]) extends Command
|
||||||
) {
|
|
||||||
|
|
||||||
def behavior: Behavior[Command] =
|
class Props(startingHealth: Int, startingStamina: Int) {
|
||||||
Behaviors.setup { ctx =>
|
def behavior =
|
||||||
val playerMovementEl = ctx.spawn(
|
Behaviors.setup[Command] { ctx =>
|
||||||
Behaviors
|
new StatsActor(ctx, this)
|
||||||
.supervise(PlayerMovementEventListener(movementActor))
|
.receive(State(startingHealth, startingStamina))
|
||||||
.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")
|
|
||||||
}
|
|
||||||
|
|
||||||
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
|
||||||
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
|
||||||
Behaviors.receiveMessage { msg => Behaviors.same }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class State(hp: Int, stamina: Int)
|
||||||
|
}
|
||||||
|
class StatsActor(
|
||||||
|
ctx: ActorContext[StatsActor.Command],
|
||||||
|
props: StatsActor.Props
|
||||||
|
) {
|
||||||
|
import StatsActor._
|
||||||
|
import com.softwaremill.quicklens._
|
||||||
|
def receive(state: State): Behavior[Command] =
|
||||||
|
Behaviors.receiveMessage[Command] {
|
||||||
|
// Todo add min max values
|
||||||
|
case TakeDamage(value) =>
|
||||||
|
val nextState =
|
||||||
|
if (state.hp - value <= 0)
|
||||||
|
state.modify(_.hp).setTo(0)
|
||||||
|
else
|
||||||
|
state.modify(_.hp).using(_ - value)
|
||||||
|
receive(nextState)
|
||||||
|
case TakeDamageResult(value, replyTo) =>
|
||||||
|
val nextState = if (state.hp - value <= 0) {
|
||||||
|
replyTo ! true -> state
|
||||||
|
state
|
||||||
|
} else {
|
||||||
|
replyTo ! false -> state
|
||||||
|
state.modify(_.hp).using(_ - value)
|
||||||
|
}
|
||||||
|
receive(nextState)
|
||||||
|
case Heal(value) => receive(state.modify(_.hp).using(_ + value))
|
||||||
|
case CurrentStats(replyTo) =>
|
||||||
|
replyTo ! state
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,149 @@
|
|||||||
|
package wow.doge.mygame.game.entities.player
|
||||||
|
|
||||||
|
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.game.entities.PlayerCameraActor
|
||||||
|
import wow.doge.mygame.game.entities.PlayerCameraEventListener
|
||||||
|
import wow.doge.mygame.game.entities.PlayerMovementEventListener
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import wow.doge.mygame.subsystems.events.EventBus
|
||||||
|
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||||
|
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
|
||||||
|
|
||||||
|
object PlayerActorSupervisor2 {
|
||||||
|
sealed trait Command
|
||||||
|
case object Tick extends Command
|
||||||
|
sealed trait Movement extends Command
|
||||||
|
final case class MoveLeft(pressed: Boolean) extends Movement
|
||||||
|
final case class MoveUp(pressed: Boolean) extends Movement
|
||||||
|
final case class MoveRight(pressed: Boolean) extends Movement
|
||||||
|
final case class MoveDown(pressed: Boolean) extends Movement
|
||||||
|
case object Jump extends Movement
|
||||||
|
sealed trait Camera
|
||||||
|
case object RotateLeft extends Camera
|
||||||
|
case object RotateRight extends Camera
|
||||||
|
case object RotateUp extends Camera
|
||||||
|
case object RotateDown extends Camera
|
||||||
|
class Props(
|
||||||
|
val playerEventBus: GameEventBus[PlayerEvent],
|
||||||
|
val tickEventBus: GameEventBus[TickEvent],
|
||||||
|
val imMovementActorBehavior: Behavior[ImMovementActor.Command],
|
||||||
|
val playerCameraActorBehavior: Behavior[PlayerCameraActor.Command]
|
||||||
|
) {
|
||||||
|
def behavior =
|
||||||
|
Behaviors.logMessages(
|
||||||
|
LogOptions()
|
||||||
|
.withLevel(Level.TRACE)
|
||||||
|
.withLogger(
|
||||||
|
Logger[PlayerActorSupervisor2].underlying
|
||||||
|
),
|
||||||
|
Behaviors
|
||||||
|
.setup[Command] { ctx =>
|
||||||
|
ctx.log.infoP("Starting PlayerActor")
|
||||||
|
|
||||||
|
// spawn children actors
|
||||||
|
val movementActor =
|
||||||
|
ctx.spawn(
|
||||||
|
Behaviors
|
||||||
|
.supervise(imMovementActorBehavior)
|
||||||
|
.onFailure[Exception](
|
||||||
|
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
),
|
||||||
|
"playerMovementActor",
|
||||||
|
Dispatchers.jmeDispatcher
|
||||||
|
)
|
||||||
|
|
||||||
|
val playerCameraActor =
|
||||||
|
ctx.spawn(
|
||||||
|
playerCameraActorBehavior,
|
||||||
|
"playerCameraActor",
|
||||||
|
Dispatchers.jmeDispatcher
|
||||||
|
)
|
||||||
|
|
||||||
|
val playerCameraEl = ctx.spawn(
|
||||||
|
PlayerCameraEventListener(playerCameraActor),
|
||||||
|
"playerCameraActorEl"
|
||||||
|
)
|
||||||
|
|
||||||
|
val playerMovementEl = ctx.spawn(
|
||||||
|
Behaviors
|
||||||
|
.supervise(PlayerMovementEventListener(movementActor))
|
||||||
|
.onFailure[Exception](
|
||||||
|
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
),
|
||||||
|
"playerMovementEventHandler"
|
||||||
|
)
|
||||||
|
|
||||||
|
val renderTickEl = {
|
||||||
|
val behavior: Behavior[RenderTick.type] =
|
||||||
|
Behaviors.setup(ctx =>
|
||||||
|
Behaviors
|
||||||
|
.receiveMessage[RenderTick.type] {
|
||||||
|
case RenderTick =>
|
||||||
|
movementActor ! ImMovementActor.Tick
|
||||||
|
// playerCameraActor ! PlayerCameraActor.Tick
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
ctx.log.infoP("stopped")
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
)
|
||||||
|
ctx.spawn(behavior, "playerMovementTickListener")
|
||||||
|
}
|
||||||
|
|
||||||
|
//init listeners
|
||||||
|
playerEventBus ! EventBus.Subscribe(playerMovementEl)
|
||||||
|
tickEventBus ! EventBus.Subscribe(renderTickEl)
|
||||||
|
playerEventBus ! EventBus.Subscribe(playerCameraEl)
|
||||||
|
|
||||||
|
new PlayerActorSupervisor2(
|
||||||
|
ctx,
|
||||||
|
this,
|
||||||
|
Children(movementActor)
|
||||||
|
).receive
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Children(
|
||||||
|
movementActor: ActorRef[ImMovementActor.Command]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
class PlayerActorSupervisor2(
|
||||||
|
ctx: ActorContext[PlayerActorSupervisor2.Command],
|
||||||
|
props: PlayerActorSupervisor2.Props,
|
||||||
|
children: PlayerActorSupervisor2.Children
|
||||||
|
) {
|
||||||
|
import PlayerActorSupervisor2._
|
||||||
|
def receive =
|
||||||
|
Behaviors
|
||||||
|
.receiveMessage[Command] {
|
||||||
|
case m @ MoveDown(pressed) =>
|
||||||
|
// children.movementActor ! m
|
||||||
|
Behaviors.same
|
||||||
|
case _ =>
|
||||||
|
// children.movementActor ! ImMovementActor.MovedDown(true)
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
ctx.log.infoP("stopped")
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
@ -55,28 +55,27 @@ class PlayerCameraActor(
|
|||||||
case RotateLeft =>
|
case RotateLeft =>
|
||||||
val rot = rotationBuf
|
val rot = rotationBuf
|
||||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||||
props.enqueueR(() => props.cameraPivotNode.rotate(rot))
|
props.cameraPivotNode.rotate(rot)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case RotateRight =>
|
case RotateRight =>
|
||||||
val rot = rotationBuf
|
val rot = rotationBuf
|
||||||
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_Y)
|
||||||
props.enqueueR(() => props.cameraPivotNode.rotate(rot))
|
props.cameraPivotNode.rotate(rot)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case RotateUp =>
|
case RotateUp =>
|
||||||
val rot = rotationBuf
|
val rot = rotationBuf
|
||||||
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
.fromAngleAxis(-1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||||
props.enqueueR(() => props.cameraPivotNode.rotate(rot))
|
props.cameraPivotNode.rotate(rot)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case RotateDown =>
|
case RotateDown =>
|
||||||
val rot = rotationBuf
|
val rot = rotationBuf
|
||||||
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
.fromAngleAxis(1 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X)
|
||||||
props.enqueueR(() => props.cameraPivotNode.rotate(rot))
|
props.cameraPivotNode.rotate(rot)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case Tick =>
|
case Tick =>
|
||||||
props.enqueueR(() => {
|
val location = props.getPlayerLocation()
|
||||||
val location = props.getPlayerLocation()
|
props.cameraPivotNode.setLocalTranslation(location)
|
||||||
props.cameraPivotNode.setLocalTranslation(location)
|
|
||||||
})
|
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
package wow.doge.mygame.game.entities
|
package wow.doge.mygame.game.entities
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.Scheduler
|
import akka.actor.typed.DispatcherSelector
|
||||||
import akka.actor.typed.SpawnProtocol
|
|
||||||
import akka.util.Timeout
|
|
||||||
import com.jme3.bullet.BulletAppState
|
|
||||||
import com.jme3.bullet.control.BetterCharacterControl
|
import com.jme3.bullet.control.BetterCharacterControl
|
||||||
import com.jme3.math.FastMath
|
|
||||||
import com.jme3.renderer.Camera
|
import com.jme3.renderer.Camera
|
||||||
import com.jme3.scene.CameraNode
|
import com.jme3.scene.CameraNode
|
||||||
import com.jme3.scene.Geometry
|
import com.jme3.scene.Geometry
|
||||||
@ -18,67 +14,76 @@ import io.odin.Logger
|
|||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import wow.doge.mygame.AppError
|
import wow.doge.mygame.AppError
|
||||||
import wow.doge.mygame.game.GameAppTags
|
import wow.doge.mygame.game.GameApp
|
||||||
import wow.doge.mygame.game.SimpleAppExt
|
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import wow.doge.mygame.math.ImVector3f
|
import wow.doge.mygame.math.ImVector3f
|
||||||
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus
|
||||||
import wow.doge.mygame.subsystems.events.PlayerEvent
|
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.movement.ImMovementActor
|
import wow.doge.mygame.subsystems.movement.ImMovementActor
|
||||||
import wow.doge.mygame.utils.AkkaUtils
|
|
||||||
import wow.doge.mygame.utils.wrappers.jme._
|
import wow.doge.mygame.utils.wrappers.jme._
|
||||||
object PlayerControllerTags {
|
import wow.doge.mygame.types._
|
||||||
sealed trait PlayerTag
|
|
||||||
sealed trait PlayerCameraNode
|
|
||||||
sealed trait PlayerCameraPivotNode
|
|
||||||
}
|
|
||||||
|
|
||||||
object PlayerController {
|
object PlayerController {
|
||||||
sealed trait Error
|
sealed trait Error
|
||||||
case class CouldNotCreatePlayerModel(reason: AssetManager.Error) extends Error
|
case class CouldNotCreatePlayerModel(reason: AssetManager.Error) extends Error
|
||||||
|
|
||||||
|
object Tags {
|
||||||
|
sealed trait PlayerNode
|
||||||
|
sealed trait PlayerCameraNode
|
||||||
|
sealed trait PlayerCameraPivotNode
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlayerNode = Node @@ Tags.PlayerNode
|
||||||
|
type PlayerCameraNode = CameraNode @@ Tags.PlayerCameraNode
|
||||||
|
type PlayerCameraPivotNode =
|
||||||
|
Node @@ Tags.PlayerCameraPivotNode
|
||||||
|
|
||||||
class Props(
|
class Props(
|
||||||
|
gameApp: GameApp,
|
||||||
enqueueR: Function1[() => Unit, Unit],
|
enqueueR: Function1[() => Unit, Unit],
|
||||||
rootNode: AppNode2 @@ GameAppTags.RootNode,
|
rootNode: RootNode,
|
||||||
loggerL: Logger[Task],
|
loggerL: Logger[Task],
|
||||||
// physicsSpace: com.jme3.bullet.PhysicsSpace,
|
|
||||||
physicsSpace: PhysicsSpace,
|
physicsSpace: PhysicsSpace,
|
||||||
initialPlayerPos: ImVector3f = ImVector3f.Zero,
|
initialPlayerPos: ImVector3f,
|
||||||
playerEventBus: GameEventBus[PlayerEvent],
|
playerEventBus: GameEventBus[PlayerEvent],
|
||||||
playerPhysicsControl: BetterCharacterControl,
|
playerPhysicsControl: BetterCharacterControl,
|
||||||
// appScheduler: monix.execution.Scheduler,
|
// appScheduler: monix.execution.Scheduler,
|
||||||
playerNode: Node @@ PlayerControllerTags.PlayerTag,
|
scheduler: monix.execution.Scheduler,
|
||||||
cameraNode: CameraNode @@ PlayerControllerTags.PlayerCameraNode,
|
playerNode: PlayerNode,
|
||||||
cameraPivotNode: Node @@ PlayerControllerTags.PlayerCameraPivotNode,
|
cameraNode: PlayerCameraNode,
|
||||||
|
cameraPivotNode: PlayerCameraPivotNode,
|
||||||
tickEventBus: GameEventBus[TickEvent],
|
tickEventBus: GameEventBus[TickEvent],
|
||||||
camera: Camera
|
camera: Camera
|
||||||
)(implicit
|
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
|
||||||
timeout: Timeout,
|
|
||||||
scheduler: Scheduler
|
|
||||||
) {
|
) {
|
||||||
val playerActorBehavior = {
|
val playerActorBehavior = {
|
||||||
val movementActorBeh = new ImMovementActor.Props(
|
val movementActorBeh = new ImMovementActor.Props(
|
||||||
enqueueR,
|
enqueueR,
|
||||||
camera
|
camera
|
||||||
).behavior(playerPhysicsControl)
|
).behavior(playerPhysicsControl)
|
||||||
val cameraActorBeh = new PlayerCameraActor.Props(
|
|
||||||
cameraPivotNode,
|
|
||||||
enqueueR,
|
|
||||||
playerNode.getWorldTranslation _
|
|
||||||
).behavior
|
|
||||||
new PlayerActorSupervisor.Props(
|
new PlayerActorSupervisor.Props(
|
||||||
playerEventBus,
|
playerEventBus,
|
||||||
tickEventBus,
|
tickEventBus,
|
||||||
movementActorBeh,
|
movementActorBeh,
|
||||||
cameraActorBeh
|
scheduler
|
||||||
).behavior
|
).behavior
|
||||||
}
|
}
|
||||||
|
val playerActor =
|
||||||
|
gameApp.spawnGameActor(
|
||||||
|
playerActorBehavior,
|
||||||
|
// Some("playerActorSupervisor"),
|
||||||
|
props = DispatcherSelector.default()
|
||||||
|
)
|
||||||
val create: IO[AppError, ActorRef[PlayerActorSupervisor.Command]] =
|
val create: IO[AppError, ActorRef[PlayerActorSupervisor.Command]] =
|
||||||
(for {
|
(for {
|
||||||
playerActor <-
|
// playerActor <-
|
||||||
AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")
|
// // AkkaUtils.spawnActorL(playerActorBehavior, "playerActorSupervisor")
|
||||||
|
// gameApp.spawnGameActor(
|
||||||
|
// playerActorBehavior,
|
||||||
|
// Some("playerActorSupervisor"),
|
||||||
|
// props = DispatcherSelector.default()
|
||||||
|
// )
|
||||||
|
pa <- playerActor
|
||||||
_ <- (for {
|
_ <- (for {
|
||||||
_ <- rootNode += playerNode
|
_ <- rootNode += playerNode
|
||||||
_ <- physicsSpace += playerNode
|
_ <- physicsSpace += playerNode
|
||||||
@ -86,61 +91,16 @@ object PlayerController {
|
|||||||
_ = cameraPivotNode += cameraNode
|
_ = cameraPivotNode += cameraNode
|
||||||
_ <- rootNode += cameraPivotNode
|
_ <- rootNode += cameraPivotNode
|
||||||
} yield ()).mapError(AppError.AppNodeError)
|
} yield ()).mapError(AppError.AppNodeError)
|
||||||
} yield playerActor)
|
} yield pa)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(
|
|
||||||
app: SimpleAppExt,
|
|
||||||
modelPath: os.RelPath,
|
|
||||||
cam: Camera
|
|
||||||
)(assetManager: AssetManager, bulletAppState: BulletAppState) = {
|
|
||||||
val playerPos = ImVector3f.Zero
|
|
||||||
val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f)
|
|
||||||
.withJumpForce(ImVector3f(0, 5f, 0))
|
|
||||||
val playerNode = new Node("PlayerNode")
|
|
||||||
.withChildren(
|
|
||||||
assetManager
|
|
||||||
.loadModel(modelPath)
|
|
||||||
.asInstanceOf[Node]
|
|
||||||
.withRotate(0, FastMath.PI, 0)
|
|
||||||
)
|
|
||||||
.withLocalTranslation(playerPos)
|
|
||||||
.withControl(playerPhysicsControl)
|
|
||||||
|
|
||||||
{
|
|
||||||
bulletAppState.physicsSpace += playerNode
|
|
||||||
bulletAppState.physicsSpace += playerPhysicsControl
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
app.rootNode += playerNode
|
|
||||||
}
|
|
||||||
|
|
||||||
playerPhysicsControl
|
|
||||||
}
|
|
||||||
|
|
||||||
object Defaults {
|
object Defaults {
|
||||||
def defaultMesh = {
|
def defaultMesh = {
|
||||||
val b = Box(1, 1, 1)
|
val b = Box(1, 1, 1)
|
||||||
val geom = Geometry("playerGeom", b)
|
val geom = Geometry("playerGeom", b)
|
||||||
geom
|
geom
|
||||||
}
|
}
|
||||||
// def defaultTexture(assetManager: AssetManager) =
|
|
||||||
// MyMaterial(
|
|
||||||
// assetManager = assetManager,
|
|
||||||
// path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md"
|
|
||||||
// )
|
|
||||||
|
|
||||||
// new CameraControl(cam) {
|
|
||||||
// override def controlUpdate(tpf: Float) = {
|
|
||||||
// this.getCamera().setRotation(spatial.getWorldRotation())
|
|
||||||
// cameraPivotNode.setLocalTranslation(
|
|
||||||
// playerNode.getWorldTranslation()
|
|
||||||
// )
|
|
||||||
// this.getCamera().setLocation(spatial.getWorldTranslation())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
def defaultCamerNode(
|
def defaultCamerNode(
|
||||||
cam: Camera,
|
cam: Camera,
|
||||||
@ -163,7 +123,7 @@ object PlayerController {
|
|||||||
.withChildren(playerModel)
|
.withChildren(playerModel)
|
||||||
.withLocalTranslation(playerPos)
|
.withLocalTranslation(playerPos)
|
||||||
.withControl(playerPhysicsControl)
|
.withControl(playerPhysicsControl)
|
||||||
.taggedWith[PlayerControllerTags.PlayerTag]
|
.taggedWith[Tags.PlayerNode]
|
||||||
|
|
||||||
def defaultNpcNode(
|
def defaultNpcNode(
|
||||||
// assetManager: AssetManager,
|
// assetManager: AssetManager,
|
||||||
|
@ -21,16 +21,16 @@ object PlayerMovementEventListener {
|
|||||||
Behaviors.setup[PlayerMovementEvent](ctx =>
|
Behaviors.setup[PlayerMovementEvent](ctx =>
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
case PlayerMovedLeft(pressed) =>
|
case PlayerMovedLeft(pressed) =>
|
||||||
movementActor ! ImMovementActor.MovedLeft(pressed)
|
movementActor ! ImMovementActor.MoveLeft(pressed)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case PlayerMovedRight(pressed) =>
|
case PlayerMovedRight(pressed) =>
|
||||||
movementActor ! ImMovementActor.MovedRight(pressed)
|
movementActor ! ImMovementActor.MoveRight(pressed)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case PlayerMovedForward(pressed) =>
|
case PlayerMovedForward(pressed) =>
|
||||||
movementActor ! ImMovementActor.MovedUp(pressed)
|
movementActor ! ImMovementActor.MoveUp(pressed)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case PlayerMovedBackward(pressed) =>
|
case PlayerMovedBackward(pressed) =>
|
||||||
movementActor ! ImMovementActor.MovedDown(pressed)
|
movementActor ! ImMovementActor.MoveDown(pressed)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case PlayerJumped =>
|
case PlayerJumped =>
|
||||||
movementActor ! ImMovementActor.Jump
|
movementActor ! ImMovementActor.Jump
|
||||||
|
@ -43,10 +43,10 @@ object GameInputHandler {
|
|||||||
// inputManager,
|
// inputManager,
|
||||||
// playerEventBus
|
// playerEventBus
|
||||||
// ).completedL,
|
// ).completedL,
|
||||||
generateCameraEvents(
|
// cameraMovementEventsGenerator(
|
||||||
inputManager,
|
// inputManager,
|
||||||
playerEventBus
|
// playerEventBus
|
||||||
).completedL,
|
// ).completedL,
|
||||||
Ref
|
Ref
|
||||||
.of[me.Task, Boolean](false)
|
.of[me.Task, Boolean](false)
|
||||||
.flatMap(value => cursorToggle(value))
|
.flatMap(value => cursorToggle(value))
|
||||||
@ -170,14 +170,14 @@ object GameInputHandler {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
case PlayerMovementInput.Jump =>
|
case PlayerMovementInput.Jump =>
|
||||||
if (action.value) {
|
if (action.value)
|
||||||
me.Task(
|
me.Task(
|
||||||
playerEventBus ! EventBus.Publish(
|
playerEventBus ! EventBus.Publish(
|
||||||
PlayerMovementEvent.PlayerJumped,
|
PlayerMovementEvent.PlayerJumped,
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else me.Task.unit
|
else me.Task.unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,36 +185,35 @@ object GameInputHandler {
|
|||||||
def generateAnalogMovementEvents(
|
def generateAnalogMovementEvents(
|
||||||
inputManager: InputManager,
|
inputManager: InputManager,
|
||||||
playerEventBus: GameEventBus[PlayerEvent]
|
playerEventBus: GameEventBus[PlayerEvent]
|
||||||
) = {
|
) =
|
||||||
// val name = "analogMovementEventsGenerator"
|
// val name = "analogMovementEventsGenerator"
|
||||||
inputManager
|
inputManager
|
||||||
.enumAnalogObservable(PlayerAnalogMovementInput)
|
.enumAnalogObservable(PlayerAnalogMovementInput)
|
||||||
.sample(1.millis)
|
.sample(1.millis)
|
||||||
// .doOnNext(analogEvent =>
|
// .doOnNext(analogEvent =>
|
||||||
// analogEvent.binding match {
|
// analogEvent.binding match {
|
||||||
// case PlayerAnalogMovementInput.TurnRight =>
|
// case PlayerAnalogMovementInput.TurnRight =>
|
||||||
// me.Task(
|
// me.Task(
|
||||||
// playerMovementEventBus ! EventBus.Publish(
|
// playerMovementEventBus ! EventBus.Publish(
|
||||||
// PlayerMovementEvent.PlayerTurnedRight,
|
// PlayerMovementEvent.PlayerTurnedRight,
|
||||||
// name
|
// name
|
||||||
// )
|
// )
|
||||||
// )
|
// )
|
||||||
// case PlayerAnalogMovementInput.TurnLeft =>
|
// case PlayerAnalogMovementInput.TurnLeft =>
|
||||||
// me.Task(
|
// me.Task(
|
||||||
// playerMovementEventBus ! EventBus.Publish(
|
// playerMovementEventBus ! EventBus.Publish(
|
||||||
// PlayerMovementEvent.PlayerTurnedLeft,
|
// PlayerMovementEvent.PlayerTurnedLeft,
|
||||||
// name
|
// name
|
||||||
// )
|
// )
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
// )
|
// )
|
||||||
}
|
|
||||||
|
|
||||||
def generateCameraEvents(
|
def cameraMovementEventsGenerator(
|
||||||
inputManager: InputManager,
|
inputManager: InputManager,
|
||||||
playerEventBus: ActorRef[EventBus.Command[PlayerEvent]]
|
playerEventBus: ActorRef[EventBus.Command[PlayerEvent]]
|
||||||
) = {
|
) = {
|
||||||
val name = "cameraMovementEventsGenerator"
|
val name = methodName
|
||||||
inputManager
|
inputManager
|
||||||
.enumAnalogObservable(PlayerCameraInput)
|
.enumAnalogObservable(PlayerCameraInput)
|
||||||
.sample(1.millis)
|
.sample(1.millis)
|
||||||
|
@ -12,7 +12,7 @@ import wow.doge.mygame.subsystems.movement.RotateDir
|
|||||||
|
|
||||||
trait CanMove[-A] {
|
trait CanMove[-A] {
|
||||||
// def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
// def getDirection(cam: Camera, cardinalDir: CardinalDirection): ImVector3f
|
||||||
def move(inst: A, direction: ImVector3f, speedFactor: Float = 20f): Unit
|
def move(inst: A, direction: ImVector3f, speedFactor: Float): Unit
|
||||||
def location(inst: A): ImVector3f
|
def location(inst: A): ImVector3f
|
||||||
def jump(inst: A): Unit
|
def jump(inst: A): Unit
|
||||||
def stop(inst: A): Unit
|
def stop(inst: A): Unit
|
||||||
@ -25,7 +25,7 @@ object CanMove {
|
|||||||
override def move(
|
override def move(
|
||||||
inst: BetterCharacterControl,
|
inst: BetterCharacterControl,
|
||||||
direction: ImVector3f,
|
direction: ImVector3f,
|
||||||
speedFactor: Float = 20f
|
speedFactor: Float
|
||||||
): Unit = {
|
): Unit = {
|
||||||
val dir = direction.mutable.normalizeLocal()
|
val dir = direction.mutable.normalizeLocal()
|
||||||
inst.setViewDirection(dir.negate())
|
inst.setViewDirection(dir.negate())
|
||||||
@ -61,19 +61,17 @@ object CanMove {
|
|||||||
inst: Spatial,
|
inst: Spatial,
|
||||||
direction: ImVector3f,
|
direction: ImVector3f,
|
||||||
speedFactor: Float = 1f
|
speedFactor: Float = 1f
|
||||||
): Unit = {
|
): Unit =
|
||||||
inst.move(direction.mutable multLocal speedFactor)
|
inst.move(direction.mutable multLocal speedFactor)
|
||||||
}
|
|
||||||
override def location(inst: Spatial) =
|
override def location(inst: Spatial) =
|
||||||
inst.getLocalTranslation().immutable
|
inst.getLocalTranslation().immutable
|
||||||
override def jump(inst: Spatial): Unit =
|
override def jump(inst: Spatial): Unit =
|
||||||
logger.warn("`Jump` is not implemented for type `Spatial`")
|
logger.warn("`Jump` is not implemented for type `Spatial`")
|
||||||
override def rotate(inst: Spatial, rotateDir: RotateDir): Unit = {
|
override def rotate(inst: Spatial, rotateDir: RotateDir): Unit =
|
||||||
rotateDir match {
|
rotateDir match {
|
||||||
case RotateDir.Left => inst.rotate(0, -0.01f, 0)
|
case RotateDir.Left => inst.rotate(0, -0.01f, 0)
|
||||||
case RotateDir.Right => inst.rotate(0, 0.01f, 0)
|
case RotateDir.Right => inst.rotate(0, 0.01f, 0)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
override def stop(inst: Spatial) = { /*not required*/ }
|
override def stop(inst: Spatial) = { /*not required*/ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
package wow.doge.mygame.subsystems.movement
|
package wow.doge.mygame.subsystems.movement
|
||||||
|
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.PostStop
|
||||||
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 com.jme3.math.Vector3f
|
import com.jme3.math.Vector3f
|
||||||
import com.jme3.renderer.Camera
|
import com.jme3.renderer.Camera
|
||||||
|
import com.jme3.scene.Geometry
|
||||||
import com.softwaremill.quicklens._
|
import com.softwaremill.quicklens._
|
||||||
import wow.doge.mygame.game.subsystems.movement.CanMove
|
import wow.doge.mygame.game.subsystems.movement.CanMove
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import wow.doge.mygame.math.ImVector3f
|
import wow.doge.mygame.math.ImVector3f
|
||||||
import wow.doge.mygame.state.CardinalDirection
|
|
||||||
|
final case class CardinalDirection(
|
||||||
|
left: Boolean = false,
|
||||||
|
right: Boolean = false,
|
||||||
|
up: Boolean = false,
|
||||||
|
down: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
sealed trait RotateDir
|
sealed trait RotateDir
|
||||||
object RotateDir {
|
object RotateDir {
|
||||||
@ -19,19 +27,16 @@ object RotateDir {
|
|||||||
|
|
||||||
object ImMovementActor {
|
object ImMovementActor {
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
// final case class Tick(tpf: Float) extends Command
|
case object Tick extends Command
|
||||||
final case object Tick extends Command
|
|
||||||
|
|
||||||
sealed trait Movement extends Command
|
sealed trait Movement extends Command
|
||||||
final case class MovedLeft(pressed: Boolean) extends Movement
|
final case class MoveLeft(pressed: Boolean) extends Movement
|
||||||
final case class MovedUp(pressed: Boolean) extends Movement
|
final case class MoveUp(pressed: Boolean) extends Movement
|
||||||
final case class MovedRight(pressed: Boolean) extends Movement
|
final case class MoveRight(pressed: Boolean) extends Movement
|
||||||
final case class MovedDown(pressed: Boolean) extends Movement
|
final case class MoveDown(pressed: Boolean) extends Movement
|
||||||
final case object Jump extends Movement
|
case object Jump extends Movement
|
||||||
// final case object RotateRight extends Movement
|
|
||||||
// final case object RotateLeft extends Movement
|
|
||||||
|
|
||||||
final class Props(
|
class Props(
|
||||||
val enqueueR: Function1[() => Unit, Unit],
|
val enqueueR: Function1[() => Unit, Unit],
|
||||||
// playerMovementEventBus: ActorRef[
|
// playerMovementEventBus: ActorRef[
|
||||||
// EventBus.Command[PlayerMovementEvent]
|
// EventBus.Command[PlayerMovementEvent]
|
||||||
@ -40,7 +45,7 @@ object ImMovementActor {
|
|||||||
) {
|
) {
|
||||||
def behavior[T: CanMove](movable: T): Behavior[Command] =
|
def behavior[T: CanMove](movable: T): Behavior[Command] =
|
||||||
Behaviors.setup(ctx =>
|
Behaviors.setup(ctx =>
|
||||||
new ImMovementActor(ctx, this, movable).receive(State())
|
new ImMovementActor(ctx, this, movable).receive(State(), new Vector3f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,48 +62,84 @@ class ImMovementActor[T](
|
|||||||
ctx: ActorContext[ImMovementActor.Command],
|
ctx: ActorContext[ImMovementActor.Command],
|
||||||
props: ImMovementActor.Props,
|
props: ImMovementActor.Props,
|
||||||
val movable: T
|
val movable: T
|
||||||
) {
|
)(implicit cm: CanMove[T]) {
|
||||||
import ImMovementActor._
|
import ImMovementActor._
|
||||||
import Methods._
|
|
||||||
|
|
||||||
def receive(
|
def receive(
|
||||||
state: ImMovementActor.State
|
state: ImMovementActor.State,
|
||||||
)(implicit cm: CanMove[T]): Behavior[Command] =
|
walkDirBuf: Vector3f
|
||||||
Behaviors.receiveMessage {
|
): Behavior[Command] =
|
||||||
case m: Movement =>
|
Behaviors
|
||||||
m match {
|
.receiveMessage[Command] {
|
||||||
case MovedLeft(pressed) =>
|
case m: Movement =>
|
||||||
props.enqueueR(() => stopIfNotPressed(pressed, movable))
|
m match {
|
||||||
receive(state = state.modify(_.cardinalDir.left).setTo(pressed))
|
case MoveLeft(pressed) =>
|
||||||
case MovedUp(pressed) =>
|
stopIfNotPressed(pressed)
|
||||||
props.enqueueR(() => stopIfNotPressed(pressed, movable))
|
receive(
|
||||||
receive(state = state.modify(_.cardinalDir.up).setTo(pressed))
|
state = state.modify(_.cardinalDir.left).setTo(pressed),
|
||||||
case MovedRight(pressed) =>
|
walkDirBuf
|
||||||
props.enqueueR(() => stopIfNotPressed(pressed, movable))
|
)
|
||||||
receive(state = state.modify(_.cardinalDir.right).setTo(pressed))
|
case MoveUp(pressed) =>
|
||||||
case MovedDown(pressed) =>
|
stopIfNotPressed(pressed)
|
||||||
props.enqueueR(() => stopIfNotPressed(pressed, movable))
|
receive(
|
||||||
receive(state = state.modify(_.cardinalDir.down).setTo(pressed))
|
state = state.modify(_.cardinalDir.up).setTo(pressed),
|
||||||
case Jump =>
|
walkDirBuf
|
||||||
props.enqueueR(() => cm.jump(movable))
|
)
|
||||||
Behaviors.same
|
case MoveRight(pressed) =>
|
||||||
}
|
stopIfNotPressed(pressed)
|
||||||
|
receive(
|
||||||
|
state = state.modify(_.cardinalDir.right).setTo(pressed),
|
||||||
|
walkDirBuf
|
||||||
|
)
|
||||||
|
case MoveDown(pressed) =>
|
||||||
|
stopIfNotPressed(pressed)
|
||||||
|
receive(
|
||||||
|
state = state.modify(_.cardinalDir.down).setTo(pressed),
|
||||||
|
walkDirBuf
|
||||||
|
)
|
||||||
|
case Jump =>
|
||||||
|
cm.jump(movable)
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
|
||||||
case Tick =>
|
case Tick =>
|
||||||
val walkDir =
|
val camDir =
|
||||||
getDirection2(state.cardinalDir, ctx.log.traceP)
|
props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
|
||||||
// if (walkDir != ImVector3f.ZERO) {
|
val camLeft =
|
||||||
val tmp = walkDir * 25f * (1f / 144)
|
props.camera.getLeft().clone().normalizeLocal.multLocal(0.4f)
|
||||||
props.enqueueR { () =>
|
val dir = state.cardinalDir
|
||||||
cm.move(movable, tmp)
|
walkDirBuf.set(0, 0, 0)
|
||||||
}
|
if (dir.up) {
|
||||||
// }
|
ctx.log.traceP("up")
|
||||||
Behaviors.same
|
walkDirBuf += camDir
|
||||||
}
|
}
|
||||||
|
if (dir.left) {
|
||||||
|
ctx.log.traceP("left")
|
||||||
|
walkDirBuf += camLeft
|
||||||
|
}
|
||||||
|
if (dir.right) {
|
||||||
|
ctx.log.traceP("right")
|
||||||
|
walkDirBuf += -camLeft
|
||||||
|
}
|
||||||
|
if (dir.down) {
|
||||||
|
ctx.log.traceP("down")
|
||||||
|
walkDirBuf += -camDir
|
||||||
|
}
|
||||||
|
walkDirBuf *= 25f *= (1f / 144)
|
||||||
|
cm.move(movable, walkDirBuf.immutable, 20f)
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
ctx.log.debugP("Stopped")
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
|
||||||
|
def stopIfNotPressed(pressed: Boolean) = if (!pressed) cm.stop(movable)
|
||||||
|
|
||||||
def getDirection2(
|
def getDirection2(
|
||||||
cardinalDir: CardinalDirection,
|
cardinalDir: CardinalDirection
|
||||||
debug: sourcecode.Text[String] => sourcecode.Text[String]
|
// debug: sourcecode.Text[String] => sourcecode.Text[String]
|
||||||
) = {
|
) = {
|
||||||
val camDir =
|
val camDir =
|
||||||
props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
|
props.camera.getDirection().clone().normalizeLocal.multLocal(0.6f)
|
||||||
@ -107,19 +148,19 @@ class ImMovementActor[T](
|
|||||||
val walkDir = {
|
val walkDir = {
|
||||||
val mutWalkDir = new Vector3f()
|
val mutWalkDir = new Vector3f()
|
||||||
if (dir.up) {
|
if (dir.up) {
|
||||||
debug("up")
|
ctx.log.traceP("up")
|
||||||
mutWalkDir += camDir
|
mutWalkDir += camDir
|
||||||
}
|
}
|
||||||
if (dir.left) {
|
if (dir.left) {
|
||||||
debug("left")
|
ctx.log.traceP("left")
|
||||||
mutWalkDir += camLeft
|
mutWalkDir += camLeft
|
||||||
}
|
}
|
||||||
if (dir.right) {
|
if (dir.right) {
|
||||||
debug("right")
|
ctx.log.traceP("right")
|
||||||
mutWalkDir += -camLeft
|
mutWalkDir += -camLeft
|
||||||
}
|
}
|
||||||
if (dir.down) {
|
if (dir.down) {
|
||||||
debug("down")
|
ctx.log.traceP("down")
|
||||||
mutWalkDir += -camDir
|
mutWalkDir += -camDir
|
||||||
}
|
}
|
||||||
mutWalkDir.immutable
|
mutWalkDir.immutable
|
||||||
@ -127,50 +168,111 @@ class ImMovementActor[T](
|
|||||||
walkDir
|
walkDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
object Methods {
|
|
||||||
def getDirection(cardinalDir: CardinalDirection, trace: String => Unit) = {
|
|
||||||
val zero = ImVector3f.Zero
|
|
||||||
val dir = cardinalDir
|
|
||||||
val walkDir = {
|
|
||||||
val mutWalkDir = new Vector3f()
|
|
||||||
if (dir.left) {
|
|
||||||
trace("left")
|
|
||||||
mutWalkDir += zero +=: new Vector3f(-1, 0, 0)
|
|
||||||
}
|
|
||||||
if (dir.right) {
|
|
||||||
trace("right")
|
|
||||||
mutWalkDir += zero +=: new Vector3f(1, 0, 0)
|
|
||||||
}
|
|
||||||
if (dir.up) {
|
|
||||||
trace("up")
|
|
||||||
mutWalkDir += zero +=: new Vector3f(0, 0, -1)
|
|
||||||
}
|
|
||||||
if (dir.down) {
|
|
||||||
trace("down")
|
|
||||||
mutWalkDir += zero +=: new Vector3f(0, 0, 1)
|
|
||||||
}
|
|
||||||
mutWalkDir.immutable
|
|
||||||
}
|
|
||||||
walkDir
|
|
||||||
}
|
|
||||||
|
|
||||||
def stopIfNotPressed[T](pressed: Boolean, movable: T)(implicit
|
// old/unused
|
||||||
cm: CanMove[T]
|
object MovementActor {
|
||||||
) =
|
sealed trait Command
|
||||||
if (!pressed) cm.stop(movable)
|
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
|
||||||
|
|
||||||
|
class Props(val app: com.jme3.app.Application, val 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))
|
||||||
|
}
|
||||||
|
|
||||||
// props.app
|
case Tick =>
|
||||||
// .enqueueScala { () =>
|
val camDir =
|
||||||
// 1
|
props.app.getCamera.getDirection().clone().multLocal(0.6f)
|
||||||
// }
|
val camLeft = props.app.getCamera.getLeft().clone().multLocal(0.4f)
|
||||||
// .map(println)(scala.concurrent.ExecutionContext.global)
|
val walkDir = state.walkDirection.set(0, 0, 0)
|
||||||
// monix.eval.Task
|
// val walkDir = new Vector3f
|
||||||
// .fromFuture(
|
val dir = state.cardinalDir
|
||||||
// props.app
|
if (dir.up) {
|
||||||
// .enqueueScala { () =>
|
ctx.log.debugP("up")
|
||||||
// 1
|
// ctx.log.debugP(Thread.currentThread().getName())
|
||||||
// }
|
// walkDir.addLocal(0, 0, -1)
|
||||||
// )
|
walkDir += camDir
|
||||||
// .flatMap(i => monix.eval.Task(println(i)))
|
}
|
||||||
// .runToFuture(monix.execution.Scheduler.Implicits.global)
|
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))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,7 +4,10 @@ import scala.jdk.CollectionConverters._
|
|||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.Props
|
||||||
import akka.actor.typed.Scheduler
|
import akka.actor.typed.Scheduler
|
||||||
|
import akka.actor.typed.scaladsl.ActorContext
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import com.jayfella.jme.jfx.JavaFxUI
|
import com.jayfella.jme.jfx.JavaFxUI
|
||||||
import com.jme3.app.Application
|
import com.jme3.app.Application
|
||||||
@ -14,10 +17,12 @@ import com.jme3.app.state.AppStateManager
|
|||||||
import com.jme3.asset.AssetLocator
|
import com.jme3.asset.AssetLocator
|
||||||
import com.jme3.asset.AssetManager
|
import com.jme3.asset.AssetManager
|
||||||
import com.jme3.bullet.BulletAppState
|
import com.jme3.bullet.BulletAppState
|
||||||
import com.jme3.bullet.PhysicsSpace
|
|
||||||
import com.jme3.bullet.PhysicsTickListener
|
import com.jme3.bullet.PhysicsTickListener
|
||||||
import com.jme3.bullet.collision.PhysicsCollisionEvent
|
|
||||||
import com.jme3.bullet.collision.PhysicsCollisionListener
|
import com.jme3.bullet.collision.PhysicsCollisionListener
|
||||||
|
import com.jme3.bullet.collision.PhysicsCollisionObject
|
||||||
|
import com.jme3.bullet.collision.{
|
||||||
|
PhysicsCollisionEvent => jmePhysicsCollisionEvent
|
||||||
|
}
|
||||||
import com.jme3.bullet.control.BetterCharacterControl
|
import com.jme3.bullet.control.BetterCharacterControl
|
||||||
import com.jme3.input.Action
|
import com.jme3.input.Action
|
||||||
import com.jme3.input.InputManager
|
import com.jme3.input.InputManager
|
||||||
@ -35,14 +40,17 @@ import com.jme3.scene.SceneGraphVisitor
|
|||||||
import com.jme3.scene.Spatial
|
import com.jme3.scene.Spatial
|
||||||
import com.jme3.scene.control.CameraControl.ControlDirection
|
import com.jme3.scene.control.CameraControl.ControlDirection
|
||||||
import com.jme3.scene.control.Control
|
import com.jme3.scene.control.Control
|
||||||
|
import com.jme3.{bullet => jmeb}
|
||||||
import com.simsilica.es.EntityComponent
|
import com.simsilica.es.EntityComponent
|
||||||
import com.simsilica.es.EntityData
|
import com.simsilica.es.EntityData
|
||||||
import com.simsilica.es.EntityId
|
import com.simsilica.es.EntityId
|
||||||
import enumeratum._
|
import enumeratum._
|
||||||
import io.odin.meta.Position
|
import io.odin.meta.Position
|
||||||
import io.odin.meta.Render
|
import io.odin.meta.Render
|
||||||
|
import monix.bio.IO
|
||||||
import monix.bio.Task
|
import monix.bio.Task
|
||||||
import monix.bio.UIO
|
import monix.bio.UIO
|
||||||
|
import monix.eval.Coeval
|
||||||
import monix.execution.Ack
|
import monix.execution.Ack
|
||||||
import monix.execution.Ack.Continue
|
import monix.execution.Ack.Continue
|
||||||
import monix.execution.Ack.Stop
|
import monix.execution.Ack.Stop
|
||||||
@ -54,6 +62,7 @@ import monix.reactive.observers.Subscriber
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import wow.doge.mygame.math.ImVector3f
|
import wow.doge.mygame.math.ImVector3f
|
||||||
import wow.doge.mygame.state.MyBaseState
|
import wow.doge.mygame.state.MyBaseState
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||||
|
|
||||||
final case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
|
final case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
|
||||||
final case class EnumActionEvent[T <: EnumEntry](
|
final case class EnumActionEvent[T <: EnumEntry](
|
||||||
@ -68,8 +77,15 @@ final case class EnumAnalogEvent[T <: EnumEntry](
|
|||||||
value: Float,
|
value: Float,
|
||||||
tpf: Float
|
tpf: Float
|
||||||
)
|
)
|
||||||
final class PrePhysicsTickEvent(val space: PhysicsSpace) extends AnyVal
|
final case class PrePhysicsTickEvent(space: PhysicsSpace)
|
||||||
final class PhysicsTickEvent(val space: PhysicsSpace) extends AnyVal
|
final case class PhysicsTickEvent(space: PhysicsSpace)
|
||||||
|
final case class CollisionEvent(
|
||||||
|
nodeA: Option[Spatial],
|
||||||
|
nodeB: Option[Spatial],
|
||||||
|
objectA: PhysicsCollisionObject,
|
||||||
|
objectB: PhysicsCollisionObject,
|
||||||
|
appliedImpulse: Function0[Float]
|
||||||
|
)
|
||||||
|
|
||||||
package object implicits {
|
package object implicits {
|
||||||
type PrePhysicsTickEvent = PhysicsTickEvent
|
type PrePhysicsTickEvent = PhysicsTickEvent
|
||||||
@ -112,10 +128,9 @@ package object implicits {
|
|||||||
|
|
||||||
override def init(): Unit = {}
|
override def init(): Unit = {}
|
||||||
|
|
||||||
override def update(tpf: Float) = {
|
override def update(tpf: Float) =
|
||||||
if (sub.onNext(tpf) == Ack.Stop)
|
if (sub.onNext(tpf) == Ack.Stop)
|
||||||
c.cancel()
|
c.cancel()
|
||||||
}
|
|
||||||
|
|
||||||
override def stop(): Unit = {}
|
override def stop(): Unit = {}
|
||||||
|
|
||||||
@ -133,13 +148,11 @@ package object implicits {
|
|||||||
def registerLocator(
|
def registerLocator(
|
||||||
assetPath: os.RelPath,
|
assetPath: os.RelPath,
|
||||||
locator: Class[_ <: AssetLocator]
|
locator: Class[_ <: AssetLocator]
|
||||||
): Unit = {
|
): Unit =
|
||||||
am.registerLocator(assetPath.toString(), locator)
|
am.registerLocator(assetPath.toString(), locator)
|
||||||
}
|
|
||||||
|
|
||||||
def loadModel(assetPath: os.RelPath): Spatial = {
|
def loadModel(assetPath: os.RelPath): Spatial =
|
||||||
am.loadModel(assetPath.toString())
|
am.loadModel(assetPath.toString())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit final class BulletAppStateExt(private val bas: BulletAppState)
|
implicit final class BulletAppStateExt(private val bas: BulletAppState)
|
||||||
@ -251,7 +264,7 @@ package object implicits {
|
|||||||
}
|
}
|
||||||
// case _ => loop(subscriber, tail)
|
// case _ => loop(subscriber, tail)
|
||||||
}
|
}
|
||||||
case LazyList() => Task.unit
|
case LazyList() => Task(subscriber.onComplete())
|
||||||
}
|
}
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||||
@ -297,7 +310,7 @@ package object implicits {
|
|||||||
Task(subscriber.onComplete())
|
Task(subscriber.onComplete())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case LazyList() => Task.unit
|
case LazyList() => Task(subscriber.onComplete())
|
||||||
}
|
}
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
Observable.create(OverflowStrategy.Unbounded) { sub =>
|
||||||
@ -372,9 +385,8 @@ package object implicits {
|
|||||||
*/
|
*/
|
||||||
def askL[Res](
|
def askL[Res](
|
||||||
replyTo: ActorRef[Res] => Req
|
replyTo: ActorRef[Res] => Req
|
||||||
)(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] = {
|
)(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] =
|
||||||
Task.deferFuture(a.ask(replyTo)(timeout, scheduler))
|
Task.deferFuture(a.ask(replyTo)(timeout, scheduler))
|
||||||
}
|
|
||||||
def ??[Res](
|
def ??[Res](
|
||||||
replyTo: ActorRef[Res] => Req
|
replyTo: ActorRef[Res] => Req
|
||||||
)(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] =
|
)(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] =
|
||||||
@ -481,8 +493,7 @@ package object implicits {
|
|||||||
* @see [[ActionEvent]]
|
* @see [[ActionEvent]]
|
||||||
* @see [[enumObservableAction]]
|
* @see [[enumObservableAction]]
|
||||||
*/
|
*/
|
||||||
def observableAction(mappingNames: String*): Observable[ActionEvent] = {
|
def observableAction(mappingNames: String*): Observable[ActionEvent] =
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val al = new ActionListener {
|
val al = new ActionListener {
|
||||||
@ -490,14 +501,13 @@ package object implicits {
|
|||||||
binding: String,
|
binding: String,
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
tpf: Float
|
tpf: Float
|
||||||
): Unit = {
|
): Unit =
|
||||||
if (
|
if (
|
||||||
sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
|
sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
|
||||||
) {
|
) {
|
||||||
sub.onComplete()
|
sub.onComplete()
|
||||||
c.cancel()
|
c.cancel()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputManager.addListener(al, mappingNames: _*)
|
inputManager.addListener(al, mappingNames: _*)
|
||||||
@ -505,7 +515,6 @@ package object implicits {
|
|||||||
c := Cancelable(() => inputManager.removeListener(al))
|
c := Cancelable(() => inputManager.removeListener(al))
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -530,8 +539,7 @@ package object implicits {
|
|||||||
*/
|
*/
|
||||||
def enumObservableAction[T <: EnumEntry](
|
def enumObservableAction[T <: EnumEntry](
|
||||||
mappingEnum: Enum[T]
|
mappingEnum: Enum[T]
|
||||||
): Observable[EnumActionEvent[T]] = {
|
): Observable[EnumActionEvent[T]] =
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val entryNames = mappingEnum.values.map(_.entryName)
|
val entryNames = mappingEnum.values.map(_.entryName)
|
||||||
@ -540,14 +548,13 @@ package object implicits {
|
|||||||
binding: String,
|
binding: String,
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
tpf: Float
|
tpf: Float
|
||||||
): Unit = {
|
): Unit =
|
||||||
mappingEnum.withNameOption(binding).foreach { b =>
|
mappingEnum.withNameOption(binding).foreach { b =>
|
||||||
if (sub.onNext(EnumActionEvent(b, value, tpf)) == Ack.Stop) {
|
if (sub.onNext(EnumActionEvent(b, value, tpf)) == Ack.Stop) {
|
||||||
sub.onComplete()
|
sub.onComplete()
|
||||||
c.cancel()
|
c.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputManager.addListener(al, entryNames: _*)
|
inputManager.addListener(al, entryNames: _*)
|
||||||
@ -555,12 +562,10 @@ package object implicits {
|
|||||||
c := Cancelable(() => inputManager.removeListener(al))
|
c := Cancelable(() => inputManager.removeListener(al))
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def enumEntryObservableAction[T <: EnumEntry](
|
def enumEntryObservableAction[T <: EnumEntry](
|
||||||
mappingEnumEntry: T
|
mappingEnumEntry: T
|
||||||
): Observable[EnumActionEvent[T]] = {
|
): Observable[EnumActionEvent[T]] =
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
Observable.create(OverflowStrategy.DropOld(10)) { sub =>
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val al = new ActionListener {
|
val al = new ActionListener {
|
||||||
@ -568,7 +573,7 @@ package object implicits {
|
|||||||
binding: String,
|
binding: String,
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
tpf: Float
|
tpf: Float
|
||||||
): Unit = {
|
): Unit =
|
||||||
if (
|
if (
|
||||||
sub.onNext(
|
sub.onNext(
|
||||||
EnumActionEvent(mappingEnumEntry, value, tpf)
|
EnumActionEvent(mappingEnumEntry, value, tpf)
|
||||||
@ -577,7 +582,6 @@ package object implicits {
|
|||||||
sub.onComplete()
|
sub.onComplete()
|
||||||
c.cancel()
|
c.cancel()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputManager.addListener(al, mappingEnumEntry.entryName)
|
inputManager.addListener(al, mappingEnumEntry.entryName)
|
||||||
@ -585,10 +589,8 @@ package object implicits {
|
|||||||
c := Cancelable(() => inputManager.removeListener(al))
|
c := Cancelable(() => inputManager.removeListener(al))
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def analogObservable(mappingNames: String*): Observable[AnalogEvent] = {
|
|
||||||
|
|
||||||
|
def analogObservable(mappingNames: String*): Observable[AnalogEvent] =
|
||||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val al = new AnalogListener {
|
val al = new AnalogListener {
|
||||||
@ -596,14 +598,13 @@ package object implicits {
|
|||||||
binding: String,
|
binding: String,
|
||||||
value: Float,
|
value: Float,
|
||||||
tpf: Float
|
tpf: Float
|
||||||
): Unit = {
|
): Unit =
|
||||||
if (
|
if (
|
||||||
sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
|
sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
|
||||||
) {
|
) {
|
||||||
sub.onComplete()
|
sub.onComplete()
|
||||||
c.cancel()
|
c.cancel()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputManager.addListener(al, mappingNames: _*)
|
inputManager.addListener(al, mappingNames: _*)
|
||||||
@ -611,12 +612,10 @@ package object implicits {
|
|||||||
c := Cancelable(() => inputManager.removeListener(al))
|
c := Cancelable(() => inputManager.removeListener(al))
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def enumAnalogObservable[T <: EnumEntry](
|
def enumAnalogObservable[T <: EnumEntry](
|
||||||
mappingEnum: Enum[T]
|
mappingEnum: Enum[T]
|
||||||
): Observable[EnumAnalogEvent[T]] = {
|
): Observable[EnumAnalogEvent[T]] =
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val entryNames = mappingEnum.values.map(_.entryName)
|
val entryNames = mappingEnum.values.map(_.entryName)
|
||||||
@ -625,14 +624,13 @@ package object implicits {
|
|||||||
binding: String,
|
binding: String,
|
||||||
value: Float,
|
value: Float,
|
||||||
tpf: Float
|
tpf: Float
|
||||||
): Unit = {
|
): Unit =
|
||||||
mappingEnum.withNameOption(binding).foreach { b =>
|
mappingEnum.withNameOption(binding).foreach { b =>
|
||||||
if (sub.onNext(EnumAnalogEvent(b, value, tpf)) == Ack.Stop) {
|
if (sub.onNext(EnumAnalogEvent(b, value, tpf)) == Ack.Stop) {
|
||||||
sub.onComplete()
|
sub.onComplete()
|
||||||
c.cancel()
|
c.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputManager.addListener(al, entryNames: _*)
|
inputManager.addListener(al, entryNames: _*)
|
||||||
@ -640,72 +638,82 @@ package object implicits {
|
|||||||
c := Cancelable(() => inputManager.removeListener(al))
|
c := Cancelable(() => inputManager.removeListener(al))
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit final class PhysicsSpaceExt(private val space: PhysicsSpace)
|
implicit final class PhysicsSpaceExt(private val space: jmeb.PhysicsSpace)
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
|
|
||||||
def collisionObservable(): Observable[PhysicsCollisionEvent] = {
|
def collisionObservable(): Observable[CollisionEvent] =
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val cl = new PhysicsCollisionListener {
|
val cl = new PhysicsCollisionListener {
|
||||||
override def collision(event: PhysicsCollisionEvent): Unit = {
|
override def collision(event: jmePhysicsCollisionEvent): Unit =
|
||||||
|
if (
|
||||||
|
sub.onNext(
|
||||||
|
CollisionEvent(
|
||||||
|
Option(event.getNodeA),
|
||||||
|
Option(event.getNodeB),
|
||||||
|
event.getObjectA,
|
||||||
|
event.getObjectB,
|
||||||
|
event.getAppliedImpulse _
|
||||||
|
)
|
||||||
|
) == Ack.Stop
|
||||||
|
) c.cancel()
|
||||||
|
|
||||||
if (sub.onNext(event) == Ack.Stop) {
|
|
||||||
sub.onComplete()
|
|
||||||
c.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
space.addCollisionListener(cl)
|
space.addCollisionListener(cl)
|
||||||
|
|
||||||
c := Cancelable(() => space.removeCollisionListener(cl))
|
c := Cancelable { () =>
|
||||||
c
|
pprint.log("stopped")
|
||||||
}
|
space.removeCollisionListener(cl)
|
||||||
}
|
|
||||||
|
|
||||||
def prePhysicsTickObservable(): Observable[PrePhysicsTickEvent] = {
|
|
||||||
|
|
||||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
|
||||||
val c = SingleAssignCancelable()
|
|
||||||
val cl = new PhysicsTickListener {
|
|
||||||
|
|
||||||
override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
|
|
||||||
val event = new PrePhysicsTickEvent(space)
|
|
||||||
if (sub.onNext(event) == Ack.Stop) {
|
|
||||||
sub.onComplete()
|
|
||||||
c.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
space.addTickListener(cl)
|
|
||||||
|
|
||||||
c := Cancelable(() => space.removeTickListener(cl))
|
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
def physicsTickObservable(): Observable[PhysicsTickEvent] = {
|
|
||||||
|
|
||||||
|
def prePhysicsTickObservable(): Observable[PrePhysicsTickEvent] =
|
||||||
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val cl = new PhysicsTickListener {
|
val cl = new PhysicsTickListener {
|
||||||
|
|
||||||
override def prePhysicsTick(
|
override def prePhysicsTick(
|
||||||
space: PhysicsSpace,
|
space: jmeb.PhysicsSpace,
|
||||||
|
tpf: Float
|
||||||
|
): Unit = {
|
||||||
|
val event = new PrePhysicsTickEvent(new PhysicsSpace(space))
|
||||||
|
if (sub.onNext(event) == Ack.Stop) {
|
||||||
|
sub.onComplete()
|
||||||
|
c.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def physicsTick(
|
||||||
|
space: jmeb.PhysicsSpace,
|
||||||
tpf: Float
|
tpf: Float
|
||||||
): Unit = {}
|
): Unit = {}
|
||||||
|
|
||||||
override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
|
}
|
||||||
val event = new PhysicsTickEvent(space)
|
|
||||||
|
space.addTickListener(cl)
|
||||||
|
|
||||||
|
c := Cancelable(() => space.removeTickListener(cl))
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
def physicsTickObservable(): Observable[PhysicsTickEvent] =
|
||||||
|
Observable.create(OverflowStrategy.DropOld(50)) { sub =>
|
||||||
|
val c = SingleAssignCancelable()
|
||||||
|
val cl = new PhysicsTickListener {
|
||||||
|
|
||||||
|
override def prePhysicsTick(
|
||||||
|
space: jmeb.PhysicsSpace,
|
||||||
|
tpf: Float
|
||||||
|
): Unit = {}
|
||||||
|
|
||||||
|
override def physicsTick(
|
||||||
|
space: jmeb.PhysicsSpace,
|
||||||
|
tpf: Float
|
||||||
|
): Unit = {
|
||||||
|
val event = new PhysicsTickEvent(new PhysicsSpace(space))
|
||||||
if (sub.onNext(event) == Ack.Stop) {
|
if (sub.onNext(event) == Ack.Stop) {
|
||||||
sub.onComplete()
|
sub.onComplete()
|
||||||
c.cancel()
|
c.cancel()
|
||||||
@ -719,7 +727,6 @@ package object implicits {
|
|||||||
c := Cancelable(() => space.removeTickListener(cl))
|
c := Cancelable(() => space.removeTickListener(cl))
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//TODO Consider creating a typeclass for this
|
//TODO Consider creating a typeclass for this
|
||||||
def +=(anyObject: Any) = space.add(anyObject)
|
def +=(anyObject: Any) = space.add(anyObject)
|
||||||
@ -757,6 +764,7 @@ package object implicits {
|
|||||||
def +=:(that: ImVector3f) = v += that
|
def +=:(that: ImVector3f) = v += that
|
||||||
def *=(that: Vector3f) = v.multLocal(that)
|
def *=(that: Vector3f) = v.multLocal(that)
|
||||||
def *=(that: ImVector3f) = v.multLocal(that.x, that.y, that.z)
|
def *=(that: ImVector3f) = v.multLocal(that.x, that.y, that.z)
|
||||||
|
def *=(f: Float) = v.multLocal(f, f, f)
|
||||||
def *=:(that: ImVector3f) = v *= that
|
def *=:(that: ImVector3f) = v *= that
|
||||||
def -=(that: Vector3f) = v.subtractLocal(that)
|
def -=(that: Vector3f) = v.subtractLocal(that)
|
||||||
def -=(that: ImVector3f) = v.subtractLocal(that.x, that.y, that.z)
|
def -=(that: ImVector3f) = v.subtractLocal(that.x, that.y, that.z)
|
||||||
@ -766,6 +774,7 @@ package object implicits {
|
|||||||
def /=:(that: ImVector3f) = v *= that
|
def /=:(that: ImVector3f) = v *= that
|
||||||
def unary_- = v.negateLocal()
|
def unary_- = v.negateLocal()
|
||||||
def immutable = ImVector3f(v.x, v.y, v.z)
|
def immutable = ImVector3f(v.x, v.y, v.z)
|
||||||
|
// def transformImmutable(f: Vector3f => Vector3f) = f(v).immutable
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit final class ImVector3fExt(private val v: ImVector3f) extends AnyVal {
|
implicit final class ImVector3fExt(private val v: ImVector3f) extends AnyVal {
|
||||||
@ -858,7 +867,7 @@ package object implicits {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit class odinLoggerExt(private val logger: io.odin.Logger[Task])
|
implicit class OdinLoggerExt(private val logger: io.odin.Logger[Task])
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
def debugU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
def debugU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
||||||
logger.debug(msg).hideErrors
|
logger.debug(msg).hideErrors
|
||||||
@ -871,4 +880,28 @@ package object implicits {
|
|||||||
def errorU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
def errorU[M](msg: => M)(implicit render: Render[M], position: Position) =
|
||||||
logger.error(msg).hideErrors
|
logger.error(msg).hideErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implicit class TypedActorContextExt[T](private val ctx: ActorContext[T])
|
||||||
|
extends AnyVal {
|
||||||
|
def spawnN[U](behavior: Behavior[U], props: Props = Props.empty)(implicit
|
||||||
|
name: sourcecode.Name
|
||||||
|
): ActorRef[U] =
|
||||||
|
ctx.spawn(behavior, name.value, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class MonixEvalTaskExt[T](private val task: monix.eval.Task[T])
|
||||||
|
extends AnyVal {
|
||||||
|
def toIO = IO.deferAction(implicit s => IO.from(task))
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class MonixBioTaskExt[T](private val task: monix.bio.Task[T])
|
||||||
|
extends AnyVal {
|
||||||
|
def toTask =
|
||||||
|
monix.eval.Task.deferAction(implicit s => monix.eval.Task.from(task))
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit class CoevalEitherExt[L, R](private val coeval: Coeval[Either[L, R]])
|
||||||
|
extends AnyVal {
|
||||||
|
def toIO = coeval.to[Task].hideErrors.rethrow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,9 @@ object ImVector3f {
|
|||||||
val UnitY = ImVector3f(0, 1, 0)
|
val UnitY = ImVector3f(0, 1, 0)
|
||||||
val UnitZ = ImVector3f(0, 0, 1)
|
val UnitZ = ImVector3f(0, 0, 1)
|
||||||
val Unit = ImVector3f(1, 1, 1)
|
val Unit = ImVector3f(1, 1, 1)
|
||||||
|
val Max = ImVector3f(Float.MaxValue, Float.MaxValue, Float.MaxValue)
|
||||||
|
val Min = ImVector3f(Float.MinValue, Float.MinValue, Float.MinValue)
|
||||||
//format: on
|
//format: on
|
||||||
val Max = ImVector3f(Float.MaxValue, Float.MaxValue, Float.MaxValue)
|
|
||||||
val Min = ImVector3f(Float.MinValue, Float.MinValue, Float.MinValue)
|
|
||||||
|
|
||||||
implicit val show = new Show[ImVector3f] {
|
implicit val show = new Show[ImVector3f] {
|
||||||
def format(f: Float) = f.formatted("%.2f")
|
def format(f: Float) = f.formatted("%.2f")
|
||||||
@ -39,6 +39,6 @@ object ImVector3f {
|
|||||||
sqrt(total)
|
sqrt(total)
|
||||||
}
|
}
|
||||||
def manhattanDst(v1: ImVector3f, v2: ImVector3f) =
|
def manhattanDst(v1: ImVector3f, v2: ImVector3f) =
|
||||||
abs(v1.x - v2.x) + abs(v1.y - v2.y) + abs(v1.z - v2.z)
|
if (v1 === v2) 0 else abs(v1.x - v2.x) + abs(v1.y - v2.y) + abs(v1.z - v2.z)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package wow.doge.mygame.subsystems.events
|
package wow.doge.mygame.subsystems.events
|
||||||
|
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
import scala.util.Random
|
|
||||||
|
|
||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
|
import akka.actor.typed.PostStop
|
||||||
import akka.actor.typed.Scheduler
|
import akka.actor.typed.Scheduler
|
||||||
import akka.actor.typed.SpawnProtocol
|
import akka.actor.typed.SpawnProtocol
|
||||||
import akka.actor.typed.scaladsl.AskPattern._
|
import akka.actor.typed.scaladsl.AskPattern._
|
||||||
@ -59,42 +59,7 @@ object EventBus {
|
|||||||
new EventBus().eventStreamBehavior(eventStream)
|
new EventBus().eventStreamBehavior(eventStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
def observable[E](eventBus: ActorRef[EventBus.Command[E]])(implicit
|
def observable[A, B <: A](eventBus: ActorRef[EventBus.Command[A]])(implicit
|
||||||
timeout: Timeout,
|
|
||||||
scheduler: Scheduler,
|
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
|
||||||
ct: ClassTag[E]
|
|
||||||
) =
|
|
||||||
Observable.create[E](OverflowStrategy.DropOld(50)) { sub =>
|
|
||||||
implicit val s = sub.scheduler
|
|
||||||
val c = SingleAssignCancelable()
|
|
||||||
val behavior = Behaviors.receive[E] { (ctx, msg) =>
|
|
||||||
if (sub.onNext(msg) == Ack.Stop) {
|
|
||||||
c.cancel()
|
|
||||||
Behaviors.stopped
|
|
||||||
} else Behaviors.same
|
|
||||||
|
|
||||||
}
|
|
||||||
val actor =
|
|
||||||
AkkaUtils
|
|
||||||
.spawnActorL(
|
|
||||||
behavior,
|
|
||||||
s"eventBusObservable-${ct.toString}-${Random.nextLong()}"
|
|
||||||
)
|
|
||||||
.mapError(err => new Exception(err.toString))
|
|
||||||
.tapError {
|
|
||||||
case ex => UIO(sub.onError(ex))
|
|
||||||
}
|
|
||||||
|
|
||||||
(for {
|
|
||||||
a <- actor
|
|
||||||
_ <- eventBus !! Subscribe(a)
|
|
||||||
_ <- UIO(c := Cancelable(() => eventBus ! Unsubscribe(a)))
|
|
||||||
} yield ()).runToFuture
|
|
||||||
c
|
|
||||||
}
|
|
||||||
|
|
||||||
def observable2[A, B <: A](eventBus: ActorRef[EventBus.Command[A]])(implicit
|
|
||||||
timeout: Timeout,
|
timeout: Timeout,
|
||||||
scheduler: Scheduler,
|
scheduler: Scheduler,
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
@ -104,18 +69,25 @@ object EventBus {
|
|||||||
Observable.create[B](OverflowStrategy.DropOld(50)) { sub =>
|
Observable.create[B](OverflowStrategy.DropOld(50)) { sub =>
|
||||||
implicit val s = sub.scheduler
|
implicit val s = sub.scheduler
|
||||||
val c = SingleAssignCancelable()
|
val c = SingleAssignCancelable()
|
||||||
val behavior = Behaviors.receive[B] { (ctx, msg) =>
|
val behavior = Behaviors
|
||||||
if (sub.onNext(msg) == Ack.Stop) {
|
.receive[B] { (ctx, msg) =>
|
||||||
c.cancel()
|
ctx.log.traceP(s"Emitted $msg")
|
||||||
Behaviors.stopped
|
if (sub.onNext(msg) == Ack.Stop) {
|
||||||
} else Behaviors.same
|
c.cancel()
|
||||||
|
Behaviors.stopped
|
||||||
}
|
} else Behaviors.same
|
||||||
|
}
|
||||||
|
.receiveSignal {
|
||||||
|
case (_, PostStop) =>
|
||||||
|
sub.onComplete()
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
val actor =
|
val actor =
|
||||||
AkkaUtils
|
AkkaUtils
|
||||||
.spawnActorL(
|
.spawnActorL(
|
||||||
behavior,
|
behavior,
|
||||||
s"eventBusObservable-${ct.toString}-${math.abs(Random.nextLong())}"
|
actorName =
|
||||||
|
Some(s"eventBusObservable-${ct.toString.split("""\.""").last}")
|
||||||
)
|
)
|
||||||
.mapError(err => new Throwable(err.toString))
|
.mapError(err => new Throwable(err.toString))
|
||||||
.tapError {
|
.tapError {
|
||||||
@ -155,7 +127,7 @@ class EventBus[A] {
|
|||||||
eventStream.unsubscribe(subscriber.toClassic)
|
eventStream.unsubscribe(subscriber.toClassic)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case s @ ObservableSubscription(replyTo) =>
|
case s @ ObservableSubscription(replyTo) =>
|
||||||
val obs = EventBus.observable2(
|
val obs = EventBus.observable(
|
||||||
ctx.self
|
ctx.self
|
||||||
)(timeout, scheduler, spawnProtocol, ct, s.ct)
|
)(timeout, scheduler, spawnProtocol, ct, s.ct)
|
||||||
replyTo ! obs
|
replyTo ! obs
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package wow.doge.mygame.subsystems.events
|
package wow.doge.mygame.subsystems.events
|
||||||
|
|
||||||
sealed trait Event
|
sealed trait Event
|
||||||
final case object BulletFired extends Event
|
case object BulletFired extends Event
|
||||||
// type BulletFired = BulletFired.type
|
// type BulletFired = BulletFired.type
|
||||||
final case class EventWithData(data: Int) extends Event
|
final case class EventWithData(data: Int) extends Event
|
||||||
|
|
||||||
sealed trait TickEvent extends Event
|
sealed trait TickEvent extends Event
|
||||||
object TickEvent {
|
object TickEvent {
|
||||||
final case object RenderTick extends TickEvent
|
case object RenderTick extends TickEvent
|
||||||
final case object PhysicsTick extends TickEvent
|
case object PhysicsTick extends TickEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait EntityMovementEvent extends Event
|
sealed trait EntityMovementEvent extends Event
|
||||||
@ -22,3 +22,9 @@ object EntityMovementEvent {
|
|||||||
final case class MovedDown(name: String, pressed: Boolean)
|
final case class MovedDown(name: String, pressed: Boolean)
|
||||||
extends EntityMovementEvent
|
extends EntityMovementEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed trait StatsEvent extends Event
|
||||||
|
object StatsEvent {
|
||||||
|
case class DamageEvent(hitBy: String, victimName: String, amount: Int)
|
||||||
|
extends StatsEvent
|
||||||
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package wow.doge.mygame.subsystems.events
|
package wow.doge.mygame.subsystems.events
|
||||||
|
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
@ -17,6 +15,7 @@ import com.typesafe.scalalogging.{Logger => SLogger}
|
|||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
import org.slf4j.event.Level
|
import org.slf4j.event.Level
|
||||||
import wow.doge.mygame.AppError
|
import wow.doge.mygame.AppError
|
||||||
|
import wow.doge.mygame.AppError.TimeoutError
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import wow.doge.mygame.subsystems.events.Event
|
import wow.doge.mygame.subsystems.events.Event
|
||||||
import wow.doge.mygame.subsystems.events.EventBus
|
import wow.doge.mygame.subsystems.events.EventBus
|
||||||
@ -34,21 +33,20 @@ class EventsModule(
|
|||||||
val eventBusLogger = SLogger[EventBus[_]]
|
val eventBusLogger = SLogger[EventBus[_]]
|
||||||
|
|
||||||
val playerEventBus: IO[AppError, GameEventBus[PlayerEvent]] =
|
val playerEventBus: IO[AppError, GameEventBus[PlayerEvent]] =
|
||||||
createEventBus[PlayerEvent]("playerEventBus")
|
createEventBus[PlayerEvent]()
|
||||||
|
|
||||||
// val playerCameraEventBusTask =
|
// val playerCameraEventBusTask =
|
||||||
// createEventBus[PlayerCameraEvent]("playerCameraEventBus", Level.DEBUG)
|
// createEventBus[PlayerCameraEvent](Level.DEBUG)
|
||||||
|
|
||||||
val tickEventBus: IO[AppError, GameEventBus[TickEvent]] =
|
val tickEventBus: IO[AppError, GameEventBus[TickEvent]] =
|
||||||
createEventBus[TickEvent]("tickEventBus", Level.TRACE)
|
createEventBus[TickEvent](Level.TRACE)
|
||||||
|
|
||||||
val mainEventBus: IO[AppError, GameEventBus[Event]] =
|
val mainEventBus: IO[AppError, GameEventBus[Event]] = createEventBus[Event]()
|
||||||
createEventBus[Event]("mainEventBus")
|
|
||||||
|
|
||||||
def createEventBus[T: ClassTag](
|
def createEventBus[T: ClassTag](
|
||||||
busName: String,
|
logLevel: Level = Level.DEBUG,
|
||||||
logLevel: Level = Level.DEBUG
|
busName: Option[String] = None
|
||||||
) =
|
)(implicit name: sourcecode.Name) =
|
||||||
spawnProtocol
|
spawnProtocol
|
||||||
.askL(
|
.askL(
|
||||||
SpawnProtocol.Spawn[EventBus.Command[T]](
|
SpawnProtocol.Spawn[EventBus.Command[T]](
|
||||||
@ -58,17 +56,16 @@ class EventsModule(
|
|||||||
.withLogger(eventBusLogger.underlying),
|
.withLogger(eventBusLogger.underlying),
|
||||||
Behaviors
|
Behaviors
|
||||||
.supervise(EventBus[T]())
|
.supervise(EventBus[T]())
|
||||||
.onFailure[Exception](SupervisorStrategy.restart)
|
.onFailure[Exception](
|
||||||
|
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
busName,
|
busName.fold(name.value)(identity),
|
||||||
Props.empty,
|
Props.empty,
|
||||||
_
|
_
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.onErrorHandleWith {
|
.onErrorHandleWith(TimeoutError.from)
|
||||||
case ex: TimeoutException =>
|
|
||||||
IO.raiseError(AppError.TimeoutError(ex.getMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package wow.doge.mygame.subsystems.events
|
|||||||
sealed trait PlayerEvent
|
sealed trait PlayerEvent
|
||||||
|
|
||||||
sealed trait PlayerMovementEvent extends PlayerEvent
|
sealed trait PlayerMovementEvent extends PlayerEvent
|
||||||
final object PlayerMovementEvent {
|
object PlayerMovementEvent {
|
||||||
final case class PlayerMovedLeft(pressed: Boolean) extends PlayerMovementEvent
|
final case class PlayerMovedLeft(pressed: Boolean) extends PlayerMovementEvent
|
||||||
final case class PlayerMovedRight(pressed: Boolean)
|
final case class PlayerMovedRight(pressed: Boolean)
|
||||||
extends PlayerMovementEvent
|
extends PlayerMovementEvent
|
||||||
@ -11,16 +11,16 @@ final object PlayerMovementEvent {
|
|||||||
extends PlayerMovementEvent
|
extends PlayerMovementEvent
|
||||||
final case class PlayerMovedBackward(pressed: Boolean)
|
final case class PlayerMovedBackward(pressed: Boolean)
|
||||||
extends PlayerMovementEvent
|
extends PlayerMovementEvent
|
||||||
final case object PlayerJumped extends PlayerMovementEvent
|
case object PlayerJumped extends PlayerMovementEvent
|
||||||
// final case object PlayerTurnedRight extends PlayerMovementEvent
|
// case object PlayerTurnedRight extends PlayerMovementEvent
|
||||||
// final case object PlayerTurnedLeft extends PlayerMovementEvent
|
// case object PlayerTurnedLeft extends PlayerMovementEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait PlayerCameraEvent extends PlayerEvent
|
sealed trait PlayerCameraEvent extends PlayerEvent
|
||||||
|
|
||||||
final object PlayerCameraEvent {
|
object PlayerCameraEvent {
|
||||||
final case object CameraLeft extends PlayerCameraEvent
|
case object CameraLeft extends PlayerCameraEvent
|
||||||
final case object CameraRight extends PlayerCameraEvent
|
case object CameraRight extends PlayerCameraEvent
|
||||||
final case object CameraMovedUp extends PlayerCameraEvent
|
case object CameraMovedUp extends PlayerCameraEvent
|
||||||
final case object CameraMovedDown extends PlayerCameraEvent
|
case object CameraMovedDown extends PlayerCameraEvent
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ object ScriptCachingActor {
|
|||||||
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
|
final case class GetMap(requester: ActorRef[ScriptsMap]) extends Command
|
||||||
final case class Put(scriptPath: os.Path, script: ScriptObject)
|
final case class Put(scriptPath: os.Path, script: ScriptObject)
|
||||||
extends Command
|
extends Command
|
||||||
private[scriptsystem] final case object NoOp extends Command
|
private[scriptsystem] case object NoOp extends Command
|
||||||
|
|
||||||
private[scriptsystem] final case class DelegateToChild(
|
private[scriptsystem] final case class DelegateToChild(
|
||||||
scriptActor: ActorRef[ScriptActor.Command],
|
scriptActor: ActorRef[ScriptActor.Command],
|
||||||
@ -62,7 +62,7 @@ object ScriptCachingActor {
|
|||||||
// requester: ActorRef[Map[os.Path, ScriptResult]]
|
// requester: ActorRef[Map[os.Path, ScriptResult]]
|
||||||
// ) extends Command
|
// ) extends Command
|
||||||
|
|
||||||
// final case class Props(
|
// class Props(
|
||||||
// ctx: ActorContext[Command],
|
// ctx: ActorContext[Command],
|
||||||
// scriptActor: ActorRef[ScriptActor.Command]
|
// scriptActor: ActorRef[ScriptActor.Command]
|
||||||
// ) {
|
// ) {
|
||||||
@ -221,7 +221,7 @@ class ScriptCachingActor(
|
|||||||
scriptActor: ActorRef[ScriptActor.Command],
|
scriptActor: ActorRef[ScriptActor.Command],
|
||||||
scriptPath: os.Path,
|
scriptPath: os.Path,
|
||||||
requester: ActorRef[ScriptResult]
|
requester: ActorRef[ScriptResult]
|
||||||
)(implicit timeout: Timeout) = {
|
)(implicit timeout: Timeout) =
|
||||||
ctx.ask(scriptActor, ScriptActor.CompileAny(scriptPath, _)) {
|
ctx.ask(scriptActor, ScriptActor.CompileAny(scriptPath, _)) {
|
||||||
case Success(value) =>
|
case Success(value) =>
|
||||||
requester ! value
|
requester ! value
|
||||||
@ -230,16 +230,12 @@ class ScriptCachingActor(
|
|||||||
ctx.log.errorP(err.reason)
|
ctx.log.errorP(err.reason)
|
||||||
NoOp
|
NoOp
|
||||||
},
|
},
|
||||||
res => {
|
res => Put(scriptPath, res)
|
||||||
Put(scriptPath, res)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
case Failure(exception) => {
|
case Failure(exception) =>
|
||||||
ctx.log.errorP(exception.getMessage)
|
ctx.log.errorP(exception.getMessage)
|
||||||
NoOp
|
NoOp
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +247,8 @@ object ScriptActorPool {
|
|||||||
// make sure the workers are restarted if they fail
|
// make sure the workers are restarted if they fail
|
||||||
Behaviors
|
Behaviors
|
||||||
.supervise(ScriptActor())
|
.supervise(ScriptActor())
|
||||||
.onFailure[Exception](SupervisorStrategy.restart)
|
.onFailure[Exception](
|
||||||
|
SupervisorStrategy.restart.withLimit(2, 100.millis)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,8 @@ class ScriptSystemResource(
|
|||||||
findScriptFiles(os.pwd / "assets" / "scripts")
|
findScriptFiles(os.pwd / "assets" / "scripts")
|
||||||
).hideErrors
|
).hideErrors
|
||||||
scriptCacheActor <- AkkaUtils.spawnActorL(
|
scriptCacheActor <- AkkaUtils.spawnActorL(
|
||||||
ScriptCachingActor(),
|
ScriptCachingActor()
|
||||||
"scriptCachingActor"
|
// "scriptCachingActor"
|
||||||
)
|
)
|
||||||
} yield scriptCacheActor
|
} yield scriptCacheActor
|
||||||
|
|
||||||
|
17
src/main/scala/wow/doge/mygame/types/package.scala
Normal file
17
src/main/scala/wow/doge/mygame/types/package.scala
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package wow.doge.mygame
|
||||||
|
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.AppNode2
|
||||||
|
import com.softwaremill.tagging._
|
||||||
|
import wow.doge.mygame.game.GameAppTags
|
||||||
|
import monix.execution.Scheduler
|
||||||
|
import io.estatico.newtype.macros.newtype
|
||||||
|
|
||||||
|
package object types {
|
||||||
|
type RootNode = AppNode2 @@ GameAppTags.RootNode
|
||||||
|
type GuiNode = AppNode2 @@ GameAppTags.GuiNode
|
||||||
|
@newtype case class AsyncScheduler(value: Scheduler)
|
||||||
|
@newtype case class IoScheduler(value: Scheduler)
|
||||||
|
@newtype case class FxScheduler(value: Scheduler)
|
||||||
|
@newtype case class JmeScheduler(value: Scheduler)
|
||||||
|
@newtype case class AkkaScheduler(value: akka.actor.typed.Scheduler)
|
||||||
|
}
|
@ -26,18 +26,19 @@ object AkkaUtils {
|
|||||||
)
|
)
|
||||||
def spawnActorL[T](
|
def spawnActorL[T](
|
||||||
behavior: Behavior[T],
|
behavior: Behavior[T],
|
||||||
actorName: String,
|
actorName: Option[String] = None,
|
||||||
props: Props = Props.empty
|
props: Props = Props.empty
|
||||||
)(implicit
|
)(implicit
|
||||||
timeout: Timeout,
|
timeout: Timeout,
|
||||||
scheduler: Scheduler,
|
scheduler: Scheduler,
|
||||||
spawnProtocol: ActorRef[SpawnProtocol.Command]
|
spawnProtocol: ActorRef[SpawnProtocol.Command],
|
||||||
|
name: sourcecode.Name
|
||||||
) =
|
) =
|
||||||
spawnProtocol
|
spawnProtocol
|
||||||
.askL[ActorRef[T]](
|
.askL[ActorRef[T]](
|
||||||
SpawnProtocol.Spawn(
|
SpawnProtocol.Spawn(
|
||||||
behavior,
|
behavior,
|
||||||
actorName,
|
actorName.fold(name.value)(identity),
|
||||||
props,
|
props,
|
||||||
_
|
_
|
||||||
)
|
)
|
||||||
|
@ -12,8 +12,8 @@ import wow.doge.mygame.implicits._
|
|||||||
|
|
||||||
object GenericTimerActor {
|
object GenericTimerActor {
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
final case object Start extends Command
|
case object Start extends Command
|
||||||
final case object Stop extends Command
|
case object Stop extends Command
|
||||||
private case object Tick extends Command
|
private case object Tick extends Command
|
||||||
case class TimerKey(seed: Long)
|
case class TimerKey(seed: Long)
|
||||||
|
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
package wow.doge.mygame.utils
|
package wow.doge.mygame.utils
|
||||||
|
|
||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
|
import monix.bio.Task
|
||||||
|
import monix.eval.Coeval
|
||||||
|
|
||||||
object IOUtils {
|
object IOUtils {
|
||||||
def toTask[T](bio: monix.bio.IO[Throwable, T]) =
|
def toTask[T](bio: IO[Throwable, T]) =
|
||||||
monix.eval.Task.deferAction(implicit s => bio.to[monix.eval.Task])
|
monix.eval.Task.deferAction(implicit s => bio.to[monix.eval.Task])
|
||||||
|
|
||||||
def toIO[T](task: monix.eval.Task[T]) =
|
def toIO[T](task: monix.eval.Task[T]) =
|
||||||
IO.deferAction(implicit s => IO.from(task))
|
IO.deferAction(implicit s => IO.from(task))
|
||||||
|
|
||||||
|
def fromCoevalEither[L, R](coeval: Coeval[Either[L, R]]) =
|
||||||
|
coeval.to[Task].hideErrors.rethrow
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import cats.syntax.eq._
|
|||||||
import com.jme3.light.Light
|
import com.jme3.light.Light
|
||||||
import com.jme3.{scene => jmes}
|
import com.jme3.{scene => jmes}
|
||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
import monix.bio.Task
|
|
||||||
import monix.bio.UIO
|
import monix.bio.UIO
|
||||||
import monix.execution.annotations.UnsafeBecauseImpure
|
import monix.execution.annotations.UnsafeBecauseImpure
|
||||||
import monix.reactive.Observable
|
import monix.reactive.Observable
|
||||||
@ -71,6 +70,8 @@ abstract class NodeWrapper2 protected (node: jmes.Node) {
|
|||||||
import NodeWrapper2._
|
import NodeWrapper2._
|
||||||
def name = node.getName()
|
def name = node.getName()
|
||||||
def children: Observable[jmes.Spatial] = node.observableChildren
|
def children: Observable[jmes.Spatial] = node.observableChildren
|
||||||
|
def breadthFirstTraversal = node.observableBreadthFirst()
|
||||||
|
def depthFirstTraversal = node.observableDepthFirst()
|
||||||
def attachChild(n: jmes.Spatial): IO[AddNodeToItselfError.type, Unit] =
|
def attachChild(n: jmes.Spatial): IO[AddNodeToItselfError.type, Unit] =
|
||||||
IO { node.attachChild(n); () }.onErrorHandleWith {
|
IO { node.attachChild(n); () }.onErrorHandleWith {
|
||||||
case ex: IllegalArgumentException =>
|
case ex: IllegalArgumentException =>
|
||||||
@ -86,17 +87,10 @@ abstract class NodeWrapper2 protected (node: jmes.Node) {
|
|||||||
else IO.unit
|
else IO.unit
|
||||||
}
|
}
|
||||||
def remove(n: jmes.Spatial) = UIO(node.detachChild(n))
|
def remove(n: jmes.Spatial) = UIO(node.detachChild(n))
|
||||||
def remove(wn: Node2) =
|
def remove(wn: Node2) = UIO(node.detachChild(wn.unsafeDelegate))
|
||||||
UIO(node.detachChild(wn.unsafeDelegate))
|
def addLight(light: Light) = UIO(node.addLight(light))
|
||||||
def addLight(light: Light) =
|
def removeLight(light: Light) = UIO(node.removeLight(light))
|
||||||
UIO {
|
def asSpatial: UIO[jmes.Spatial] = UIO(node)
|
||||||
node.addLight(light)
|
|
||||||
}
|
|
||||||
def removeLight(light: Light) =
|
|
||||||
UIO {
|
|
||||||
node.removeLight(light)
|
|
||||||
}
|
|
||||||
def asSpatial: Task[jmes.Spatial] = UIO(node)
|
|
||||||
}
|
}
|
||||||
object NodeWrapper2 {
|
object NodeWrapper2 {
|
||||||
sealed trait Error
|
sealed trait Error
|
||||||
|
@ -1,57 +1,91 @@
|
|||||||
package wow.doge.mygame.utils.wrappers.jme
|
package wow.doge.mygame.utils.wrappers.jme
|
||||||
|
import com.jme3.bullet.control.PhysicsControl
|
||||||
|
import com.jme3.bullet.joints.PhysicsJoint
|
||||||
import com.jme3.{bullet => jmeb}
|
import com.jme3.{bullet => jmeb}
|
||||||
import com.jme3.{scene => jmes}
|
import com.jme3.{scene => jmes}
|
||||||
import monix.bio.UIO
|
import monix.bio.UIO
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
|
|
||||||
final class PhysicsSpace(space: jmeb.PhysicsSpace) {
|
final class PhysicsSpace(space: jmeb.PhysicsSpace) {
|
||||||
def add(anyObject: Any) = UIO(space.add(anyObject))
|
def add[T](addable: T)(implicit P: PhysicsSpaceAddable[T]) =
|
||||||
|
UIO(P.addToSpace(addable, space))
|
||||||
|
|
||||||
def remove(anyObject: Any) =
|
def remove(anyObject: Any) = UIO(space.remove(anyObject))
|
||||||
UIO {
|
|
||||||
space.remove(anyObject)
|
|
||||||
space
|
|
||||||
}
|
|
||||||
|
|
||||||
def addAll(spatial: jmes.Spatial) = UIO(space.addAll(spatial))
|
def addAll(spatial: jmes.Spatial) = UIO(space.addAll(spatial))
|
||||||
|
|
||||||
def removeAll(spatial: jmes.Spatial) =
|
def removeAll(spatial: jmes.Spatial) = UIO(space.removeAll(spatial))
|
||||||
UIO {
|
|
||||||
space.removeAll(spatial)
|
|
||||||
space
|
|
||||||
}
|
|
||||||
|
|
||||||
def collisionObservable = space.collisionObservable()
|
def collisionObservable = space.collisionObservable()
|
||||||
|
|
||||||
|
// space.enqueue(() => ())
|
||||||
|
|
||||||
def physicsTickObservable = space.physicsTickObservable()
|
def physicsTickObservable = space.physicsTickObservable()
|
||||||
|
|
||||||
|
def prePhysicsTickObservable = space.prePhysicsTickObservable()
|
||||||
}
|
}
|
||||||
object PhysicsSpace {
|
object PhysicsSpace {
|
||||||
implicit final class PhysicsSpaceOps(private val space: PhysicsSpace)
|
implicit final class PhysicsSpaceOps(private val space: PhysicsSpace)
|
||||||
extends AnyVal {
|
extends AnyVal {
|
||||||
def +=(anyObject: Any) = space.add(anyObject)
|
def +=[T: PhysicsSpaceAddable](addable: T) = space.add(addable)
|
||||||
|
|
||||||
def :+(anyObject: Any) = {
|
def :+[T: PhysicsSpaceAddable](addable: T) =
|
||||||
space.add(anyObject)
|
for {
|
||||||
space
|
_ <- space.add(addable)
|
||||||
}
|
} yield space
|
||||||
def -(anyObject: Any) = {
|
def -(anyObject: Any) =
|
||||||
space.remove(anyObject)
|
for {
|
||||||
space
|
_ <- space.remove(anyObject)
|
||||||
}
|
} yield space
|
||||||
|
|
||||||
def -=(anyObject: Any) = space.remove(anyObject)
|
def -=(anyObject: Any) = space.remove(anyObject)
|
||||||
|
|
||||||
def +=(spatial: jmes.Spatial) = space.addAll(spatial)
|
def +=(spatial: jmes.Spatial) = space.addAll(spatial)
|
||||||
|
|
||||||
def :+(spatial: jmes.Spatial) = {
|
// def :+(spatial: jmes.Spatial) = {
|
||||||
space.addAll(spatial)
|
// space.addAll(spatial)
|
||||||
space
|
// space
|
||||||
}
|
// }
|
||||||
|
|
||||||
def -(spatial: jmes.Spatial) = {
|
// def -(spatial: jmes.Spatial) = {
|
||||||
space.removeAll(spatial)
|
// space.removeAll(spatial)
|
||||||
space
|
// space
|
||||||
}
|
// }
|
||||||
|
|
||||||
def -=(spatial: jmes.Spatial) = space.removeAll(spatial)
|
def -=(spatial: jmes.Spatial) = space.removeAll(spatial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait PhysicsSpaceAddable[-T] {
|
||||||
|
def addToSpace(inst: T, space: jmeb.PhysicsSpace): Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
object PhysicsSpaceAddable {
|
||||||
|
implicit val physicsSpaceAddableForPhysControl =
|
||||||
|
new PhysicsSpaceAddable[PhysicsControl] {
|
||||||
|
|
||||||
|
override def addToSpace(
|
||||||
|
inst: PhysicsControl,
|
||||||
|
space: jmeb.PhysicsSpace
|
||||||
|
): Unit = space.add(inst)
|
||||||
|
|
||||||
|
}
|
||||||
|
implicit val physicsSpaceAddableForSpatial =
|
||||||
|
new PhysicsSpaceAddable[jmes.Spatial] {
|
||||||
|
|
||||||
|
override def addToSpace(
|
||||||
|
inst: jmes.Spatial,
|
||||||
|
space: jmeb.PhysicsSpace
|
||||||
|
): Unit = space.add(inst)
|
||||||
|
|
||||||
|
}
|
||||||
|
implicit val physicsSpaceAddableForPhysJoint =
|
||||||
|
new PhysicsSpaceAddable[PhysicsJoint] {
|
||||||
|
|
||||||
|
override def addToSpace(
|
||||||
|
inst: PhysicsJoint,
|
||||||
|
space: jmeb.PhysicsSpace
|
||||||
|
): Unit = space.add(inst)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,14 +17,13 @@ class ActorTimeoutTest extends AnyFunSuite with BeforeAndAfterAll {
|
|||||||
implicit val timeout = Timeout(1.millis)
|
implicit val timeout = Timeout(1.millis)
|
||||||
|
|
||||||
test("timeoutTest") {
|
test("timeoutTest") {
|
||||||
val fut = as.ask(MyActor.GetInt(_))
|
val fut = as.ask(MyActor.GetInt)
|
||||||
val res = Await.result(fut, 1.second)
|
val res = Await.result(fut, 1.second)
|
||||||
assert(res == 1)
|
assert(res == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def afterAll(): Unit = {
|
override protected def afterAll(): Unit =
|
||||||
as.terminate()
|
as.terminate()
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,12 @@ import monix.bio.UIO
|
|||||||
import org.scalatest.funsuite.AnyFunSuite
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
import monix.execution.Scheduler.Implicits.global
|
import monix.execution.Scheduler.Implicits.global
|
||||||
import monix.bio.IO
|
import monix.bio.IO
|
||||||
|
import cats.mtl.Ask
|
||||||
|
import cats.mtl.implicits._
|
||||||
|
import cats.effect.LiftIO
|
||||||
|
import monix.bio.Task
|
||||||
|
import cats.data.Kleisli
|
||||||
|
import monix.bio.IOLift
|
||||||
|
|
||||||
class ReaderT_Test extends AnyFunSuite {
|
class ReaderT_Test extends AnyFunSuite {
|
||||||
|
|
||||||
@ -33,8 +39,36 @@ class ReaderT_Test extends AnyFunSuite {
|
|||||||
// uio.runSyncUnsafe()
|
// uio.runSyncUnsafe()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
implicit val L =
|
||||||
|
new LiftIO[ReaderT[IO[String, ?], Environment, *]] {
|
||||||
|
|
||||||
|
override def liftIO[A](
|
||||||
|
ioa: cats.effect.IO[A]
|
||||||
|
): ReaderT[IO[String, ?], Environment, A] =
|
||||||
|
ReaderT(_ => IO.from(ioa).onErrorHandleWith(_ => IO.raiseError("whew")))
|
||||||
|
|
||||||
|
// override def liftIO[A](ioa: cats.effect.IO[A]): monix.bio.Task[A] =
|
||||||
|
// Task.from(ioa)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val L2 = new IOLift[ReaderT[IO[String, ?], Environment, *]] {
|
||||||
|
|
||||||
|
override def apply[A](
|
||||||
|
task: monix.bio.Task[A]
|
||||||
|
): ReaderT[IO[String, ?], Environment, A] =
|
||||||
|
ReaderT(_ => IO.from(task).onErrorHandleWith(_ => IO.raiseError("whew")))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait AppError
|
||||||
|
|
||||||
|
type RIO[S, E, A] = ReaderT[IO[E, ?], S, A]
|
||||||
|
type EIO[S, A] = RIO[S, AppError, A]
|
||||||
|
type AppIO[A] = RIO[Environment, AppError, A]
|
||||||
|
|
||||||
test("2") {
|
test("2") {
|
||||||
def fun1: ReaderT[IO[String, ?], String, Unit] =
|
def fun1: RIO[String, String, Unit] =
|
||||||
ReaderT(s => IO(println(s)).onErrorHandleWith(_ => IO.raiseError("wow")))
|
ReaderT(s => IO(println(s)).onErrorHandleWith(_ => IO.raiseError("wow")))
|
||||||
def fun2: ReaderT[IO[String, ?], Int, Unit] =
|
def fun2: ReaderT[IO[String, ?], Int, Unit] =
|
||||||
ReaderT(num =>
|
ReaderT(num =>
|
||||||
@ -44,6 +78,8 @@ class ReaderT_Test extends AnyFunSuite {
|
|||||||
for {
|
for {
|
||||||
env <- ReaderT.ask[IO[String, ?], Environment]
|
env <- ReaderT.ask[IO[String, ?], Environment]
|
||||||
} yield ()
|
} yield ()
|
||||||
|
def test[F[_]]()(implicit A: Ask[F, Int]) = A.ask[Int]
|
||||||
|
// def synctest[F[_]]
|
||||||
def fun4: ReaderT[IO[String, ?], Environment, Unit] =
|
def fun4: ReaderT[IO[String, ?], Environment, Unit] =
|
||||||
for {
|
for {
|
||||||
env <- ReaderT.ask[IO[String, ?], Environment]
|
env <- ReaderT.ask[IO[String, ?], Environment]
|
||||||
@ -52,12 +88,15 @@ class ReaderT_Test extends AnyFunSuite {
|
|||||||
)
|
)
|
||||||
_ <- fun3
|
_ <- fun3
|
||||||
} yield ()
|
} yield ()
|
||||||
|
val topkek =
|
||||||
|
test[ReaderT[IO[String, ?], Int, *]]().local[Environment](_.num)
|
||||||
def app: ReaderT[IO[String, ?], Environment, Unit] =
|
def app: ReaderT[IO[String, ?], Environment, Unit] =
|
||||||
for {
|
for {
|
||||||
_ <- fun1.local[Environment](_.str)
|
_ <- fun1.local[Environment](_.str)
|
||||||
_ <- fun2.local[Environment](_.num)
|
_ <- fun2.local[Environment](_.num)
|
||||||
_ <- fun3
|
_ <- fun3
|
||||||
_ <- fun4
|
_ <- fun4
|
||||||
|
_ <- topkek
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
val io: IO[String, Unit] = app.run(Environment("hello", 50))
|
val io: IO[String, Unit] = app.run(Environment("hello", 50))
|
||||||
|
Loading…
Reference in New Issue
Block a user