package wow.doge.mygame import akka.actor.typed.ActorSystem import akka.actor.typed.Scheduler import akka.actor.typed.SpawnProtocol import akka.util.Timeout import cats.effect.Resource import cats.effect.concurrent.Deferred import com.jme3.app.state.AppStateManager import com.jme3.asset.AssetManager import com.jme3.asset.plugins.ZipLocator import com.jme3.bullet.BulletAppState import com.jme3.bullet.control.BetterCharacterControl import com.jme3.input.InputManager import com.jme3.renderer.Camera import com.jme3.renderer.RenderManager import com.jme3.renderer.ViewPort import com.jme3.scene.Node import com.jme3.scene.control.AbstractControl import com.softwaremill.macwire._ import com.softwaremill.tagging._ import io.odin.Logger import monix.bio.Fiber import monix.bio.IO import monix.bio.Task import monix.execution.exceptions.DummyException import scalafx.scene.control.TextArea import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.game.GameApp import wow.doge.mygame.game.GameAppActor import wow.doge.mygame.game.GameAppTags import wow.doge.mygame.game.entities.EntityIds import wow.doge.mygame.game.entities.NpcActorSupervisor import wow.doge.mygame.game.entities.NpcMovementActor import wow.doge.mygame.game.entities.PlayerController import wow.doge.mygame.game.entities.PlayerControllerTags import wow.doge.mygame.game.subsystems.input.GameInputHandler import wow.doge.mygame.game.subsystems.level.DefaultGameLevel import wow.doge.mygame.implicits._ import wow.doge.mygame.launcher.Launcher import wow.doge.mygame.launcher.Launcher.LauncherResult import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.subsystems.events.EventsModule import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.GenericConsoleStream import cats.syntax.eq._ import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus class MainApp( logger: Logger[Task], jmeThread: monix.execution.Scheduler, schedulers: Schedulers, consoleStream: GenericConsoleStream[TextArea] )(implicit spawnProtocol: ActorSystem[SpawnProtocol.Command], @annotation.unused timeout: Timeout, @annotation.unused scheduler: Scheduler ) { val scriptSystemInit = new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init val eventsModule = new EventsModule(spawnProtocol) def gameInit: Resource[Task, Fiber[Throwable, Unit]] = GameApp.resource(logger, jmeThread, schedulers).evalMap { case gameApp -> gameAppFib => for { playerEventBus <- eventsModule.playerEventBusTask mainEventBus <- eventsModule.mainEventBusTask tickEventBus <- eventsModule.tickEventBusTask gameAppActor <- AkkaUtils.spawnActorL( GameAppActor.Props(tickEventBus).behavior, "gameAppActor" ) _ <- gameAppActor !! GameAppActor.Start inputManager <- gameApp.inputManager assetManager <- gameApp.assetManager stateManager <- gameApp.stateManager camera <- gameApp.camera rootNode <- gameApp.rootNode enqueueR <- Task(gameApp.enqueue _) viewPort <- gameApp.viewPort _ <- logger.info("before") // jfxUI <- gameApp.jfxUI consoleTextArea <- Task(new TextArea { text = "hello \n" editable = false wrapText = true // maxHeight = 150 // maxWidth = 300 }) // _ <- Task(consoleStream := consoleTextArea) // _ <- Task(jfxUI += consoleTextArea) _ <- logger.info("after") bulletAppState <- Task(new BulletAppState()) _ <- Task(stateManager.attach(bulletAppState)) _ <- logger.info("Initializing console stream") _ <- wire[MainAppDelegate] .init(gameApp.scheduler) .executeOn(gameApp.scheduler) } yield gameAppFib } val program = for { scriptSystem <- scriptSystemInit launchSignal <- Deferred[Task, Launcher.LauncherResult] launcher <- new Launcher.Props(schedulers, launchSignal).create launchResult <- launcher.init.use(_ => launchSignal.get) _ <- /** * User chose to quit */ if (launchResult === LauncherResult.Exit) logger.info("Exiting") /** * User chose launch. Wait for game window to close */ else gameInit.use(_.join) } yield () } /** * Class with all dependencies in one place for easy wiring */ class MainAppDelegate( gameApp: GameApp, loggerL: Logger[Task], playerEventBus: GameEventBus[PlayerEvent], tickEventBus: GameEventBus[TickEvent], inputManager: InputManager, assetManager: AssetManager, stateManager: AppStateManager, camera: Camera, viewPort: ViewPort, enqueueR: Function1[() => Unit, Unit], rootNode: Node @@ GameAppTags.RootNode, bulletAppState: BulletAppState )(implicit spawnProtocol: ActorSystem[SpawnProtocol.Command], @annotation.unused timeout: Timeout, @annotation.unused scheduler: Scheduler ) { val physicsSpace = bulletAppState.physicsSpace def init( appScheduler: monix.execution.Scheduler // consoleStream: GenericConsoleStream[TextArea] ) = for { _ <- loggerL.info("Initializing Systems") _ <- Task( assetManager.registerLocator( os.rel / "assets" / "town.zip", classOf[ZipLocator] ) ) _ <- loggerL.info("test") // _ <- Task(consoleStream.println("text")) _ <- DefaultGameLevel(assetManager, viewPort) .addToGame( rootNode, bulletAppState.physicsSpace ) _ <- createPlayerController(appScheduler) .absorbWith(e => DummyException("boom")) .onErrorRestart(3) _ <- wire[GameInputHandler.Props].begin.onErrorRestart(3) // johnActor <- createTestNpc(appScheduler, "John").executeOn(appScheduler) // _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20)) // _ <- // (johnActor !! NpcActorSupervisor.Move( // ImVector3f(-30, 0, 10) // )).executeAsync // .delayExecution(2.seconds) // _ <- // IOUtils // .toIO( // rootNode // .observableBreadthFirst() // .doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName()))) // .completedL // ) // .executeOn(appScheduler) // .startAndForget } yield () def createPlayerController( appScheduler: monix.execution.Scheduler ): IO[PlayerController.Error, Unit] = { val playerPos = ImVector3f.ZERO val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" val playerPhysicsControl = PlayerController.Defaults.defaultPlayerPhysicsControl .taggedWith[PlayerControllerTags.PlayerTag] // lazy val camNode = // PlayerController.Defaults // .defaultCamerNode(camera, playerPos) // .taggedWith[PlayerControllerTags.PlayerCameraNode] val mbPlayerNode = PlayerController.Defaults .defaultPlayerNode( assetManager, modelPath, playerPos, // camNode playerPhysicsControl ) val cameraPivotNode = new Node(EntityIds.CameraPivot.value) .taggedWith[PlayerControllerTags.PlayerCameraPivotNode] for { playerNode <- IO.fromEither(mbPlayerNode) _ <- IO(cameraPivotNode.addControl(new FollowControl(playerNode))) .onErrorHandleWith(e => IO.raiseError(PlayerController.GenericError(e.getMessage())) ) camNode <- IO( PlayerController.Defaults .defaultCamerNode(camera, cameraPivotNode, playerNode, playerPos) ).onErrorHandleWith(e => IO.raiseError(PlayerController.GenericError(e.getMessage())) ).map(_.taggedWith[PlayerControllerTags.PlayerCameraNode]) // _ <- Task { // val chaseCam = new ChaseCamera(camera, playerNode, inputManager) // chaseCam.setSmoothMotion(false) // chaseCam.setLookAtOffset(new Vector3f(0, 1.5f, 10)) // chaseCam // } // .onErrorHandleWith(e => // IO.raiseError(PlayerController.GenericError(e.getMessage())) // ) _ <- wire[PlayerController.Props].create } yield () } def createTestNpc( appScheduler: monix.execution.Scheduler, npcName: String ) = { val initialPos = ImVector3f(50, 5, 0) val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) // (1f, 2.1f, 10f) .withJumpForce(ImVector3f(0, 5f, 0)) val mbNpcNode = PlayerController.Defaults.defaultNpcNode( assetManager, initialPos, npcPhysicsControl, npcName ) val npcActorTask = AkkaUtils.spawnActorL( NpcActorSupervisor .Props( new NpcMovementActor.Props( enqueueR, initialPos, // tickEventBus, npcPhysicsControl ).behavior, npcName, initialPos ) .behavior, s"${npcName}-npcActorSupervisor" ) // .taggedWith[PlayerControllerTags.PlayerTag] for { npcNode <- IO.fromEither(mbNpcNode) npcActor <- npcActorTask _ <- IO { physicsSpace += npcPhysicsControl physicsSpace += npcNode rootNode += npcNode } } yield npcActor } } 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 = {} }