package wow.doge.mygame.game.nodes import akka.actor.typed.ActorRef import akka.actor.typed.Props import akka.actor.typed.Scheduler import akka.actor.typed.SpawnProtocol import akka.util.Timeout import cats.effect.concurrent.Ref import cats.implicits._ import com.jme3.asset.AssetManager import com.jme3.bullet.BulletAppState import com.jme3.bullet.control.BetterCharacterControl import com.jme3.math.FastMath import com.jme3.renderer.Camera import com.jme3.scene.CameraNode import com.jme3.scene.Geometry import com.jme3.scene.Node import com.jme3.scene.control.CameraControl.ControlDirection import com.jme3.scene.shape.Box import com.softwaremill.tagging._ import io.odin.Logger import monix.bio.IO import monix.bio.Task import wow.doge.mygame.events.EventBus import wow.doge.mygame.game.GameApp import wow.doge.mygame.implicits._ import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.state.MyMaterial import wow.doge.mygame.subsystems.events.EntityMovementEvent.PlayerMovementEvent import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.movement.ImMovementActor import wow.doge.mygame.utils.AkkaUtils // class PlayerNode(val name: String) extends Node(name) {} sealed trait PlayerTag sealed trait PlayerCameraNode object PlayerController { sealed trait Error case class GenericError(reason: String) extends Error class Props( enqueueR: Function1[() => Unit, Unit], rootNode: Ref[Task, Node], camera: Camera, loggerL: Logger[Task], assetManager: AssetManager, bulletAppState: BulletAppState, initialPlayerPos: ImVector3f = ImVector3f.ZERO, modelPath: os.RelPath, spawnProtocol: ActorRef[SpawnProtocol.Command], playerMovementEventBus: ActorRef[ EventBus.Command[PlayerMovementEvent] ], playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], _playerPhysicsControl: Option[BetterCharacterControl], _playerNode: Option[Node with PlayerTag] = None, _cameraNode: Option[CameraNode with PlayerCameraNode] = None, appScheduler: monix.execution.Scheduler )(implicit timeout: Timeout, scheduler: Scheduler) { import Defaults._ val create: IO[Error, Unit] = // IO.raiseError(GenericError("not implemented yet")) (for { camNode <- IO( _cameraNode.getOrElse(defaultCamerNode(camera, initialPlayerPos)) ) playerPhysicsControl <- IO( _playerPhysicsControl .getOrElse(defaultPlayerPhysicsControl) .taggedWith[PlayerTag] ) playerNode <- IO.fromEither( _playerNode.fold( defaultPlayerNode( assetManager, modelPath, initialPlayerPos, camNode, playerPhysicsControl ) )(_.asRight) ) playerActor <- AkkaUtils.spawnActorL( spawnProtocol, "playerActorSupervisor", new PlayerActorSupervisor.Props( enqueueR, camNode, playerMovementEventBus, playerCameraEventBus ).create(playerPhysicsControl) ) _ <- IO { bulletAppState.physicsSpace += playerNode bulletAppState.physicsSpace += playerPhysicsControl } _ <- rootNode.update(_ :+ playerNode) } yield ()) .onErrorHandleWith(e => IO.raiseError(GenericError(e.getMessage()))) .executeOn(appScheduler) } def apply( app: GameApp, modelPath: os.RelPath, cam: Camera )(assetManager: AssetManager, bulletAppState: BulletAppState) = { lazy val playerPos = ImVector3f.ZERO lazy val playerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) .withJumpForce(ImVector3f(0, 5f, 0)) lazy 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 { lazy val defaultMesh = { val b = Box(1, 1, 1) val geom = Geometry("playerMesh", b) geom } def defaultTexture(assetManager: AssetManager) = MyMaterial( assetManager = assetManager, path = os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md" ) def defaultCamerNode(cam: Camera, playerPos: ImVector3f) = new CameraNode("CameraNode", cam) .withControlDir(ControlDirection.SpatialToCamera) .withLocalTranslation(ImVector3f(0, 1.5f, 10)) .withLookAt(playerPos, ImVector3f.UNIT_Y) def defaultPlayerNode( assetManager: AssetManager, modelPath: os.RelPath, playerPos: ImVector3f, camNode: CameraNode, playerPhysicsControl: BetterCharacterControl ) = Either.catchNonFatal( Node("PlayerNode") .withChildren( camNode, assetManager .loadModel(modelPath) .asInstanceOf[Node] .withRotate(0, FastMath.PI, 0) ) .withLocalTranslation(playerPos) .withControl(playerPhysicsControl) ) lazy val defaultPlayerPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) .withJumpForce(ImVector3f(0, 5f, 0)) } object Methods { def spawnMovementActor( enqueueR: Function1[() => Unit, Unit], spawnProtocol: ActorRef[SpawnProtocol.Command], movable: BetterCharacterControl @@ PlayerTag, playerMovementEventBus: ActorRef[ EventBus.Command[PlayerMovementEvent] ], loggerL: Logger[Task] )(implicit timeout: Timeout, scheduler: Scheduler) = spawnProtocol.askL[ActorRef[ImMovementActor.Command]]( SpawnProtocol.Spawn( ImMovementActor.Props(enqueueR, movable, playerMovementEventBus).create, "imMovementActor", Props.empty, _ ) ) // def spawnPlayerActor( // app: GameApp, // spawnProtocol: ActorRef[SpawnProtocol.Command], // movable: BetterCharacterControl @@ Player, // playerMovementEventBus: ActorRef[ // EventBus.Command[PlayerMovementEvent] // ] // )(implicit timeout: Timeout, scheduler: Scheduler) = // spawnProtocol.askL[ActorRef[PlayerActorSupervisor.Command]]( // SpawnProtocol.Spawn( // new PlayerActorSupervisor.Props( // app, // movable, // playerMovementEventBus // ).create, // "playerActor", // Props.empty, // _ // ) // ) } // spawnPlayerActor( // app, // spawnProtocol, // playerPhysicsControl, // playerMovementEventBus // )