package wow.doge.mygame import java.util.concurrent.TimeoutException import scala.concurrent.duration._ import akka.actor.typed.ActorRef import akka.actor.typed.Scheduler import akka.actor.typed.SpawnProtocol import akka.util.Timeout import cats.effect.Resource import cats.effect.concurrent.Deferred import cats.syntax.eq._ import com.jme3.asset.plugins.ZipLocator import com.jme3.bullet.control.BetterCharacterControl import com.jme3.input.InputManager import com.jme3.material.Material import com.jme3.material.MaterialDef import com.jme3.math.ColorRGBA import com.jme3.math.FastMath import com.jme3.renderer.Camera import com.jme3.renderer.RenderManager import com.jme3.renderer.ViewPort import com.jme3.scene.Node import com.jme3.scene.control.AbstractControl import com.softwaremill.macwire._ import com.softwaremill.tagging._ import io.odin.Logger import monix.bio.Fiber import monix.bio.IO import monix.bio.Task import monix.bio.UIO import monix.eval.Coeval import monix.reactive.Observable 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.GameAppResource import wow.doge.mygame.game.GameAppTags import wow.doge.mygame.game.entities.EntityIds import wow.doge.mygame.game.entities.NpcActorSupervisor import wow.doge.mygame.game.entities.NpcMovementActor import wow.doge.mygame.game.entities.PlayerActorSupervisor import wow.doge.mygame.game.entities.PlayerController import wow.doge.mygame.game.entities.PlayerControllerTags import wow.doge.mygame.game.subsystems.input.GameInputHandler import wow.doge.mygame.game.subsystems.level.DefaultGameLevel import wow.doge.mygame.implicits._ import wow.doge.mygame.launcher.Launcher import wow.doge.mygame.launcher.Launcher.LauncherResult import wow.doge.mygame.math.ImVector3f import wow.doge.mygame.subsystems.events.EventBus.ObservableSubscription import wow.doge.mygame.subsystems.events.EventsModule import wow.doge.mygame.subsystems.events.EventsModule.GameEventBus import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.subsystems.events.TickEvent import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.GenericConsoleStream import wow.doge.mygame.utils.IOUtils import wow.doge.mygame.utils.wrappers.jme.AppNode2 import wow.doge.mygame.utils.wrappers.jme.AssetManager import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace class MainApp( logger: Logger[Task], jmeThread: monix.execution.Scheduler, schedulers: Schedulers, consoleStream: GenericConsoleStream[TextArea] )(implicit spawnProtocol: ActorRef[SpawnProtocol.Command], timeout: Timeout, scheduler: Scheduler ) { val scriptSystemInit = new ScriptSystemResource(os.pwd, ScriptInitMode.Eager).init val eventsModule = new EventsModule(scheduler, spawnProtocol) class TestClass( playerEventBus: GameEventBus[PlayerEvent], tickEventBus: GameEventBus[TickEvent] ) def eval(gameApp: GameApp, fib: Fiber[Nothing, Unit]) = for { // g <- UIO.pure(gameApp) playerEventBus <- eventsModule.playerEventBus mainEventBus <- eventsModule.mainEventBus tickEventBus <- eventsModule.tickEventBus obs <- playerEventBus .askL[Observable[PlayerMovementEvent]]( ObservableSubscription(_) ) .onErrorHandleWith { case ex: TimeoutException => IO.raiseError(AppError.TimeoutError(ex.getMessage())) } _ <- IOUtils .toIO( obs .doOnNextF(pme => Coeval(pprint.log(s"Received event $pme")).void) .completedL .startAndForget ) .hideErrors inputManager <- gameApp.inputManager assetManager <- UIO.pure(gameApp.assetManager) camera <- gameApp.camera rootNode <- UIO.pure(gameApp.rootNode) enqueueR <- UIO(gameApp.enqueue _) viewPort <- gameApp.viewPort physicsSpace <- UIO.pure(gameApp.physicsSpace) _ <- logger.infoU("before") // jfxUI <- gameApp.jfxUI gameAppActor <- gameApp.spawnGameActor( GameAppActor.Props(tickEventBus).behavior, "gameAppActor" ) _ <- gameAppActor !! GameAppActor.Start consoleTextArea <- UIO(new TextArea { text = "hello \n" editable = false wrapText = true // maxHeight = 150 // maxWidth = 300 }) // _ <- Task(consoleStream := consoleTextArea) // _ <- Task(jfxUI += consoleTextArea) _ <- logger.infoU("after") _ <- logger.infoU("Initializing console stream") _ <- wire[MainAppDelegate] .init() .executeOn(gameApp.scheduler) } yield fib def gameInit: Resource[UIO, Either[AppError, Fiber[Nothing, Unit]]] = wire[GameAppResource].resource.evalMap { case Right(gameApp -> gameAppFib) => eval(gameApp, gameAppFib).attempt case Left(error) => IO.terminate(new Exception(error.toString)) } // val x: Task[Unit] = for { // tickEventBus <- eventsModule.tickEventBusTask // playerEventBus <- eventsModule.playerEventBusTask // _ <- UIO(wire[TestClass]) // _ <- gameInit(tickEventBus).use(_.join) // } yield () val program = for { // scriptSystem <- scriptSystemInit launchSignal <- Deferred[Task, Launcher.LauncherResult].hideErrors launcher <- new Launcher.Props(schedulers, launchSignal).create launchResult <- launcher.init.use(_ => launchSignal.get).hideErrors _ <- /** * User chose to quit */ if (launchResult === LauncherResult.Exit) logger.infoU("Exiting") /** * User chose launch. Wait for game window to close */ else gameInit.use { case Right(fib) => fib.join >> Task.unit case Left(error) => IO.terminate(new Exception(error.toString)) }.hideErrors } 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, physicsSpace: PhysicsSpace, camera: Camera, viewPort: ViewPort, enqueueR: Function1[() => Unit, Unit], rootNode: AppNode2 @@ GameAppTags.RootNode )(implicit spawnProtocol: ActorRef[SpawnProtocol.Command], timeout: Timeout, scheduler: Scheduler ) { def init( // appScheduler: monix.execution.Scheduler // consoleStream: GenericConsoleStream[TextArea] ): IO[AppError, Unit] = for { _ <- loggerL.infoU("Initializing Systems") _ <- assetManager.registerLocator( os.rel / "assets" / "town.zip", classOf[ZipLocator] ) _ <- loggerL.infoU("test") // _ <- Task(consoleStream.println("text")) level <- DefaultGameLevel(assetManager, viewPort) _ <- level.addToGame(rootNode, physicsSpace) _ <- createPlayerController() // .onErrorRestart(3) _ <- wire[GameInputHandler.Props].begin // .onErrorRestart(3) johnActor <- createTestNpc("John") // _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20)) _ <- johnActor .tellL(NpcActorSupervisor.Move(ImVector3f(-30, 0, 10))) .delayExecution(2.seconds) // _ <- // IOUtils // .toIO( // rootNode // .observableBreadthFirst() // .doOnNext(spat => IOUtils.toTask(loggerL.debug(spat.getName()))) // .completedL // ) // .executeOn(appScheduler) // .startAndForget } yield () def createPlayerController( // appScheduler: monix.execution.Scheduler ): IO[AppError, ActorRef[PlayerActorSupervisor.Command]] = { val playerPos = ImVector3f.Zero val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" val playerPhysicsControl = PlayerController.Defaults.defaultPlayerPhysicsControl .taggedWith[PlayerControllerTags.PlayerTag] for { playerModel <- assetManager .loadModelAs[Node](modelPath) .map(_.withRotate(0, FastMath.PI, 0)) .mapError(AppError.AssetManagerError) playerNode <- UIO( PlayerController.Defaults .defaultPlayerNode( playerPos, playerModel, playerPhysicsControl ) ) cameraPivotNode <- UIO( new Node(EntityIds.CameraPivot.value) .withControl(new FollowControl(playerNode)) .taggedWith[PlayerControllerTags.PlayerCameraPivotNode] ) camNode <- UIO( PlayerController.Defaults .defaultCamerNode(camera, playerPos) .taggedWith[PlayerControllerTags.PlayerCameraNode] ) playerActor <- wire[PlayerController.Props].create } yield playerActor } def createTestNpc( // appScheduler: monix.execution.Scheduler, npcName: String ): IO[AppError, ActorRef[NpcActorSupervisor.Command]] = { val initialPos = ImVector3f(50, 5, 0) val npcPhysicsControl = new BetterCharacterControl(1.5f, 6f, 1f) // (1f, 2.1f, 10f) .withJumpForce(ImVector3f(0, 5f, 0)) val npcActorTask = AkkaUtils.spawnActorL( NpcActorSupervisor .Props( new NpcMovementActor.Props( enqueueR, initialPos, // tickEventBus, npcName, npcPhysicsControl ).behavior, npcName, initialPos ) .behavior, s"${npcName}-npcActorSupervisor" ) (for { materialDef <- assetManager .loadAssetAs[MaterialDef]( os.rel / "Common" / "MatDefs" / "Misc" / "Unshaded.j3md" ) .mapError(AppError.AssetManagerError) material = new Material(materialDef) _ = material.setColor("Color", ColorRGBA.Blue) mesh = PlayerController.Defaults.defaultMesh.withMaterial(material) npcNode = PlayerController.Defaults.defaultNpcNode( mesh, initialPos, npcPhysicsControl, npcName ) _ <- (for { _ <- physicsSpace += npcPhysicsControl _ <- physicsSpace += npcNode _ <- rootNode += npcNode } yield ()).mapError(AppError.AppNodeError) npcActor <- npcActorTask } 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 = {} }