diff --git a/src/main/scala/wow/doge/mygame/MainApp.scala b/src/main/scala/wow/doge/mygame/MainApp.scala index 8f1bbba..c982b2c 100644 --- a/src/main/scala/wow/doge/mygame/MainApp.scala +++ b/src/main/scala/wow/doge/mygame/MainApp.scala @@ -53,8 +53,10 @@ import wow.doge.mygame.game.entities.NpcActorSupervisor import wow.doge.mygame.game.entities.NpcMovementActor import wow.doge.mygame.game.entities.player.PlayerActor import wow.doge.mygame.game.entities.player.PlayerController -import wow.doge.mygame.game.subsystems.input.GameInputHandler +import wow.doge.mygame.game.entities.player.PlayerMovementReducer +import wow.doge.mygame.game.subsystems.input.InputMappings import wow.doge.mygame.game.subsystems.input.PlayerCameraInput +import wow.doge.mygame.game.subsystems.input.PlayerMovementInput import wow.doge.mygame.game.subsystems.level.DefaultGameLevel import wow.doge.mygame.implicits._ import wow.doge.mygame.launcher.Launcher @@ -80,6 +82,7 @@ import wow.doge.mygame.utils.IOUtils import wow.doge.mygame.utils.MonixDirectoryWatcher import wow.doge.mygame.utils.MonixDirectoryWatcher.ModifyEvent import wow.doge.mygame.utils.controls.JFXProgressBar +import wow.doge.mygame.utils.wrappers.jme import wow.doge.mygame.utils.wrappers.jme.AssetManager import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace @@ -118,7 +121,7 @@ class MainApp( .toIO( obs .doOnNextF(pme => - Coeval(pprint.log(show"Received event $pme")).void + logger.trace(show"Received event $pme").toTask.void ) .completedL .startAndForget @@ -259,7 +262,8 @@ class MainAppDelegate( _ <- level.addToGame(rootNode, physicsSpace) playerActor <- createPlayerController() // .onErrorRestart(3) - _ <- wire[GameInputHandler.Props].begin + // _ <- wire[GameInputHandler.Props].begin + _ <- new InputMappings(new jme.InputManager(inputManager)).setup // .onErrorRestart(3) johnActor <- createTestNpc("John") // _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20)) @@ -394,7 +398,7 @@ class MainAppDelegate( new Label("Health") { textFill = Color.White }, new Label("100") { text <-- statsObs - .doOnNextF(i => loggerL.debug(show"Received stats: $i")) + .doOnNextF(i => loggerL.trace(show"Received stats: $i")) .map(_.hp.toInt.toString) textFill = Color.White }, @@ -407,11 +411,7 @@ class MainAppDelegate( c += statsObs .scanEval(me.Task.pure("green-bar")) { case (a, b) => - me.Task { - pprint.log(show"Received $a $b") - pprint.log(show"${styleClass.toString}") - styleClass.removeAll(a) - } >> + me.Task(styleClass.removeAll(a)) >> me.Task.pure( (b.hp.toInt: @switch) match { case v if v > 80 => "green-bar" @@ -435,7 +435,7 @@ class MainAppDelegate( new Label("100") { textFill = Color.White text <-- statsObs - .doOnNextF(i => loggerL.debug(show"Received stats: $i")) + .doOnNextF(i => loggerL.trace(show"Received stats: $i")) .map(_.stamina.toInt.toString) }, new JFXProgressBar { @@ -449,11 +449,7 @@ class MainAppDelegate( c += statsObs .scanEval(me.Task.pure("green-bar")) { case (a, b) => - me.Task { - pprint.log(show"Received $a $b") - pprint.log(show"${styleClass.toString}") - styleClass.removeAll(a) - } >> + me.Task(styleClass.removeAll(a)) >> me.Task.pure( (b.stamina.toInt: @switch) match { case v if v > 80 => "green-bar" @@ -546,9 +542,22 @@ class MainAppDelegate( .toIO .hideErrors .startAndForget + sched <- UIO.pure(schedulers.async) fxSched <- UIO.pure(schedulers.fx) playerActor <- wire[PlayerController.Props].create + playerMovementReducer = new PlayerMovementReducer(playerActor, loggerL) + _ <- + inputManager + .enumObservableAction(PlayerMovementInput) + .sample(1.millis) + .scanEval(me.Task.pure(PlayerMovementReducer.State.empty))( + playerMovementReducer.value + ) + .completedL + .toIO + .hideErrors + .startAndForget } yield playerActor } diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala b/src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala index 650d919..0800d20 100644 --- a/src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala +++ b/src/main/scala/wow/doge/mygame/game/subsystems/input/GameInputHandler.scala @@ -18,6 +18,7 @@ import wow.doge.mygame.subsystems.events.PlayerCameraEvent import wow.doge.mygame.subsystems.events.PlayerEvent import wow.doge.mygame.subsystems.events.PlayerMovementEvent import wow.doge.mygame.utils.IOUtils._ +import wow.doge.mygame.utils.methodName object GameInputHandler { @@ -29,16 +30,16 @@ object GameInputHandler { ) { def begin = for { - _ <- UIO(setupMovementKeys(inputManager)) + _ <- UIO(setupMovementKeys) // _ <- UIO(setupAnalogMovementKeys) _ <- UIO(setupCameraKeys()) _ <- toIO( me.Task.parSequence( Seq( - playerMovementInputEventsGenerator( - inputManager, - playerEventBus - ).completedL, + // playerMovementInputEventsGenerator( + // inputManager, + // playerEventBus + // ).completedL, // generateAnalogMovementEvents( // inputManager, // playerEventBus @@ -55,7 +56,7 @@ object GameInputHandler { ).startAndForget } yield () - def setupMovementKeys(inputManager: InputManager) = + def setupMovementKeys = inputManager.withEnumMappings(PlayerMovementInput) { case PlayerMovementInput.WalkRight => new KeyTrigger(KeyInput.KEY_D) :: Nil @@ -128,9 +129,6 @@ object GameInputHandler { } - def methodName(implicit enclosing: sourcecode.Enclosing) = - enclosing.value.split(" ")(0).split("""\.""").last - def playerMovementInputEventsGenerator( inputManager: InputManager, playerEventBus: GameEventBus[PlayerEvent] diff --git a/src/main/scala/wow/doge/mygame/game/subsystems/input/InputMappings.scala b/src/main/scala/wow/doge/mygame/game/subsystems/input/InputMappings.scala new file mode 100644 index 0000000..b00c67c --- /dev/null +++ b/src/main/scala/wow/doge/mygame/game/subsystems/input/InputMappings.scala @@ -0,0 +1,87 @@ +package wow.doge.mygame.game.subsystems.input + +import com.jme3.input.KeyInput +import com.jme3.input.MouseInput +import com.jme3.input.controls.KeyTrigger +import com.jme3.input.controls.MouseAxisTrigger +import monix.{eval => me} +import wow.doge.mygame.implicits._ +import wow.doge.mygame.utils.wrappers.jme.InputManager + +class InputMappings(inputManager: InputManager) { + + def setup = + for { + _ <- setupMovementKeys + _ <- setupAnalogMovementKeys + _ <- setupCameraKeys + _ <- cursorToggle + } yield () + + def setupMovementKeys = + inputManager.withEnumMappings(PlayerMovementInput) { + case PlayerMovementInput.WalkRight => + new KeyTrigger(KeyInput.KEY_D) :: Nil + case PlayerMovementInput.WalkLeft => + new KeyTrigger(KeyInput.KEY_A) :: Nil + case PlayerMovementInput.WalkForward => + new KeyTrigger(KeyInput.KEY_W) :: Nil + case PlayerMovementInput.WalkBackward => + new KeyTrigger(KeyInput.KEY_S) :: Nil + case PlayerMovementInput.Jump => + new KeyTrigger(KeyInput.KEY_SPACE) :: Nil + } + + def setupAnalogMovementKeys = + inputManager.withEnumMappings(PlayerAnalogMovementInput) { + case PlayerAnalogMovementInput.TurnRight => + Seq(new KeyTrigger(KeyInput.KEY_D)) + case PlayerAnalogMovementInput.TurnLeft => + Seq(new KeyTrigger(KeyInput.KEY_A)) + } + + def setupCameraKeys = + inputManager.withEnumMappings(PlayerCameraInput) { + case PlayerCameraInput.CameraRotateLeft => + Seq( + new KeyTrigger(KeyInput.KEY_LEFT), + new MouseAxisTrigger(MouseInput.AXIS_X, false) + ) + case PlayerCameraInput.CameraRotateRight => + Seq( + new KeyTrigger(KeyInput.KEY_RIGHT), + new MouseAxisTrigger(MouseInput.AXIS_X, true) + ) + case PlayerCameraInput.CameraRotateUp => + Seq( + new KeyTrigger(KeyInput.KEY_UP), + new MouseAxisTrigger(MouseInput.AXIS_Y, false) + ) + case PlayerCameraInput.CameraRotateDown => + Seq( + new KeyTrigger(KeyInput.KEY_DOWN), + new MouseAxisTrigger(MouseInput.AXIS_Y, true) + ) + } + + def cursorToggle = + inputManager.withMapping( + MiscInput.ToggleCursor, + new KeyTrigger(KeyInput.KEY_Z) + ) >> + inputManager + .enumEntryObservableAction(MiscInput.ToggleCursor) + .doOnNext(action => + action.binding match { + case MiscInput.ToggleCursor => + if (action.value)(inputManager.cursorVisible = + !inputManager.cursorVisible).toTask + else me.Task.unit + } + ) + .completedL + .toIO + .hideErrors + .startAndForget + +} diff --git a/src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala b/src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala index 8e35dc2..0e6ad77 100644 --- a/src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala +++ b/src/main/scala/wow/doge/mygame/utils/MonixDirectoryWatcher.scala @@ -40,19 +40,16 @@ object MonixDirectoryWatcher { logger = logger.underlying ) { override def onCreate(file: File, count: Int) = - // println(show"$file got created") if (sub.onNext(CreateEvent(file, count)) == Ack.Stop) c.cancel() - override def onModify(file: File, count: Int) = { - pprint.log(show"${file.toString} got modified $count times") + + override def onModify(file: File, count: Int) = if (sub.onNext(ModifyEvent(file, count)) == Ack.Stop) c.cancel() - } + override def onDelete(file: File, count: Int) = - // println(show"$file got deleted") if (sub.onNext(DeleteEvent(file, count)) == Ack.Stop) c.cancel() - } watcher.start()(scheduler) c := Cancelable(() => watcher.stop()) diff --git a/src/main/scala/wow/doge/mygame/utils/package.scala b/src/main/scala/wow/doge/mygame/utils/package.scala index 30e17e8..29d2714 100644 --- a/src/main/scala/wow/doge/mygame/utils/package.scala +++ b/src/main/scala/wow/doge/mygame/utils/package.scala @@ -1,7 +1,6 @@ package wow.doge.mygame -// import wow.doge.mygame.utils.wrappers.Node - package object utils { -// type AppNode = Node + def methodName(implicit enclosing: sourcecode.Enclosing) = + enclosing.value.split(" ")(0).split("""\.""").last } diff --git a/src/main/scala/wow/doge/mygame/utils/wrappers/jme/InputManager.scala b/src/main/scala/wow/doge/mygame/utils/wrappers/jme/InputManager.scala new file mode 100644 index 0000000..d9ea09d --- /dev/null +++ b/src/main/scala/wow/doge/mygame/utils/wrappers/jme/InputManager.scala @@ -0,0 +1,126 @@ +package wow.doge.mygame.utils.wrappers.jme +import com.jme3.input.controls.InputListener +import com.jme3.input.controls.Trigger +import com.jme3.{input => jmei} +import enumeratum._ +import monix.bio.UIO +import monix.reactive.Observable +import wow.doge.mygame.ActionEvent +import wow.doge.mygame.AnalogEvent +import wow.doge.mygame.EnumActionEvent +import wow.doge.mygame.EnumAnalogEvent +import wow.doge.mygame.implicits._ + +final class InputManager(val delegate: jmei.InputManager) { + + /** + * Create a new mapping to the given triggers. + * + *

+ * The given mapping will be assigned to the given triggers, when + * any of the triggers given raise an event, the listeners + * registered to the mappings will receive appropriate events. + * + * @param mappingName The mapping name to assign. + * @param triggers The triggers to which the mapping is to be registered. + */ + def withMapping(mapping: String, triggers: Trigger*): UIO[Unit] = + UIO(delegate.withMapping(mapping, triggers: _*)).void + + def withMapping[T <: EnumEntry]( + mapping: T, + triggers: Trigger* + ): UIO[Unit] = UIO(delegate.withMapping(mapping, triggers: _*)).void + + def withListener(listener: InputListener, mappings: String*) = + UIO(delegate.withListener(listener, mappings: _*)) + + /** + * Creates new mappings from the values of the given Enum + * + *

+ * The given mapping will be assigned to the given triggers, when + * any of the triggers given raise an event, the listeners + * registered to the mappings will receive appropriate events. + * + * @param mappingName The mapping name to assign. + * @param mappingFn Function from enum values to the sequence of trigers. + * + * @example + * + * {{{ + * + * sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase + * object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] { + * val values = findValues + * case object TurnRight extends PlayerAnalogMovementInput + * case object TurnLeft extends PlayerAnalogMovementInput + * } + * + * { + * inputManager.withEnumMappings(PlayerAnalogMovementInput) { + * case PlayerAnalogMovementInput.TurnRight => + * Seq(new KeyTrigger(KeyInput.KEY_RIGHT)) + * case PlayerAnalogMovementInput.TurnLeft => + * Seq(new KeyTrigger(KeyInput.KEY_LEFT)) + * } + * } + * }}} + */ + def withEnumMappings[T <: EnumEntry]( + mappingEnum: Enum[T] + )(mappingFn: T => Seq[Trigger]) = + UIO(delegate.withEnumMappings(mappingEnum)(mappingFn)) + + /** + * Create an observable which emits the given mappings as elements of an observable + * + * @param mappingNames + * @return Observable of action events + * + * @see [[ActionEvent]] + * @see [[enumObservableAction]] + */ + def observableAction(mappingNames: String*): Observable[ActionEvent] = + delegate.observableAction(mappingNames: _*) + + /** + *

+ * Create an observable which emits the values of the given + * enum as elements of an observable + * + * @param mappingNames + * @return Observable of enum values + * + * @example {{{ + * inputManager + * .enumObservableAction(PlayerMovementInput) + * .doOnNext { action => + * action.binding match { + * case PlayerMovementInput.WalkLeft => Task {/* your actions */} + * } + * } + * }}} + * + * @see [[EnumActionEvent]] + * @see [[enumAnalogObservable]] + */ + def enumObservableAction[T <: EnumEntry]( + mappingEnum: Enum[T] + ): Observable[EnumActionEvent[T]] = + delegate.enumObservableAction(mappingEnum) + + def enumEntryObservableAction[T <: EnumEntry]( + mappingEnumEntry: T + ) = delegate.enumEntryObservableAction(mappingEnumEntry) + + def analogObservable(mappingNames: String*): Observable[AnalogEvent] = + delegate.analogObservable(mappingNames: _*) + + def enumAnalogObservable[T <: EnumEntry]( + mappingEnum: Enum[T] + ): Observable[EnumAnalogEvent[T]] = delegate.enumAnalogObservable(mappingEnum) + + def cursorVisible_=(value: Boolean) = UIO(delegate.setCursorVisible(value)) + def cursorVisible = delegate.isCursorVisible +}