added javafx ui in game

This commit is contained in:
Rohan Sircar 2020-11-26 10:40:31 +05:30
parent c93d0704b5
commit 2d3fea4fd8
41 changed files with 2415 additions and 463 deletions

View File

@ -21,6 +21,7 @@ scalaVersion := "2.13.3"
resolvers += "Jcenter" at "https://jcenter.bintray.com/" resolvers += "Jcenter" at "https://jcenter.bintray.com/"
resolvers += "JME Bintray" at "https://bintray.com/jmonkeyengine/com.jme3" resolvers += "JME Bintray" at "https://bintray.com/jmonkeyengine/com.jme3"
resolvers += "Jitpack" at "https://jitpack.io"
resolvers += Resolver.mavenLocal resolvers += Resolver.mavenLocal
resolvers += Resolver.sonatypeRepo("snapshots") resolvers += Resolver.sonatypeRepo("snapshots")
@ -48,7 +49,7 @@ lazy val root = (project in file(".")).settings(
organization := "wow.doge", organization := "wow.doge",
version := "1.0-SNAPSHOT", version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2", // "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2",
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-core // https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-core
"org.jmonkeyengine" % "jme3-core" % jmeVersion, "org.jmonkeyengine" % "jme3-core" % jmeVersion,
// https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-desktop // https://mvnrepository.com/artifact/org.jmonkeyengine/jme3-desktop
@ -58,23 +59,18 @@ lazy val root = (project in file(".")).settings(
"org.jmonkeyengine" % "jme3-effects" % jmeVersion, "org.jmonkeyengine" % "jme3-effects" % jmeVersion,
"org.jmonkeyengine" % "jme3-plugins" % jmeVersion, "org.jmonkeyengine" % "jme3-plugins" % jmeVersion,
"org.jmonkeyengine" % "jme3-blender" % jmeVersion, "org.jmonkeyengine" % "jme3-blender" % jmeVersion,
// https://mvnrepository.com/artifact/com.github.stephengold/Minie
"com.github.stephengold" % "Minie" % "3.0.0", "com.github.stephengold" % "Minie" % "3.0.0",
// https://mvnrepository.com/artifact/com.simsilica/zay-es
"com.simsilica" % "zay-es" % "1.2.1", "com.simsilica" % "zay-es" % "1.2.1",
"org.typelevel" %% "cats-core" % "2.1.1", "org.typelevel" %% "cats-core" % "2.1.1",
"com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full, "com.lihaoyi" % "ammonite" % "2.2.0" cross CrossVersion.full,
"org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10", "org.jetbrains.kotlin" % "kotlin-main-kts" % "1.4.10",
"org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10", "org.jetbrains.kotlin" % "kotlin-scripting-jsr223" % "1.4.10",
"org.codehaus.groovy" % "groovy-all" % "3.0.6" pomOnly (), "org.codehaus.groovy" % "groovy-all" % "3.0.6" pomOnly (),
// "wow.doge" % "game" % "1.0-SNAPSHOT",
"org.scalafx" %% "scalafx" % "14-R19", "org.scalafx" %% "scalafx" % "14-R19",
"com.typesafe.akka" %% "akka-actor-typed" % "2.6.10", "com.typesafe.akka" %% "akka-actor-typed" % "2.6.10",
// "ch.qos.logback" % "logback-classic" % "1.2.3",
"org.typelevel" %% "cats-core" % "2.1.1", "org.typelevel" %% "cats-core" % "2.1.1",
"org.typelevel" %% "cats-effect" % "2.1.4", "org.typelevel" %% "cats-effect" % "2.1.4",
"io.monix" %% "monix" % "3.2.2", "io.monix" %% "monix" % "3.2.2",
// "io.monix" %% "monix-bio" % "1.0.0",
"io.monix" %% "monix-bio" % "1.1.0", "io.monix" %% "monix-bio" % "1.1.0",
"io.circe" %% "circe-core" % "0.13.0", "io.circe" %% "circe-core" % "0.13.0",
"io.circe" %% "circe-generic" % "0.13.0", "io.circe" %% "circe-generic" % "0.13.0",
@ -85,15 +81,21 @@ lazy val root = (project in file(".")).settings(
"com.github.valskalla" %% "odin-monix" % "0.8.1", "com.github.valskalla" %% "odin-monix" % "0.8.1",
"com.github.valskalla" %% "odin-json" % "0.9.1", "com.github.valskalla" %% "odin-json" % "0.9.1",
"com.softwaremill.macwire" %% "util" % "2.3.7", "com.softwaremill.macwire" %% "util" % "2.3.7",
"com.softwaremill.macwire" %% "macros" % "2.3.6" % "provided", "com.softwaremill.macwire" %% "macros" % "2.3.7" % "provided",
"com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided", // "com.softwaremill.macwire" %% "macrosakka" % "2.3.6" % "provided",
"com.github.valskalla" %% "odin-slf4j" % "0.8.1", "com.github.valskalla" %% "odin-slf4j" % "0.8.1",
"com.softwaremill.quicklens" %% "quicklens" % "1.6.1", "com.softwaremill.quicklens" %% "quicklens" % "1.6.1",
"org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1", "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0-RC1",
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2", "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
"io.circe" %% "circe-config" % "0.8.0", "io.circe" %% "circe-config" % "0.8.0",
"com.beachape" %% "enumeratum-circe" % "1.6.1", "com.beachape" %% "enumeratum-circe" % "1.6.1",
"com.lihaoyi" %% "os-lib" % "0.7.1" "com.lihaoyi" %% "os-lib" % "0.7.1",
// "com.jayfella" % "jme-jfx-11" % "1.1.5",
"com.github.goxr3plus" % "FX-BorderlessScene" % "4.4.0",
"com.github.Oshan96" % "CustomStage" % "v1.3.1",
"com.badlogicgames.gdx" % "gdx-ai" % "1.8.2",
"org.recast4j" % "recast" % "1.2.5",
"org.recast4j" % "detour" % "1.2.5"
), ),
// Determine OS version of JavaFX binaries // Determine OS version of JavaFX binaries

BIN
lib/jme-jfx-11-1.1.5.jar Normal file

Binary file not shown.

View File

@ -0,0 +1,10 @@
package com.jayfella.jme
package object jfx {
// object JavaFxUI {
// def apply(app: Application) = {
// JavaFxUI.initialize(app)
// JavaFxUI.getInstance()
// }
// }
}

View File

@ -4,7 +4,8 @@ import com.jme3.input.Action
package object animation { package object animation {
implicit class AnimChannelWrap(private val uval: AnimChannel) extends AnyVal { implicit final class AnimChannelWrap(private val uval: AnimChannel)
extends AnyVal {
/** /**
* Set the current animation that is played by this AnimChannel. * Set the current animation that is played by this AnimChannel.
@ -28,12 +29,14 @@ package object animation {
* with the old one. If zero, then no blending will occur and the new * with the old one. If zero, then no blending will occur and the new
* animation will be applied instantly. * animation will be applied instantly.
*/ */
def setAnim(action: Action, blendTime: Float): Unit = uval.setAnim(action.name, blendTime) def setAnim(action: Action, blendTime: Float): Unit =
uval.setAnim(action.name, blendTime)
} }
implicit class AnimEventListenerWrap(private val uval: AnimEventListener) extends AnyVal { implicit final class AnimEventListenerWrap(
private val uval: AnimEventListener
) extends AnyVal {
/** /**
* Invoked when an animation "cycle" is done. For non-looping animations, * Invoked when an animation "cycle" is done. For non-looping animations,
@ -44,7 +47,11 @@ package object animation {
* @param channel The channel being altered * @param channel The channel being altered
* @param action The new animation action that is done. * @param action The new animation action that is done.
*/ */
def onAnimCycleDone(control: AnimControl, channel: AnimChannel, action: Action): Unit = def onAnimCycleDone(
control: AnimControl,
channel: AnimChannel,
action: Action
): Unit =
uval.onAnimCycleDone(control, channel, action.name) uval.onAnimCycleDone(control, channel, action.name)
/** /**
@ -54,9 +61,12 @@ package object animation {
* @param channel The channel being altered * @param channel The channel being altered
* @param action The new animation action set. * @param action The new animation action set.
*/ */
def onAnimChange(control: AnimControl, channel: AnimChannel, action: Action): Unit = def onAnimChange(
control: AnimControl,
channel: AnimChannel,
action: Action
): Unit =
uval.onAnimChange(control, channel, action.name) uval.onAnimChange(control, channel, action.name)
} }
} }

View File

@ -5,12 +5,13 @@ package com.jme3
*/ */
package object app { package object app {
implicit class SimpleApplicationWrap(private val uval: SimpleApplication) extends AnyVal { implicit final class SimpleApplicationWrap(
private val uval: SimpleApplication
) extends AnyVal {
//FIXME: proof of concept, remove later //FIXME: proof of concept, remove later
def testWrap: String = uval.hashCode().toString def testWrap: String = uval.hashCode().toString
} }
} }

View File

@ -5,7 +5,8 @@ package com.jme3.input
*/ */
package object controls { package object controls {
implicit class ActionListenerWrap(private val uval: ActionListener) extends AnyVal { implicit final class ActionListenerWrap(private val uval: ActionListener)
extends AnyVal {
/** /**
* Called when an input to which this listener is registered to is invoked. * Called when an input to which this listener is registered to is invoked.
@ -18,7 +19,8 @@ package object controls {
uval.onAction(action.name, keyPressed, tpf) uval.onAction(action.name, keyPressed, tpf)
} }
implicit class AnalogListenerWrap(private val uval: AnalogListener) extends AnyVal { implicit final class AnalogListenerWrap(private val uval: AnalogListener)
extends AnyVal {
/** /**
* Called to notify the implementation that an analog event has occurred. * Called to notify the implementation that an analog event has occurred.

View File

@ -8,7 +8,8 @@ import com.jme3.input.controls.Trigger
*/ */
package object input { package object input {
implicit class InputManagerWrap(private val uval: InputManager) extends AnyVal { implicit final class InputManagerWrap(private val uval: InputManager)
extends AnyVal {
def addMapping(action: Action, triggers: Trigger*): Unit = def addMapping(action: Action, triggers: Trigger*): Unit =
uval.addMapping(action.name, triggers: _*) uval.addMapping(action.name, triggers: _*)
def addListener(listener: InputListener, actions: Action*): Unit = def addListener(listener: InputListener, actions: Action*): Unit =

View File

@ -33,14 +33,14 @@ package object scene {
def apply(name: String): Node = new Node(name) def apply(name: String): Node = new Node(name)
} }
implicit class NodeWrap(private val uval: Node) extends AnyVal { implicit final class NodeWrap(private val uval: Node) extends AnyVal {
def getControlMaybe[T <: Control](controlType: Class[T]): Option[T] = def getControlMaybe[T <: Control](controlType: Class[T]): Option[T] =
Option(uval.getControl(controlType)) Option(uval.getControl(controlType))
} }
implicit class SpatialWrap(private val uval: Spatial) extends AnyVal { implicit final class SpatialWrap(private val uval: Spatial) extends AnyVal {
def toNode: Either[ClassCastException, Node] = def toNode: Either[ClassCastException, Node] =
uval match { uval match {

View File

@ -69,6 +69,9 @@ class StaticLoggerBinder extends OdinLoggerBinder[IO] {
case asyncHttpClient case asyncHttpClient
if asyncHttpClient.startsWith("org.asynchttpclient.netty") => if asyncHttpClient.startsWith("org.asynchttpclient.netty") =>
defaultConsoleLogger.withMinimalLevel(Level.Warn) defaultConsoleLogger.withMinimalLevel(Level.Warn)
case s if s.startsWith("com.jayfella.jme.jfx.util.JfxPlatform") =>
defaultConsoleLogger.withMinimalLevel(Level.Info)
// case s // case s
// if s.startsWith( // if s.startsWith(
// "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler" // "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler"

View File

@ -2,7 +2,6 @@ package wow.doge.mygame
import scala.concurrent.duration._ import scala.concurrent.duration._
import _root_.monix.bio.Task
import akka.util.Timeout import akka.util.Timeout
import cats.effect.ExitCode import cats.effect.ExitCode
import cats.implicits._ import cats.implicits._
@ -11,17 +10,19 @@ import io.odin._
import io.odin.json.Formatter import io.odin.json.Formatter
import io.odin.syntax._ import io.odin.syntax._
import wow.doge.mygame.game.GameAppResource import wow.doge.mygame.game.GameAppResource
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
import _root_.monix.bio.BIOApp import _root_.monix.bio.BIOApp
import _root_.monix.bio.Task
import _root_.monix.bio.UIO import _root_.monix.bio.UIO
import cats.effect.Resource import cats.effect.Resource
import scalafx.scene.control.TextArea
import wow.doge.mygame.utils.GenericConsoleStream
object Main extends BIOApp with MainModule { object Main extends BIOApp with MainModule {
import java.util.logging.{Logger => JLogger, Level} import java.util.logging.{Logger => JLogger, Level}
JLogger.getLogger("").setLevel(Level.SEVERE) JLogger.getLogger("").setLevel(Level.SEVERE)
implicit val timeout = Timeout(1.second) implicit val timeout = Timeout(1.second)
def appResource = def appResource(consoleStream: GenericConsoleStream[TextArea]) =
for { for {
logger <- logger <-
consoleLogger().withAsync(timeWindow = 1.milliseconds) |+| consoleLogger().withAsync(timeWindow = 1.milliseconds) |+|
@ -31,89 +32,38 @@ object Main extends BIOApp with MainModule {
).withAsync(timeWindow = 1.milliseconds) ).withAsync(timeWindow = 1.milliseconds)
jmeScheduler <- jMESchedulerResource jmeScheduler <- jMESchedulerResource
actorSystem <- actorSystemResource2(logger) actorSystem <- actorSystemResource2(logger)
scriptCacheActor <- new ScriptSystemResource(os.pwd, actorSystem)(
timeout,
actorSystem.scheduler
).make
// akkaScheduler = actorSystemResource2.scheduler
// consoleTextArea <- Resource.liftF(Task(new TextArea())) // consoleTextArea <- Resource.liftF(Task(new TextArea()))
// consoleStream <- wireWith(JFXConsoleStream.textAreaStream _) // consoleStream <- wireWith(JFXConsoleStream.textAreaStream _)
(gameApp) <- { gameApp <- {
// new BulletAppState() // new BulletAppState()
// bas.setThreadingType(Thr) // bas.setThreadingType(Thr)
// gameAppResource(new StatsAppState()) // gameAppResource(new StatsAppState())
wire[GameAppResource].get2 wire[GameAppResource].get
} }
_ <- Resource.liftF( _ <- Resource.liftF(
new MainApp(logger, gameApp, actorSystem, jmeScheduler)( new MainApp(
logger,
gameApp,
actorSystem,
jmeScheduler,
schedulers,
consoleStream
)(
timeout, timeout,
actorSystem.scheduler actorSystem.scheduler
).gameInit ).program
) )
// fib <- Resource.liftF(
// gameApp
// .enqueueL(() =>
// new MainApp(logger, gameApp, actorSystem, jmeScheduler)(
// timeout,
// actorSystem.scheduler
// )
// )
// .start
// )
// _ <- Resource.liftF(fib.join.flatMap(_.gameInit)
// app = gameApp
// inputManager = gameApp.inputManager
// assetManager = gameApp.assetManager
// bulletAppState = new BulletAppState()
// (playerMovementEventBus, playerCameraEventBus) <- new EventsModule2(
// actorSystem
// ).resource
// b1 = playerMovementEventBus
// b2 = playerCameraEventBus
// playerPos = ImVector3f.ZERO
// playerNode = None.taggedWith[Player]
// modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o".taggedWith[Player]
// playerController <- Resource.liftF {
// implicit val s = actorSystem.scheduler
// wire[PlayerController.Props].create.onErrorHandle(err =>
// logger.error(err.toString())
// )
// }
// gameSystemsInitializerFib <- Resource.make(
// logger.info("creating game systems initializer") >>
// gameApp
// .enqueueL(() => wire[GameSystemsInitializer])
// .start
// )(c => logger.info("destroying game systems initializer") >> c.cancel)
// _ <- Resource.liftF(gameSystemsInitializerFib.join.flatMap(_.init))
} yield () } yield ()
// def createPlayerController(
// playerMovementEventBus: ActorRef[
// EventBus.Command[PlayerMovementEvent]
// ],
// playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]]
// ): IO[PlayerController.Error, Unit] = {
// val playerPos = ImVector3f.ZERO
// val playerNode = None.taggedWith[Player]
// val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
// wire[PlayerController.Props].create
// }
def run(args: List[String]): UIO[ExitCode] = { def run(args: List[String]): UIO[ExitCode] = {
// Console.withOut( lazy val consoleStream = GenericConsoleStream.textAreaStream()
// new JFXConsoleStream( Console.withOut(consoleStream)(
// new scalafx.scene.control.TextArea(), appResource(consoleStream)
// new ByteArrayOutputStream(35)
// )
// )(())
appResource
.use(_ => Task.unit) .use(_ => Task.unit)
.onErrorHandle(_.printStackTrace()) .onErrorHandle(_.printStackTrace())
.as(ExitCode.Success) .as(ExitCode.Success)
)
} }
} }

View File

@ -20,7 +20,7 @@ import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import wow.doge.mygame.events.EventBus import wow.doge.mygame.events.EventBus
import wow.doge.mygame.game.GameApp2 import wow.doge.mygame.game.GameApp2
import wow.doge.mygame.game.nodes.Player import wow.doge.mygame.game.nodes.PlayerTag
import wow.doge.mygame.game.nodes.PlayerController import wow.doge.mygame.game.nodes.PlayerController
import wow.doge.mygame.game.subsystems.input.GameInputHandler import wow.doge.mygame.game.subsystems.input.GameInputHandler
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
@ -32,12 +32,32 @@ import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
import com.jme3.renderer.ViewPort import com.jme3.renderer.ViewPort
import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource import wow.doge.mygame.subsystems.scriptsystem.ScriptSystemResource
import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode import wow.doge.mygame.subsystems.scriptsystem.ScriptInitMode
import wow.doge.mygame.launcher.Launcher
import wow.doge.mygame.executors.Schedulers
import scalafx.application.JFXApp.PrimaryStage
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.Button
import scalafx.scene.layout.StackPane
import scalafx.scene.paint.Color
import scalafx.scene.shape.Rectangle
import scalafx.Includes._
import scala.concurrent.duration._
import cats.effect.concurrent.Deferred
import monix.bio.Fiber
import wow.doge.mygame.launcher.Launcher.LauncherResult
import scalafx.scene.control.TextArea
import com.jayfella.jme.jfx.JavaFxUI
import wow.doge.mygame.utils.GenericConsoleStream
import java.io.PrintStream
class MainApp( class MainApp(
logger: Logger[Task], logger: Logger[Task],
gameApp: GameApp2, gameApp: GameApp2,
spawnProtocol: ActorSystem[SpawnProtocol.Command], spawnProtocol: ActorSystem[SpawnProtocol.Command],
jmeThread: monix.execution.Scheduler jmeThread: monix.execution.Scheduler,
schedulers: Schedulers,
consoleStream: GenericConsoleStream[TextArea]
)(implicit )(implicit
@annotation.unused timeout: Timeout, @annotation.unused timeout: Timeout,
@annotation.unused scheduler: Scheduler @annotation.unused scheduler: Scheduler
@ -46,19 +66,21 @@ class MainApp(
lazy val scriptSystemInit = lazy val scriptSystemInit =
new ScriptSystemResource(os.pwd, spawnProtocol, ScriptInitMode.Eager).init new ScriptSystemResource(os.pwd, spawnProtocol, ScriptInitMode.Eager).init
lazy val gameInit: Task[Unit] = for { def gameInit: Task[Fiber[Throwable, Unit]] =
for {
eventsModule <- Task(new EventsModule2(spawnProtocol)) eventsModule <- Task(new EventsModule2(spawnProtocol))
playerMovementEventBus <- eventsModule.playerMovementEventBusTask playerMovementEventBus <- eventsModule.playerMovementEventBusTask
playerCameraEventBus <- eventsModule.playerCameraEventBusTask playerCameraEventBus <- eventsModule.playerCameraEventBusTask
gameAppFib <- gameApp.start.executeOn(jmeThread).start gameAppFib <- gameApp.start.executeOn(jmeThread).start
/** /**
* schedule a fiber to run on the JME thread and wait for it's completion * schedule a task to run on the JME thread and wait for it's completion
* before proceeding forward, as a signal that JME thread has been * before proceeding forward, as a signal that JME thread has been
* initialized, otherwise we'll get NPEs trying to access the fields * initialized, otherwise we'll get NPEs trying to access the fields
* of the game app * of the game app
*/ */
initFib <- gameApp.enqueueL(() => Task("done")).start res <- gameApp.enqueueL(() => Task("done")).flatten
_ <- initFib.join
_ <- logger.info(s"Result = $res")
inputManager <- gameApp.inputManager inputManager <- gameApp.inputManager
assetManager <- gameApp.assetManager assetManager <- gameApp.assetManager
stateManager <- gameApp.stateManager stateManager <- gameApp.stateManager
@ -66,19 +88,74 @@ class MainApp(
rootNode <- gameApp.rootNode rootNode <- gameApp.rootNode
enqueueR <- Task(gameApp.enqueue _) enqueueR <- Task(gameApp.enqueue _)
viewPort <- gameApp.viewPort viewPort <- gameApp.viewPort
_ <- logger.info("before")
// jfxUI <- Task(JavaFxUI.initialize(gameApp.app))
// .executeOn(gameApp.scheduler) >> Task.sleep(500.millis) >> Task(
// JavaFxUI.getInstance()
// )
// .start
jfxUI <- gameApp.jfxUI
consoleTextArea <- Task(new TextArea {
text = "hello"
editable = false
wrapText = true
// maxHeight = 150
// maxWidth = 300
})
_ <- Task(consoleStream := consoleTextArea)
_ <- Task(jfxUI += consoleTextArea)
// consoleStream <- Task(
// GenericConsoleStream.textAreaStream(consoleTextArea)
// )
_ <- logger.info("after")
bulletAppState <- Task(new BulletAppState()) bulletAppState <- Task(new BulletAppState())
appScheduler <- Task(gameApp.scheduler) _ <- Task(stateManager.attach(bulletAppState))
// enqueueL <- Task(gameApp.enqueueL _) _ <- logger.info("Initializing console stream")
// _ <- Task(GenericConsoleStream.textAreaStream(consoleTextArea)).bracket(
// consoleStream =>
// Task { System.setOut(consoleStream) } >> wire[MainAppDelegate]
// .init(gameApp.scheduler, consoleStream)
// // consoleLogger
// // Console.withOut(consoleStream)(
// // wire[MainAppDelegate].init(gameApp.scheduler, consoleStream)
// // )
// )(stream => Task(stream.close()).onErrorHandle(_.printStackTrace()))
// _ <- Task { System.setOut(new PrintStream(consoleStream, true)) }
// _ <- Task {
// Console.withOut(consoleStream)(println("hello"))
// }
_ <- wire[MainAppDelegate].init(gameApp.scheduler) _ <- wire[MainAppDelegate].init(gameApp.scheduler)
_ <- gameAppFib.join } yield (gameAppFib)
} yield ()
lazy val program = for { lazy val program = for {
scriptSystem <- scriptSystemInit scriptSystem <- scriptSystemInit
game <- gameInit /**
* Signal for synchronization between the JavaFX launcher and the in-game JavaFX GUI
* Without this, we get a "Toolkit already initialized" excResult. The launch button
* in the launcher completes the signal. The game init process which listens for this
* signal can then continue
*/
launchSignal <- Deferred[Task, Launcher.LauncherResult]
launcher <- new Launcher.Props(schedulers, launchSignal).create
cancelToken <- launcher.init()
launchResult <- launchSignal.get
_ <- cancelToken.cancel
_ <-
if (launchResult == LauncherResult.Exit)
logger.info("Exiting")
else gameInit.flatMap(_.join)
// _ <- Task.sleep(2000.millis)
// gameAppFib <- gameInit
/**
* Wait for game window to close
*/
// _ <- gameAppFib.join
} yield () } yield ()
} }
/**
* Class with all dependencies in one place for easy wiring
*/
class MainAppDelegate( class MainAppDelegate(
gameApp: GameApp2, gameApp: GameApp2,
spawnProtocol: ActorSystem[SpawnProtocol.Command], spawnProtocol: ActorSystem[SpawnProtocol.Command],
@ -101,16 +178,20 @@ class MainAppDelegate(
@annotation.unused scheduler: Scheduler @annotation.unused scheduler: Scheduler
) { ) {
def init(appScheduler: monix.execution.Scheduler) = def init(
appScheduler: monix.execution.Scheduler
// consoleStream: GenericConsoleStream[TextArea]
) =
for { for {
_ <- loggerL.info("Initializing Systems") _ <- loggerL.info("Initializing Systems")
_ <- Task(stateManager.attach(bulletAppState))
_ <- Task( _ <- Task(
assetManager.registerLocator( assetManager.registerLocator(
(os.rel / "assets" / "town.zip"), (os.rel / "assets" / "town.zip"),
classOf[ZipLocator] classOf[ZipLocator]
) )
) )
_ <- loggerL.info("test hmm")
// _ <- Task(consoleStream.println("text"))
_ <- DefaultGameLevel(assetManager, viewPort) _ <- DefaultGameLevel(assetManager, viewPort)
.addToGame( .addToGame(
rootNode, rootNode,
@ -131,7 +212,7 @@ class MainAppDelegate(
@annotation.unused @annotation.unused
val playerPos = ImVector3f.ZERO val playerPos = ImVector3f.ZERO
@annotation.unused @annotation.unused
val playerNode = None.taggedWith[Player] val playerNode = None.taggedWith[PlayerTag]
@annotation.unused @annotation.unused
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
wire[PlayerController.Props].create wire[PlayerController.Props].create

View File

@ -16,81 +16,26 @@ import wow.doge.mygame.executors.GUIExecutorService
import wow.doge.mygame.executors.Schedulers import wow.doge.mygame.executors.Schedulers
class GameApp( class GameApp(
// actorSystem: ActorSystem[SpawnProtocol.Command],
schedulers: Schedulers, schedulers: Schedulers,
appStates: AppState* appStates: AppState*
) extends SimpleApplication(appStates: _*) { ) extends SimpleApplication(appStates: _*) {
import GameApp._ import GameApp._
// implicit val timeout = Timeout(10.seconds)
// implicit val scheduler = actorSystem.scheduler
// private lazy val taskQueueS = new ConcurrentLinkedQueue[MyTask[_]]() /**
* A non blocking synchronized queue using an immutable scala queue and monix's atomic class
*/
private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]]) private lazy val taskQueue2 = Atomic(Queue.empty[MyTask[_]])
private val tickSubject = private val tickSubject =
ConcurrentSubject[Float](multicast = MulticastStrategy.publish)( ConcurrentSubject[Float](multicast = MulticastStrategy.publish)(
schedulers.async schedulers.async
) )
// (scheduler)
override def simpleInitApp(): Unit = {
println("gameapp" + Thread.currentThread().getName())
// val ship = ed.createEntity()
// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData())
// val mbState = Try(
// stateManager()
// .state[TestAppState]()
// .entity
// ).toOption.flatten.toRight(println("empty"))
// // .flatMap(_.entity)
// val x = mbState.flatMap(
// _.query
// .filter[TestComponent]("name", new Object())
// // .filterOr[TestEntity](
// // Filters
// // .fieldEquals(classOf[TestEntity], "", null)
// // )
// .component[Tag]()
// .component[TestComponent]()
// .result
// .toRight(println("failed"))
// )
// rootNode
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// Future(println("hello"))(jmeEC(this))
// val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask(
// SpawnProtocol.Spawn(
// behavior = Greeter(),
// name = "listener",
// DispatcherSelector.fromConfig("jme-dispatcher"),
// _
// )
// )
// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println))
}
def tickObservable: Observable[Float] = tickSubject def tickObservable: Observable[Float] = tickSubject
override def simpleUpdate(tpf: Float): Unit = { override def simpleInitApp(): Unit = {}
// val rot2 = rot.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1))
// val rotation = geom.getLocalRotation()
// rotation.add(rot2)
// geom.rotate(rot2)
// geom.updateModelBound() override def simpleUpdate(tpf: Float): Unit = {
// geom.updateGeometricState()
tickSubject.onNext(tpf) tickSubject.onNext(tpf)
} }
@ -101,32 +46,14 @@ class GameApp(
def enqueueScala[T](cb: () => T): CancelableFuture[T] = { def enqueueScala[T](cb: () => T): CancelableFuture[T] = {
val p = Promise[T]() val p = Promise[T]()
// p.success(cb())
// taskQueueS.add(MyTask(p, cb))
taskQueue2.transform(_ :+ MyTask(p, cb)) taskQueue2.transform(_ :+ MyTask(p, cb))
p.future p.future
} }
// taskQueue2.transform(_ :+ MyTask(p, cb))
// p
def enqueueL[T](cb: () => T): Task[T] = def enqueueL[T](cb: () => T): Task[T] =
// Task(Promise[T]()).flatMap { p =>
// Task(taskQueue2.transform(_ :+ MyTask(p, cb))) >>
// Task.fromCancelablePromise(p)
// }
// Task.fromCancelablePromise {
// val p = Promise[T]()
// taskQueue2.transform(_ :+ MyTask(p, cb))
// p
// }
Task.deferFuture(enqueueScala(cb)) Task.deferFuture(enqueueScala(cb))
// taskQueueS.add(MyTask(p, cb))
override protected def runQueuedTasks(): Unit = { override protected def runQueuedTasks(): Unit = {
// Option(taskQueueS.poll()).foreach {
// case MyTask(p, cb) =>
// p.success(cb())
// }
taskQueue2.transform { current => taskQueue2.transform { current =>
current.dequeueOption.fold(current) { current.dequeueOption.fold(current) {
case (MyTask(p, cb), updated) => case (MyTask(p, cb), updated) =>
@ -139,8 +66,9 @@ class GameApp(
} }
object JMEExecutorService extends GUIExecutorService { object JMEExecutorService extends GUIExecutorService {
override def execute(command: Runnable) = override def execute(command: Runnable): Unit =
enqueue(command) enqueueScala(() => command.run())
// enqueue(command)
// new SingleThreadEventExecutor() // new SingleThreadEventExecutor()
// sys.addShutdownHook(JMEExecutorService.shutdown()) // sys.addShutdownHook(JMEExecutorService.shutdown())
} }
@ -150,3 +78,55 @@ class GameApp(
object GameApp { object GameApp {
private[game] case class MyTask[T](p: Promise[T], cb: () => T) private[game] case class MyTask[T](p: Promise[T], cb: () => T)
} }
// val ship = ed.createEntity()
// val mbState = stateManager().state[EntityDataState]().map(_.getEntityData())
// val mbState = Try(
// stateManager()
// .state[TestAppState]()
// .entity
// ).toOption.flatten.toRight(println("empty"))
// // .flatMap(_.entity)
// val x = mbState.flatMap(
// _.query
// .filter[TestComponent]("name", new Object())
// // .filterOr[TestEntity](
// // Filters
// // .fieldEquals(classOf[TestEntity], "", null)
// // )
// .component[Tag]()
// .component[TestComponent]()
// .result
// .toRight(println("failed"))
// )
// rootNode
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// .child(geom)
// Future(println("hello"))(jmeEC(this))
// val wbActor: Future[ActorRef[Greeter.Greet]] = actorSystem.ask(
// SpawnProtocol.Spawn(
// behavior = Greeter(),
// name = "listener",
// DispatcherSelector.fromConfig("jme-dispatcher"),
// _
// )
// )
// wbActor.map(a => a.ask(Greeter.Greet("hello", _)).map(println))
// Task(Promise[T]()).flatMap { p =>
// Task(taskQueue2.transform(_ :+ MyTask(p, cb))) >>
// Task.fromCancelablePromise(p)
// }
// Task.fromCancelablePromise {
// val p = Promise[T]()
// taskQueue2.transform(_ :+ MyTask(p, cb))
// p
// }

View File

@ -6,11 +6,16 @@ import com.jme3.asset.AssetManager
import com.jme3.input.InputManager import com.jme3.input.InputManager
import monix.bio.IO import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import com.jme3.scene.Node
import monix.catnap.Semaphore
import com.jme3.scene.Spatial
import wow.doge.mygame.game.GameApp2.SynchedObject
import wow.doge.mygame.game.subsystems.ui.JFxUI
sealed trait Error sealed trait Error
case object FlyCamNotExists extends Error case object FlyCamNotExists extends Error
class GameApp2(app: GameApp) { class GameApp2(val app: GameApp) {
def stateManager: Task[AppStateManager] = Task(app.getStateManager()) def stateManager: Task[AppStateManager] = Task(app.getStateManager())
def inputManager: Task[InputManager] = Task(app.getInputManager()) def inputManager: Task[InputManager] = Task(app.getInputManager())
def assetManager: Task[AssetManager] = Task(app.getAssetManager()) def assetManager: Task[AssetManager] = Task(app.getAssetManager())
@ -22,6 +27,7 @@ class GameApp2(app: GameApp) {
def camera = Task(app.getCamera()) def camera = Task(app.getCamera())
def viewPort = Task(app.getViewPort()) def viewPort = Task(app.getViewPort())
def rootNode = Ref[Task].of(app.getRootNode()) def rootNode = Ref[Task].of(app.getRootNode())
def rootNode2 = SynchedObject(app.getRootNode())
def enqueue(cb: () => Unit) = def enqueue(cb: () => Unit) =
app.enqueue(new Runnable { app.enqueue(new Runnable {
override def run() = cb() override def run() = cb()
@ -31,5 +37,35 @@ class GameApp2(app: GameApp) {
def start = Task(app.start()) def start = Task(app.start())
def stop = Task(app.stop()) def stop = Task(app.stop())
def scheduler = app.scheduler def scheduler = app.scheduler
def jfxUI = JFxUI(app)
} }
object GameApp2 {
class WrappedNode(node: Node, lock: Semaphore[Task]) {
def +=(spat: Spatial) = lock.withPermit(Task(node.attachChild(spat)))
}
/**
* Synchronization wrapper for a mutable object
*
* @param obj the mutable object
* @param lock lock for synchronization
*/
class SynchedObject[A](obj: A, lock: Semaphore[Task]) {
def modify(f: A => Unit): Task[Unit] =
lock.withPermit(Task(f(obj)))
def flatModify(f: A => Task[Unit]): Task[Unit] =
lock.withPermit(f(obj))
def get: Task[A] = lock.withPermit(Task(obj))
}
object SynchedObject {
def apply[A](obj: A) =
Semaphore[Task](1).flatMap(lock => Task(new SynchedObject(obj, lock)))
}
}

View File

@ -17,7 +17,7 @@ class GameAppResource(
jmeScheduler: Scheduler, jmeScheduler: Scheduler,
schedulers: Schedulers schedulers: Schedulers
) { ) {
def get: Resource[Task, (GameApp2, Fiber[Throwable, Unit])] = def get2: Resource[Task, (GameApp2, Fiber[Throwable, Unit])] =
Resource.make( Resource.make(
for { for {
_ <- logger.info("Creating game app") _ <- logger.info("Creating game app")
@ -37,7 +37,7 @@ class GameAppResource(
} yield (app2 -> fib) } yield (app2 -> fib)
)(logger.info("Closing game app") >> _._2.cancel) )(logger.info("Closing game app") >> _._2.cancel)
def get2: Resource[Task, GameApp2] = def get: Resource[Task, GameApp2] =
Resource.make( Resource.make(
for { for {
_ <- logger.info("Creating game app") _ <- logger.info("Creating game app")
@ -45,9 +45,12 @@ class GameAppResource(
app2 <- Task { app2 <- Task {
val settings = new AppSettings(true) val settings = new AppSettings(true)
settings.setVSync(true) settings.setVSync(true)
settings.setUseInput(true)
// new FlyCamAppState /**
// settings.setFrameRate(250) * disables the launcher
* We'll be making our own launcher anyway
*/
app.setShowSettings(false)
app.setSettings(settings) app.setSettings(settings)
// JMERunner.runner = app // JMERunner.runner = app
new GameApp2(app) new GameApp2(app)

View File

@ -22,7 +22,7 @@ import monix.bio.IO
import monix.bio.Task import monix.bio.Task
import monix.reactive.Consumer import monix.reactive.Consumer
import wow.doge.mygame.events.EventBus import wow.doge.mygame.events.EventBus
import wow.doge.mygame.game.nodes.Player import wow.doge.mygame.game.nodes.PlayerTag
import wow.doge.mygame.game.nodes.PlayerController import wow.doge.mygame.game.nodes.PlayerController
import wow.doge.mygame.game.subsystems.input.GameInputHandler import wow.doge.mygame.game.subsystems.input.GameInputHandler
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
@ -86,7 +86,7 @@ class GameSystemsInitializer(
@annotation.unused @annotation.unused
val playerPos = ImVector3f.ZERO val playerPos = ImVector3f.ZERO
@annotation.unused @annotation.unused
val playerNode = None.taggedWith[Player] val playerNode = None.taggedWith[PlayerTag]
@annotation.unused @annotation.unused
val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o" val modelPath = os.rel / "Models" / "Jaime" / "Jaime.j3o"
wire[PlayerController.Props].create wire[PlayerController.Props].create

View File

@ -32,7 +32,7 @@ import wow.doge.mygame.subsystems.movement.ImMovementActor
import wow.doge.mygame.utils.AkkaUtils import wow.doge.mygame.utils.AkkaUtils
// class PlayerNode(val name: String) extends Node(name) {} // class PlayerNode(val name: String) extends Node(name) {}
sealed trait Player sealed trait PlayerTag
sealed trait PlayerCameraNode sealed trait PlayerCameraNode
object PlayerController { object PlayerController {
@ -54,7 +54,7 @@ object PlayerController {
], ],
playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]], playerCameraEventBus: ActorRef[EventBus.Command[PlayerCameraEvent]],
_playerPhysicsControl: Option[BetterCharacterControl], _playerPhysicsControl: Option[BetterCharacterControl],
_playerNode: Option[Node with Player] = None, _playerNode: Option[Node with PlayerTag] = None,
_cameraNode: Option[CameraNode with PlayerCameraNode] = None, _cameraNode: Option[CameraNode with PlayerCameraNode] = None,
appScheduler: monix.execution.Scheduler appScheduler: monix.execution.Scheduler
)(implicit timeout: Timeout, scheduler: Scheduler) { )(implicit timeout: Timeout, scheduler: Scheduler) {
@ -68,7 +68,7 @@ object PlayerController {
playerPhysicsControl <- IO( playerPhysicsControl <- IO(
_playerPhysicsControl _playerPhysicsControl
.getOrElse(defaultPlayerPhysicsControl) .getOrElse(defaultPlayerPhysicsControl)
.taggedWith[Player] .taggedWith[PlayerTag]
) )
playerNode <- IO.fromEither( playerNode <- IO.fromEither(
_playerNode.fold( _playerNode.fold(
@ -179,7 +179,7 @@ object Methods {
def spawnMovementActor( def spawnMovementActor(
enqueueR: Function1[() => Unit, Unit], enqueueR: Function1[() => Unit, Unit],
spawnProtocol: ActorRef[SpawnProtocol.Command], spawnProtocol: ActorRef[SpawnProtocol.Command],
movable: BetterCharacterControl @@ Player, movable: BetterCharacterControl @@ PlayerTag,
playerMovementEventBus: ActorRef[ playerMovementEventBus: ActorRef[
EventBus.Command[PlayerMovementEvent] EventBus.Command[PlayerMovementEvent]
], ],

View File

@ -22,7 +22,7 @@ object PlayerMovementEventListener {
Logger[PlayerMovementEventListener.type].underlying Logger[PlayerMovementEventListener.type].underlying
), ),
Behaviors.setup[PlayerMovementEvent](ctx => Behaviors.setup[PlayerMovementEvent](ctx =>
Behaviors.receiveMessage { Behaviors.receiveMessagePartial {
case PlayerMovedLeft(pressed) => case PlayerMovedLeft(pressed) =>
movementActor ! ImMovementActor.MovedLeft(pressed) movementActor ! ImMovementActor.MovedLeft(pressed)
Behaviors.same Behaviors.same
@ -69,7 +69,7 @@ object PlayerCameraEventListener {
Logger[PlayerCameraEventListener.type].underlying Logger[PlayerCameraEventListener.type].underlying
), ),
Behaviors.setup[PlayerCameraEvent](ctx => Behaviors.setup[PlayerCameraEvent](ctx =>
Behaviors.receiveMessage { Behaviors.receiveMessagePartial {
case CameraMovedUp => case CameraMovedUp =>
enqueueR(() => { enqueueR(() => {

View File

@ -0,0 +1,33 @@
package wow.doge.mygame.game.subsystems.ai
import com.badlogic.gdx.ai.pfa.Connection
import wow.doge.mygame.game.subsystems.ai.gdx.MyIndexedGraph
import scala.collection.immutable.ArraySeq
// import com.badlogic.gdx.ai.pfa.indexed.IndexedGraph
// import scala.jdk.javaapi.CollectionConverters._
case class City(x: Float, y: Float, name: String, index: Int)
case class Street(fromNode: City, toNode: City, cost: Float)
extends Connection[City] {
override def getCost(): Float = cost
override def getFromNode(): City = fromNode
override def getToNode(): City = toNode
}
class CityGraph extends MyIndexedGraph[City] {
override def getConnections(
city: City
): IndexedSeq[Connection[City]] =
ArraySeq(Street(City(0f, 0f, "egw", 0), City(0f, 0f, "egw", 0), 1))
// or Vector(Street(City(0f, 0f, "egw", 0), City(0f, 0f, "egw", 0), 1))
override def getIndex(city: City): Int = ???
override def getNodeCount(): Int = ???
}

View File

@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright 2014 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package wow.doge.mygame.game.subsystems.ai.gdx;
import com.badlogic.gdx.ai.pfa.Connection;
import com.badlogic.gdx.utils.Array;
// import java.lang.Iterable;
import java.util.List;
import scala.collection.immutable.IndexedSeq;
/**
* A graph is a collection of nodes, each one having a collection of outgoing
* {@link Connection connections}.
*
* @param <N> Type of node
*
* @author davebaol
*/
public interface Graph<N> {
/**
* Returns the connections outgoing from the given node.
*
* @param fromNode the node whose outgoing connections will be returned
* @return the array of connections outgoing from the given node.
*/
public IndexedSeq<Connection<N>> getConnections(N fromNode);
}

View File

@ -0,0 +1,371 @@
/*******************************************************************************
* Copyright 2014 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package wow.doge.mygame.game.subsystems.ai.gdx;
import com.badlogic.gdx.ai.pfa.Connection;
import com.badlogic.gdx.ai.pfa.GraphPath;
import com.badlogic.gdx.ai.pfa.Heuristic;
import com.badlogic.gdx.ai.pfa.PathFinder;
import com.badlogic.gdx.ai.pfa.PathFinderQueue;
import com.badlogic.gdx.ai.pfa.PathFinderRequest;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.BinaryHeap;
import com.badlogic.gdx.utils.TimeUtils;
import java.util.List;
import wow.doge.mygame.game.subsystems.ai.gdx.Graph;
import wow.doge.mygame.game.subsystems.ai.gdx.MyIndexedGraph;
import scala.collection.immutable.IndexedSeq;
/**
* A fully implemented {@link PathFinder} that can perform both interruptible
* and non-interruptible pathfinding.
* <p>
* This implementation is a common variation of the A* algorithm that is faster
* than the general A*.
* <p>
* In the general A* implementation, data are held for each node in the open or
* closed lists, and these data are held as a NodeRecord instance. Records are
* created when a node is first considered and then moved between the open and
* closed lists, as required. There is a key step in the algorithm where the
* lists are searched for a node record corresponding to a particular node. This
* operation is something time-consuming.
* <p>
* The indexed A* algorithm improves execution speed by using an array of all
* the node records for every node in the graph. Nodes must be numbered using
* sequential integers (see {@link MyIndexedGraph#getIndex(Object)}), so we
* don't need to search for a node in the two lists at all. We can simply use
* the node index to look up its record in the array (creating it if it is
* missing). This means that the close list is no longer needed. To know whether
* a node is open or closed, we use the {@link NodeRecord#category category} of
* the node record. This makes the search step very fast indeed (in fact, there
* is no search, and we can go straight to the information we need).
* Unfortunately, we can't get rid of the open list because we still need to be
* able to retrieve the element with the lowest cost. However, we use a
* {@link BinaryHeap} for the open list in order to keep performance as high as
* possible.
*
* @param <N> Type of node
*
* @author davebaol
*/
public class IndexedAStarPathFinder<N> implements PathFinder<N> {
MyIndexedGraph<N> graph;
NodeRecord<N>[] nodeRecords;
BinaryHeap<NodeRecord<N>> openList;
NodeRecord<N> current;
public Metrics metrics;
/** The unique ID for each search run. Used to mark nodes. */
private int searchId;
private static final int UNVISITED = 0;
private static final int OPEN = 1;
private static final int CLOSED = 2;
public IndexedAStarPathFinder(MyIndexedGraph<N> graph) {
this(graph, false);
}
@SuppressWarnings("unchecked")
public IndexedAStarPathFinder(MyIndexedGraph<N> graph, boolean calculateMetrics) {
this.graph = graph;
this.nodeRecords = (NodeRecord<N>[]) new NodeRecord[graph.getNodeCount()];
this.openList = new BinaryHeap<NodeRecord<N>>();
if (calculateMetrics)
this.metrics = new Metrics();
}
@Override
public boolean searchConnectionPath(N startNode, N endNode, Heuristic<N> heuristic,
GraphPath<Connection<N>> outPath) {
// Perform AStar
boolean found = search(startNode, endNode, heuristic);
if (found) {
// Create a path made of connections
generateConnectionPath(startNode, outPath);
}
return found;
}
@Override
public boolean searchNodePath(N startNode, N endNode, Heuristic<N> heuristic, GraphPath<N> outPath) {
// Perform AStar
boolean found = search(startNode, endNode, heuristic);
if (found) {
// Create a path made of nodes
generateNodePath(startNode, outPath);
}
return found;
}
protected boolean search(N startNode, N endNode, Heuristic<N> heuristic) {
initSearch(startNode, endNode, heuristic);
// Iterate through processing each node
do {
// Retrieve the node with smallest estimated total cost from the open list
current = openList.pop();
current.category = CLOSED;
// Terminate if we reached the goal node
if (current.node == endNode)
return true;
visitChildren(endNode, heuristic);
} while (openList.size > 0);
// We've run out of nodes without finding the goal, so there's no solution
return false;
}
@Override
public boolean search(PathFinderRequest<N> request, long timeToRun) {
long lastTime = TimeUtils.nanoTime();
// We have to initialize the search if the status has just changed
if (request.statusChanged) {
initSearch(request.startNode, request.endNode, request.heuristic);
request.statusChanged = false;
}
// Iterate through processing each node
do {
// Check the available time
long currentTime = TimeUtils.nanoTime();
timeToRun -= currentTime - lastTime;
if (timeToRun <= PathFinderQueue.TIME_TOLERANCE)
return false;
// Retrieve the node with smallest estimated total cost from the open list
current = openList.pop();
current.category = CLOSED;
// Terminate if we reached the goal node; we've found a path.
if (current.node == request.endNode) {
request.pathFound = true;
generateNodePath(request.startNode, request.resultPath);
return true;
}
// Visit current node's children
visitChildren(request.endNode, request.heuristic);
// Store the current time
lastTime = currentTime;
} while (openList.size > 0);
// The open list is empty and we've not found a path.
request.pathFound = false;
return true;
}
protected void initSearch(N startNode, N endNode, Heuristic<N> heuristic) {
if (metrics != null)
metrics.reset();
// Increment the search id
if (++searchId < 0)
searchId = 1;
// Initialize the open list
openList.clear();
// Initialize the record for the start node and add it to the open list
NodeRecord<N> startRecord = getNodeRecord(startNode);
startRecord.node = startNode;
startRecord.connection = null;
startRecord.costSoFar = 0;
addToOpenList(startRecord, heuristic.estimate(startNode, endNode));
current = null;
}
protected void visitChildren(N endNode, Heuristic<N> heuristic) {
// Get current node's outgoing connections
IndexedSeq<Connection<N>> connections = graph.getConnections(current.node);
// Loop through each connection in turn
for (int i = 0; i < connections.size(); i++) {
if (metrics != null)
metrics.visitedNodes++;
Connection<N> connection = connections.apply(i);
// Get the cost estimate for the node
N node = connection.getToNode();
float nodeCost = current.costSoFar + connection.getCost();
float nodeHeuristic;
NodeRecord<N> nodeRecord = getNodeRecord(node);
if (nodeRecord.category == CLOSED) { // The node is closed
// If we didn't find a shorter route, skip
if (nodeRecord.costSoFar <= nodeCost)
continue;
// We can use the node's old cost values to calculate its heuristic
// without calling the possibly expensive heuristic function
nodeHeuristic = nodeRecord.getEstimatedTotalCost() - nodeRecord.costSoFar;
} else if (nodeRecord.category == OPEN) { // The node is open
// If our route is no better, then skip
if (nodeRecord.costSoFar <= nodeCost)
continue;
// Remove it from the open list (it will be re-added with the new cost)
openList.remove(nodeRecord);
// We can use the node's old cost values to calculate its heuristic
// without calling the possibly expensive heuristic function
nodeHeuristic = nodeRecord.getEstimatedTotalCost() - nodeRecord.costSoFar;
} else { // the node is unvisited
// We'll need to calculate the heuristic value using the function,
// since we don't have a node record with a previously calculated value
nodeHeuristic = heuristic.estimate(node, endNode);
}
// Update node record's cost and connection
nodeRecord.costSoFar = nodeCost;
nodeRecord.connection = connection;
// Add it to the open list with the estimated total cost
addToOpenList(nodeRecord, nodeCost + nodeHeuristic);
}
}
protected void generateConnectionPath(N startNode, GraphPath<Connection<N>> outPath) {
// Work back along the path, accumulating connections
// outPath.clear();
while (current.node != startNode) {
outPath.add(current.connection);
current = nodeRecords[graph.getIndex(current.connection.getFromNode())];
}
// Reverse the path
outPath.reverse();
}
protected void generateNodePath(N startNode, GraphPath<N> outPath) {
// Work back along the path, accumulating nodes
// outPath.clear();
while (current.connection != null) {
outPath.add(current.node);
current = nodeRecords[graph.getIndex(current.connection.getFromNode())];
}
outPath.add(startNode);
// Reverse the path
outPath.reverse();
}
protected void addToOpenList(NodeRecord<N> nodeRecord, float estimatedTotalCost) {
openList.add(nodeRecord, estimatedTotalCost);
nodeRecord.category = OPEN;
if (metrics != null) {
metrics.openListAdditions++;
metrics.openListPeak = Math.max(metrics.openListPeak, openList.size);
}
}
protected NodeRecord<N> getNodeRecord(N node) {
int index = graph.getIndex(node);
NodeRecord<N> nr = nodeRecords[index];
if (nr != null) {
if (nr.searchId != searchId) {
nr.category = UNVISITED;
nr.searchId = searchId;
}
return nr;
}
nr = nodeRecords[index] = new NodeRecord<N>();
nr.node = node;
nr.searchId = searchId;
return nr;
}
/**
* This nested class is used to keep track of the information we need for each
* node during the search.
*
* @param <N> Type of node
*
* @author davebaol
*/
static class NodeRecord<N> extends BinaryHeap.Node {
/** The reference to the node. */
N node;
/** The incoming connection to the node */
Connection<N> connection;
/** The actual cost from the start node. */
float costSoFar;
/** The node category: {@link #UNVISITED}, {@link #OPEN} or {@link #CLOSED}. */
int category;
/** ID of the current search. */
int searchId;
/** Creates a {@code NodeRecord}. */
public NodeRecord() {
super(0);
}
/** Returns the estimated total cost. */
public float getEstimatedTotalCost() {
return getValue();
}
}
/**
* A class used by {@link IndexedAStarPathFinder} to collect search metrics.
*
* @author davebaol
*/
public static class Metrics {
public int visitedNodes;
public int openListAdditions;
public int openListPeak;
public Metrics() {
}
public void reset() {
visitedNodes = 0;
openListAdditions = 0;
openListPeak = 0;
}
}
}

View File

@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright 2014 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package wow.doge.mygame.game.subsystems.ai.gdx;
import wow.doge.mygame.game.subsystems.ai.gdx.Graph;
/**
* A graph for the {@link IndexedAStarPathFinder}.
*
* @param <N> Type of node
*
* @author davebaol
*/
public interface MyIndexedGraph<N> extends Graph<N> {
/**
* Returns the unique index of the given node.
*
* @param node the node whose index will be returned
* @return the unique index of the given node.
*/
public int getIndex(N node);
/** Returns the number of nodes in this graph. */
public int getNodeCount();
}

View File

@ -65,30 +65,6 @@ object GameInputHandler {
def setupKeys(inputManager: InputManager) = def setupKeys(inputManager: InputManager) =
inputManager inputManager
// .withMapping(
// PlayerMovementInput.WalkLeft.entryName,
// new KeyTrigger(KeyInput.KEY_A)
// // new KeyTrigger(KeyInput.KEY_LEFT)
// )
// .withMapping(
// PlayerMovementInput.WalkRight.entryName,
// new KeyTrigger(KeyInput.KEY_D)
// // new KeyTrigger(KeyInput.KEY_RIGHT)
// )
// .withMapping(
// PlayerMovementInput.WalkForward.entryName,
// new KeyTrigger(KeyInput.KEY_W)
// // new KeyTrigger(KeyInput.KEY_UP)
// )
// .withMapping(
// PlayerMovementInput.WalkBackward.entryName,
// new KeyTrigger(KeyInput.KEY_S)
// // new KeyTrigger(KeyInput.KEY_DOWN)
// )
// .withMapping(
// PlayerMovementInput.Jump.entryName,
// new KeyTrigger(KeyInput.KEY_SPACE)
// )
.withMapping( .withMapping(
PlayerAnalogInput.TurnRight.entryName, PlayerAnalogInput.TurnRight.entryName,
new KeyTrigger(KeyInput.KEY_RIGHT), new KeyTrigger(KeyInput.KEY_RIGHT),

View File

@ -17,8 +17,8 @@ object DefaultGameLevel {
lazy val sceneModel: Spatial = assetManager.loadModel("main.scene") lazy val sceneModel: Spatial = assetManager.loadModel("main.scene")
lazy val sceneShape = CollisionShapeFactory.createMeshShape( lazy val sceneShape = CollisionShapeFactory.createMeshShape(
sceneModel.toNode match { sceneModel.toNode match {
case util.Right(node) => node case Right(node) => node
case util.Left(ex) => case Left(ex) =>
throw new NotImplementedError("No fallback sceneshape") throw new NotImplementedError("No fallback sceneshape")
} }
) )
@ -40,7 +40,7 @@ object DefaultGameLevel {
dl.setColor(ColorRGBA.White); dl.setColor(ColorRGBA.White);
dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal()); dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
// app.rootNode.addLight(dl); // app.rootNode.addLight(dl);
new Level( new GameLevel(
model = sceneModel, model = sceneModel,
physicsControl = landscape, physicsControl = landscape,
ambientLight = al, ambientLight = al,

View File

@ -10,7 +10,7 @@ import monix.bio.Task
import com.jme3.scene.Node import com.jme3.scene.Node
import wow.doge.mygame.implicits._ import wow.doge.mygame.implicits._
class Level( class GameLevel(
model: Spatial, model: Spatial,
physicsControl: RigidBodyControl, physicsControl: RigidBodyControl,
ambientLight: AmbientLight, ambientLight: AmbientLight,
@ -19,14 +19,8 @@ class Level(
def addToGame(rootNode: Ref[Task, Node], physicsSpace: PhysicsSpace) = { def addToGame(rootNode: Ref[Task, Node], physicsSpace: PhysicsSpace) = {
for { for {
_ <- rootNode.update(_ :+ model) _ <- rootNode.update(_ :+ model)
_ <- rootNode.update { r => _ <- rootNode.update(_ :+ ambientLight)
r.addLight(ambientLight) _ <- rootNode.update(_ :+ directionalLight)
r
}
_ <- rootNode.update { r =>
r.addLight(directionalLight)
r
}
_ <- Task(physicsSpace += model) _ <- Task(physicsSpace += model)
_ <- Task(physicsSpace += physicsControl) _ <- Task(physicsSpace += physicsControl)
} yield () } yield ()

View File

@ -0,0 +1,43 @@
package wow.doge.mygame.game.subsystems.ui
import com.jme3.app.Application
import com.jayfella.jme.jfx.JavaFxUI
import scalafx.application.Platform
import monix.execution.CancelablePromise
import monix.bio.Task
import cats.effect.concurrent.Deferred
import scala.concurrent.duration._
import wow.doge.mygame.game.GameApp
object JFxUI {
def apply(app: GameApp) =
Task(JavaFxUI.initialize(app))
.executeOn(app.scheduler) >> Task.sleep(500.millis) >> Task(
JavaFxUI.getInstance()
)
// Task {
// Platform.runLater(() => {
// println("here jfx")
// JavaFxUI.initialize(app)
// println("here2 jfx2")
// val inst = JavaFxUI.getInstance()
// println(inst)
// })
// }
// Task.fromFuture {
// val p = CancelablePromise[JavaFxUI]()
// Platform.runLater(() => {
// println("here")
// JavaFxUI.initialize(app)
// println("here2")
// val inst = JavaFxUI.getInstance()
// println(inst)
// p.success(inst)
// })
// p.future
// }
// for {
// d <- Deferred[Task, JavaFxUI]
// _ <- Task(JavaFxUI.initialize(app))
// } yield ()
}

View File

@ -0,0 +1,88 @@
package wow.doge.mygame.implicits
import javafx.{
collections => jfxc,
event => jfxe,
geometry => jfxg,
scene => jfxs,
util => jfxu
}
import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp}
import scalafx.scene.Scene
import monix.execution.Cancelable
import monix.reactive.OverflowStrategy
import monix.reactive.Observable
import monix.execution.Ack
import scalafx.scene.control.ButtonBase
object JavaFXMonixObservables {
implicit final class SceneObservables(private val scene: Scene)
extends AnyVal {
def observableMousePressed(): Observable[jfxsi.MouseEvent] = {
import monix.execution.cancelables.SingleAssignCancelable
Observable.create(OverflowStrategy.Unbounded) { sub =>
val c = SingleAssignCancelable()
val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
override def handle(event: jfxsi.MouseEvent): Unit = {
if (sub.onNext(event) == Ack.Stop) c.cancel()
}
}
scene.onMousePressed = l
c := Cancelable(() =>
scene.removeEventHandler(
jfxsi.MouseEvent.MOUSE_PRESSED,
l
)
)
c
}
}
def observableMouseDragged(): Observable[jfxsi.MouseEvent] = {
import monix.execution.cancelables.SingleAssignCancelable
Observable.create(OverflowStrategy.Unbounded) { sub =>
val c = SingleAssignCancelable()
val l = new jfxe.EventHandler[jfxsi.MouseEvent] {
override def handle(event: jfxsi.MouseEvent): Unit = {
if (sub.onNext(event) == Ack.Stop) c.cancel()
}
}
scene.onMouseDragged = l
c := Cancelable(() =>
scene.removeEventHandler(
jfxsi.MouseEvent.MOUSE_DRAGGED,
l
)
)
c
}
}
}
implicit final class OnActionObservable(
private val button: ButtonBase
) extends AnyVal {
def observableAction(): Observable[jfxe.ActionEvent] = {
import monix.execution.cancelables.SingleAssignCancelable
Observable.create(OverflowStrategy.Unbounded) { sub =>
val c = SingleAssignCancelable()
val l = new jfxe.EventHandler[jfxe.ActionEvent] {
override def handle(event: jfxe.ActionEvent): Unit = {
if (sub.onNext(event) == Ack.Stop) c.cancel()
}
}
button.onAction = l
c := Cancelable(() =>
button.removeEventHandler(
jfxe.ActionEvent.ACTION,
l
)
)
c
}
}
}
}

View File

@ -1,43 +0,0 @@
package wow.doge.mygame.implicits
import enumeratum._
sealed trait TestEnum extends EnumEntry
object TestEnum extends Enum[TestEnum] {
val values = findValues
case object Test2 extends TestEnum
}
sealed trait Greeting extends EnumEntry
object Greeting extends Enum[Greeting] {
/*
`findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
You use it to implement the `val values` member
*/
val values = findValues
case object Hello extends Greeting
case object GoodBye extends Greeting
case object Hi extends Greeting
case object Bye extends Greeting
}
object ObsTest {}
sealed trait PlayerMovementEnum extends EnumEntry {
def test: String
}
object PlayerMovementEnum extends Enum[PlayerMovementEnum] {
val values = findValues
case object MOVE_RIGHT extends PlayerMovementEnum {
val test = "hmm"
}
case object MOVE_LEFT extends PlayerMovementEnum {
val test = "mmh"
}
}

View File

@ -0,0 +1,18 @@
package wow.doge.mygame.implicits
import javafx.{
collections => jfxc,
event => jfxe,
geometry => jfxg,
scene => jfxs,
util => jfxu
}
import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp}
import scalafx.scene.Scene
import monix.execution.Cancelable
import monix.reactive.OverflowStrategy
import monix.reactive.Observable
import monix.execution.Ack
import scalafx.scene.control.Button
package object observables {}

View File

@ -6,7 +6,6 @@ import scala.reflect.ClassTag
import akka.actor.typed.ActorRef import akka.actor.typed.ActorRef
import akka.actor.typed.Scheduler import akka.actor.typed.Scheduler
import akka.util.Timeout import akka.util.Timeout
import cats.effect.concurrent.Ref
import com.jme3.app.Application import com.jme3.app.Application
import com.jme3.app.SimpleApplication import com.jme3.app.SimpleApplication
import com.jme3.app.state.AppState import com.jme3.app.state.AppState
@ -49,6 +48,8 @@ import monix.reactive.OverflowStrategy
import monix.reactive.observers.Subscriber import monix.reactive.observers.Subscriber
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 com.jme3.light.Light
import com.jayfella.jme.jfx.JavaFxUI
case class ActionEvent(binding: Action, value: Boolean, tpf: Float) case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
case class EnumActionEvent[T <: EnumEntry]( case class EnumActionEvent[T <: EnumEntry](
@ -70,7 +71,7 @@ package object implicits {
type PhysicsTickObservable = type PhysicsTickObservable =
Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]] Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]]
implicit class JMEAppExt(private val app: Application) extends AnyVal { implicit final class JMEAppExt(private val app: Application) extends AnyVal {
def enqueueR(cb: () => Unit) = def enqueueR(cb: () => Unit) =
app.enqueue(new Runnable { app.enqueue(new Runnable {
@ -78,7 +79,7 @@ package object implicits {
}) })
} }
implicit class StateManagerExt(private val asm: AppStateManager) implicit final class StateManagerExt(private val asm: AppStateManager)
extends AnyVal { extends AnyVal {
def state[S <: AppState]()(implicit c: ClassTag[S]): S = def state[S <: AppState]()(implicit c: ClassTag[S]): S =
asm.getState(c.runtimeClass.asInstanceOf[Class[S]]) asm.getState(c.runtimeClass.asInstanceOf[Class[S]])
@ -87,8 +88,9 @@ package object implicits {
} }
implicit class SimpleApplicationExt[T <: SimpleApplication](private val sa: T) implicit final class SimpleApplicationExt[T <: SimpleApplication](
extends AnyVal { private val sa: T
) extends AnyVal {
def stateManager: AppStateManager = sa.getStateManager() def stateManager: AppStateManager = sa.getStateManager()
def inputManager: InputManager = sa.getInputManager() def inputManager: InputManager = sa.getInputManager()
def assetManager: AssetManager = sa.getAssetManager() def assetManager: AssetManager = sa.getAssetManager()
@ -121,7 +123,8 @@ package object implicits {
} }
} }
implicit class AssetManagerExt(private val am: AssetManager) extends AnyVal { implicit final class AssetManagerExt(private val am: AssetManager)
extends AnyVal {
def registerLocator( def registerLocator(
assetPath: os.RelPath, assetPath: os.RelPath,
locator: Class[_ <: AssetLocator] locator: Class[_ <: AssetLocator]
@ -134,13 +137,13 @@ package object implicits {
} }
} }
implicit class BulletAppStateExt(private val bas: BulletAppState) implicit final class BulletAppStateExt(private val bas: BulletAppState)
extends AnyVal { extends AnyVal {
def physicsSpace = bas.getPhysicsSpace() def physicsSpace = bas.getPhysicsSpace()
def speed = bas.getSpeed() def speed = bas.getSpeed()
} }
implicit class BetterCharacterControlExt( implicit final class BetterCharacterControlExt(
private val bcc: BetterCharacterControl private val bcc: BetterCharacterControl
) { ) {
def withJumpForce(force: ImVector3f) = { def withJumpForce(force: ImVector3f) = {
@ -149,11 +152,12 @@ package object implicits {
} }
} }
implicit class SpatialExt[T <: Spatial](private val spat: T) extends AnyVal { implicit final class SpatialExt[T <: Spatial](private val spat: T)
def asRef = Ref[Task].of(spat) extends AnyVal {
// def asRef = Ref[Task].of(spat)
} }
implicit class NodeExt[T <: Node](private val n: T) extends AnyVal { implicit final class NodeExt[T <: Node](private val n: T) extends AnyVal {
/** /**
* Attaches the given child * Attaches the given child
@ -167,18 +171,13 @@ package object implicits {
} }
/** /**
* Gets the list of children as a monix observable * @return Gets the list of children as a monix observable
*
* @return
*/ */
// def children = n.getChildren().asScala.toSeq
def observableChildren = def observableChildren =
Observable.fromIterable(n.getChildren().asScala) Observable.fromIterable(n.getChildren().asScala)
/** /**
* A copy of the list of children of this node as a lazy list * @return A copy of the list of children of this node as a lazy list
*
* @return
*/ */
def children = LazyList.from(n.getChildren().asScala) def children = LazyList.from(n.getChildren().asScala)
@ -199,6 +198,11 @@ package object implicits {
n n
} }
def :+(light: Light) = {
n.addLight(light)
n
}
def -=(spatial: Spatial) = n.detachChild(spatial) def -=(spatial: Spatial) = n.detachChild(spatial)
def :-(spatial: Spatial) = { def :-(spatial: Spatial) = {
@ -206,6 +210,11 @@ package object implicits {
n n
} }
def :-(light: Light) = {
n.removeLight(light)
n
}
def depthFirst(cb: Spatial => Unit) = def depthFirst(cb: Spatial => Unit) =
n.depthFirstTraversal(new SceneGraphVisitor() { n.depthFirstTraversal(new SceneGraphVisitor() {
override def visit(s: Spatial) = cb(s) override def visit(s: Spatial) = cb(s)
@ -313,7 +322,7 @@ package object implicits {
} }
implicit class CameraNodeExt(private val cn: CameraNode) { implicit final class CameraNodeExt(private val cn: CameraNode) {
def withControlDir(controlDir: ControlDirection) = { def withControlDir(controlDir: ControlDirection) = {
cn.setControlDir(controlDir) cn.setControlDir(controlDir)
cn cn
@ -325,14 +334,15 @@ package object implicits {
} }
} }
implicit class EntityDataExt(private val ed: EntityData) extends AnyVal { implicit final class EntityDataExt(private val ed: EntityData)
extends AnyVal {
def query = new EntityQuery(ed) def query = new EntityQuery(ed)
// def entities[T <: EntityComponent](entities: Seq[T]) // def entities[T <: EntityComponent](entities: Seq[T])
} }
implicit class EntityExt(private val e: EntityId) extends AnyVal { implicit final class EntityExt(private val e: EntityId) extends AnyVal {
def withComponents(classes: EntityComponent*)(implicit def withComponents(classes: EntityComponent*)(implicit
ed: EntityData ed: EntityData
): EntityId = { ): EntityId = {
@ -341,7 +351,8 @@ package object implicits {
} }
} }
implicit class ActorRefExt[Req](private val a: ActorRef[Req]) extends AnyVal { implicit final class ActorRefExt[Req](private val a: ActorRef[Req])
extends AnyVal {
import akka.actor.typed.scaladsl.AskPattern._ import akka.actor.typed.scaladsl.AskPattern._
/** /**
@ -374,7 +385,7 @@ package object implicits {
// ask(replyTo)(timeout, scheduler) // ask(replyTo)(timeout, scheduler)
// } // }
implicit class InputManagerExt(private val inputManager: InputManager) implicit final class InputManagerExt(private val inputManager: InputManager)
extends AnyVal { extends AnyVal {
def withMapping(mapping: String, triggers: Trigger*): InputManager = { def withMapping(mapping: String, triggers: Trigger*): InputManager = {
inputManager.addMapping(mapping, triggers: _*) inputManager.addMapping(mapping, triggers: _*)
@ -507,7 +518,7 @@ package object implicits {
} }
} }
implicit class PhysicsSpaceExt(private val space: PhysicsSpace) implicit final class PhysicsSpaceExt(private val space: PhysicsSpace)
extends AnyVal { extends AnyVal {
def collisionObservable(): Observable[PhysicsCollisionEvent] = { def collisionObservable(): Observable[PhysicsCollisionEvent] = {
@ -587,9 +598,11 @@ package object implicits {
space space
} }
// def asRef = Ref[Task].of(space)
} }
implicit class Vector3fExt(private val v: Vector3f) extends AnyVal { implicit final class Vector3fExt(private val v: Vector3f) extends AnyVal {
//TODO add more operations //TODO add more operations
def +=(that: Vector3f) = v.addLocal(that) def +=(that: Vector3f) = v.addLocal(that)
def +=(f: Float) = v.addLocal(f, f, f) def +=(f: Float) = v.addLocal(f, f, f)
@ -608,7 +621,7 @@ package object implicits {
def immutable = ImVector3f(v.x, v.y, v.z) def immutable = ImVector3f(v.x, v.y, v.z)
} }
implicit class ImVector3fExt(private val v: ImVector3f) extends AnyVal { implicit final class ImVector3fExt(private val v: ImVector3f) extends AnyVal {
def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z) def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
def +(f: Float): ImVector3f = v.copy(v.x + f, v.y + f, v.z + f) def +(f: Float): ImVector3f = v.copy(v.x + f, v.y + f, v.z + f)
def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z) def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
@ -628,4 +641,8 @@ package object implicits {
// f.hideErrors // f.hideErrors
// } // }
implicit final class JavaFxUIExt(private val jfxui: JavaFxUI) extends AnyVal {
def +=(node: scalafx.scene.Node) = jfxui.attachChild(node)
def -=(node: scalafx.scene.Node) = jfxui.detachChild(node)
}
} }

View File

@ -0,0 +1,102 @@
package wow.doge.mygame.launcher
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.effect.DropShadow
import scalafx.scene.layout.HBox
import scalafx.scene.paint.Color._
import scalafx.scene.paint._
import scalafx.scene.text.Text
import scalafx.scene.control.Button
import scalafx.scene.layout.VBox
import scalafx.scene.layout.FlowPane
import scalafx.geometry.Orientation
import scalafx.geometry.Pos
import scalafx.stage.Stage
object DefaultUI {
def scene(
// stage: Stage,
launchButton: Button,
exitButton: Button
) =
new Scene {
fill = Color.rgb(38, 38, 38)
content = new VBox {
children = Seq(
new HBox {
padding = Insets(50, 80, 50, 80)
children = Seq(
new Text {
text = "JMonkeyEngine"
style = "-fx-font: normal bold 50pt sans-serif"
fill = new LinearGradient(endX = 0, stops = Stops(Red, DarkRed))
},
new Text {
text = " Game"
style = "-fx-font: italic bold 50pt sans-serif"
fill = new LinearGradient(
endX = 0,
stops = Stops(White, DarkGray)
)
effect = new DropShadow {
color = DarkGray
radius = 15
spread = 0.25
}
}
)
},
new FlowPane {
hgap = 10
padding = Insets(50, 80, 50, 80)
orientation = Orientation.Horizontal
alignment = Pos.Center
children = Seq(launchButton, exitButton)
}
)
}
// onMousePressed = (pressEvent) => {
// onMouseDragged = (dragEvent) => {
// stage.setX(dragEvent.getScreenX() - pressEvent.getSceneX())
// stage.setY(dragEvent.getScreenY() - pressEvent.getSceneY())
// }
// }
}
def box(launchButton: Button, exitButton: Button) =
new VBox {
children = Seq(
new HBox {
padding = Insets(50, 80, 50, 80)
children = Seq(
new Text {
text = "JMonkeyEngine"
style = "-fx-font: normal bold 50pt sans-serif"
fill = new LinearGradient(endX = 0, stops = Stops(Red, DarkRed))
},
new Text {
text = " Game"
style = "-fx-font: italic bold 50pt sans-serif"
fill = new LinearGradient(
endX = 0,
stops = Stops(White, DarkGray)
)
effect = new DropShadow {
color = DarkGray
radius = 15
spread = 0.25
}
}
)
},
new FlowPane {
hgap = 10
padding = Insets(50, 80, 50, 80)
orientation = Orientation.Horizontal
alignment = Pos.Center
children = Seq(launchButton, exitButton)
}
)
}
}

View File

@ -0,0 +1,153 @@
package wow.doge.mygame.launcher
import scala.concurrent.duration.FiniteDuration
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import wow.doge.mygame.executors.Schedulers
import cats.effect.Resource
import monix.bio.Task
import scala.concurrent.duration._
import javafx.application.Platform
import scalafx.scene.control.Button
import cats.effect.concurrent.Deferred
import wow.doge.mygame.utils.IOUtils._
import monix.eval.{Task => ETask}
import monix.reactive.Observable
import monix.bio.Fiber
import scalafx.stage.StageStyle
import scalafx.Includes._
import wow.doge.mygame.utils.ResizeHelper
import scalafx.scene.Scene
import scalafx.scene.layout.VBox
import wow.doge.mygame.implicits.JavaFXMonixObservables._
import monix.catnap.cancelables.SingleAssignCancelableF
import monix.catnap.CancelableF
// import wow.doge.mygame.implicits.JavaFXMonixObservables._
// import scala.language.implicitConversions
// object Stage {
// implicit def sfxStage2jfx(v: Stage): jfxs.Stage = if (v != null) v.delegate else null
// }
object Launcher {
sealed trait LauncherResult
object LauncherResult {
case object LaunchGame extends LauncherResult
case object Exit extends LauncherResult
}
class Props(
val schedulers: Schedulers,
val signal: Deferred[Task, LauncherResult]
) {
// val resource2
// : Resource[Task, (LauncherApp, Task[Ref[Task, Stage]], Fiber[Unit])] =
// Resource.make(for {
// app <- Task(new LauncherApp(this))
// fib <- app.init.start
// } yield ((app, app.stageRef, fib)))(_._3.cancel)
val create = Task(new Launcher(this))
val resource: Resource[Task, Launcher] =
Resource.make(for {
app <- Task(new Launcher(this))
// fib <- app.init.start
} yield (app))(_ => Task.unit)
}
}
class Launcher private (props: Launcher.Props) {
import Launcher._
private lazy val launchButton = new Button {
text = "Launch"
}
// private lazy val launchButtonObs =
private lazy val launchAction =
launchButton
.observableAction()
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.LaunchGame)))
private lazy val exitButton = new Button {
text = "Exit"
}
// private lazy val exitButtonObs =
private lazy val exitAction =
exitButton
.observableAction()
.doOnNext(_ => toTask(props.signal.complete(LauncherResult.Exit)))
private lazy val _scene =
// new Scene {
// content = new VBox
// }
DefaultUI.scene(launchButton, exitButton)
private lazy val _stage = new PrimaryStage {
scene = _scene
}
private lazy val internal = new JFXApp {
stage = _stage
stage.initStyle(StageStyle.Undecorated)
// ResizeHelper.addResizeListener(stage)
}
private lazy val sceneDragObservable = {
lazy val mpo = _scene.observableMousePressed()
lazy val mdo = _scene.observableMouseDragged()
mpo.mergeMap(pressEvent =>
mdo.doOnNext(dragEvent =>
ETask(
_stage.setX(dragEvent.screenX - pressEvent.sceneX)
) >>
ETask(
_stage.setY(
dragEvent.screenY - pressEvent.sceneY
)
)
)
)
}
// var stage = internal.stage
// lazy val stageRef = Ref.of[Task, Stage](internal.stage)
// stage: => PrimaryStage
def init(delay: FiniteDuration = 2000.millis) =
for {
_ <- Task(Platform.setImplicitExit(false))
fib <- Task(internal.main(Array.empty)).start
_ <- Task.sleep(500.millis)
// _ <- Task {
// // lazy val _stage = new CustomStageBuilder()
// // .setWindowTitle("CustomStage example")
// // .setWindowColor("rgb(34,54,122)")
// // .build()
// internal.stage.scene =
// DefaultUI.scene(internal.stage, launchButton, exitButton)
// // _stage.setScene(DefaultUI.scene(launchButton, exitButton))
// // JFXApp.Stage = _stage
// }.executeOn(props.schedulers.fx)
// c <- SingleAssignCancelableF[Task]
sceneDragFib <- toIO(sceneDragObservable.completedL).start
fib2 <- toIO(
Observable(launchAction, exitAction).merge
.doOnNext(_ =>
ETask(internal.stage.close()).executeOn(props.schedulers.fx)
)
// .doOnNext(_ => toTask(fib.cancel))
.completedL
).start
c <- CancelableF[Task](fib.cancel >> fib2.cancel >> sceneDragFib.cancel)
// _ <- Task {
// internal.stage = stage
// }.executeOn(props.schedulers.fx)
// .delayExecution(delay)
} yield (c)
}

View File

@ -3,7 +3,6 @@ package wow.doge.mygame.subsystems.events
import wow.doge.mygame.game.subsystems.movement.CanMove import wow.doge.mygame.game.subsystems.movement.CanMove
sealed trait EntityMovementEvent sealed trait EntityMovementEvent
object EntityMovementEvent { object EntityMovementEvent {
final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T) final case class MovedLeft[T: CanMove](pressed: Boolean, movable: T)
extends EntityMovementEvent extends EntityMovementEvent
@ -29,5 +28,6 @@ object EntityMovementEvent {
final case object PlayerRotatedLeft extends PlayerMovementEvent final case object PlayerRotatedLeft extends PlayerMovementEvent
final case object PlayerCameraUp extends PlayerMovementEvent final case object PlayerCameraUp extends PlayerMovementEvent
final case object PlayerCameraDown extends PlayerMovementEvent final case object PlayerCameraDown extends PlayerMovementEvent
} }
} }

View File

@ -23,9 +23,6 @@ final case class Test1(hello1: String, hello2: String)
final case class Test2(hello1: String) final case class Test2(hello1: String)
final case class Plugin(name: String, priority: Int) final case class Plugin(name: String, priority: Int)
object Plugin { object Plugin {
// @annotation.nowarn(
// "msg=Block result was adapted via implicit conversion"
// )
implicit val pluginFormat: Decoder[Plugin] = deriveDecoder implicit val pluginFormat: Decoder[Plugin] = deriveDecoder
} }

View File

@ -29,6 +29,11 @@ object ScriptActor {
result: ActorRef[Either[Error, Any]] result: ActorRef[Either[Error, Any]]
) extends Command ) extends Command
final case class CompileAll(
paths: Seq[os.Path],
result: ActorRef[Map[os.Path, Either[Error, Any]]]
) extends Command
lazy val defaultScalaRunner = lazy val defaultScalaRunner =
ammonite ammonite
.Main( .Main(
@ -126,15 +131,18 @@ class ScriptActor(
import ScriptActor._ import ScriptActor._
def receiveMessage: Behavior[Command] = def receiveMessage: Behavior[Command] =
Behaviors.receiveMessage { msg => Behaviors.receiveMessage {
msg match {
case CompileAny(path, requester) => case CompileAny(path, requester) =>
context.log.debug(s"Received $path") context.log.debug(s"Received $path")
val res = getScript(path) val res = getScript(path)
context.log.debug(s"result = $res") context.log.debug(s"result = $res")
requester ! res requester ! res
Behaviors.same Behaviors.same
}
case CompileAll(paths, requester) =>
context.log.debug(s"Received $paths")
requester ! compileAll(paths)
Behaviors.same
} }
def getScript(path: os.Path): Either[Error, Any] = def getScript(path: os.Path): Either[Error, Any] =
@ -148,4 +156,25 @@ class ScriptActor(
case l @ Left(err) => l case l @ Left(err) => l
} }
type LOL = Map[os.Path, Either[wow.doge.mygame.state.ScriptActor.Error, Any]]
def compileAll(
paths: Seq[os.Path]
): LOL = {
@annotation.tailrec
def loop(
paths: Seq[os.Path],
scriptsMap: Map[
os.Path,
Either[wow.doge.mygame.state.ScriptActor.Error, Any]
]
): LOL = {
paths match {
case head :: next => loop(next, scriptsMap + (head -> getScript(head)))
case Nil => scriptsMap
}
}
loop(paths, Map.empty)
}
} }

View File

@ -37,6 +37,11 @@ object ScriptCachingActor {
requester: ActorRef[ScriptResult], requester: ActorRef[ScriptResult],
force: Boolean = false force: Boolean = false
) extends Command ) extends Command
// final case class GetAll(
// scriptPaths: Seq[os.Path],
// requester: ActorRef[Map[os.Path, ScriptResult]],
// force: Boolean = false
// ) extends Command
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
@ -47,6 +52,15 @@ object ScriptCachingActor {
scriptPath: os.Path, scriptPath: os.Path,
requester: ActorRef[ScriptResult] requester: ActorRef[ScriptResult]
) extends Command ) extends Command
// private[scriptsystem] final case class DelegateAllToChild(
// scriptPaths: Seq[os.Path],
// requester: ActorRef[Map[os.Path, ScriptResult]]
// ) extends Command
// private[scriptsystem] final case class ReplyWithPrecompiled(
// precompiled: Map[os.Path, ScriptResult],
// requester: ActorRef[Map[os.Path, ScriptResult]]
// ) extends Command
// final case class Props( // final case class Props(
// ctx: ActorContext[Command], // ctx: ActorContext[Command],
@ -102,6 +116,51 @@ class ScriptCachingActor(
) )
Behaviors.same Behaviors.same
// case GetAll(scriptPaths, requester, force) =>
// import scala.concurrent.duration._
// implicit val timeout = Timeout(15.seconds)
// /**
// * Holy complexity batman this is getting too complex
// */
// if (force) {
// scriptPaths
// .sliding(
// if (scriptPaths.length > 3 && scriptPaths.length % 2 == 0) 2
// else 3
// )
// .foreach(lst =>
// ctx.self ! DelegateAllToChild(scriptPaths, requester)
// )
// } else {
// val (failures, successes) = scriptPaths
// .partitionMap(path =>
// state.scriptsMap.get(path) match {
// case Some(value) => Right(path -> value)
// case None => Left(path)
// }
// ) match {
// case (failures, successes) =>
// import cats.syntax.either._
// failures -> Map.from(successes.map {
// case (p, obj) => p -> obj.asRight[ScriptActor.Error]
// })
// }
// ctx.ask(scriptActor, ScriptActor.CompileAll(failures, _)) {
// case Success(value) =>
// val total = successes ++ value
// requester ! total
// value.foreach {
// case (path, res) => res.foreach(r => ctx.self ! Put(path, r))
// }
// NoOp
// case Failure(exception) => NoOp
// }
// }
// // scriptPaths.foreach(p => ctx.self ! Get(p))
// Behaviors.same
case DelegateToChild(scriptActor, scriptPath, requester) => case DelegateToChild(scriptActor, scriptPath, requester) =>
import scala.concurrent.duration._ import scala.concurrent.duration._
implicit val timeout = Timeout(15.seconds) implicit val timeout = Timeout(15.seconds)
@ -114,6 +173,19 @@ class ScriptCachingActor(
) )
Behaviors.same Behaviors.same
// case DelegateAllToChild(scriptPaths, requester) =>
// import scala.concurrent.duration._
// implicit val timeout = Timeout(15.seconds)
// ctx.ask(scriptActor, ScriptActor.CompileAll(scriptPaths, _)) {
// case Success(value) =>
// value.foreach {
// case (path, res) => res.foreach(r => ctx.self ! Put(path, r))
// }
// NoOp
// case Failure(exception) => NoOp
// }
// Behaviors.same
case GetMap(requester) => case GetMap(requester) =>
requester ! state.scriptsMap requester ! state.scriptsMap
Behaviors.same Behaviors.same
@ -128,6 +200,7 @@ class ScriptCachingActor(
case NoOp => Behaviors.same case NoOp => Behaviors.same
} }
} }
} }
object ScriptActorPool { object ScriptActorPool {

View File

@ -35,6 +35,7 @@ class ScriptSystemResource(
Resource.liftF(scriptCacheActor) Resource.liftF(scriptCacheActor)
} }
// sys.ask(ref => ScriptCachingActor.GetAll(os.pwd/'assets/'scripts/'scala/"hello2.sc",ref, false))
val init = for { val init = for {
scriptFiles <- Task(findScriptFiles(os.pwd / "assets" / "scripts")) scriptFiles <- Task(findScriptFiles(os.pwd / "assets" / "scripts"))

View File

@ -0,0 +1,750 @@
package wow.doge.mygame.utils
/*
* Copyright (c) 2011-2019, ScalaFX Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the ScalaFX Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE SCALAFX PROJECT OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javafx.scene.{input => jfxsi, layout => jfxsl, paint => jfxsp}
import javafx.{
collections => jfxc,
event => jfxe,
geometry => jfxg,
scene => jfxs,
util => jfxu
}
import scalafx.Includes._
import scalafx.beans.property.{
ObjectProperty,
ReadOnlyDoubleProperty,
ReadOnlyObjectProperty
}
import scalafx.collections._
import scalafx.delegate.SFXDelegate
import scalafx.geometry.NodeOrientation
import scalafx.scene.image.WritableImage
import scalafx.scene.input.{Dragboard, Mnemonic, TransferMode}
import scalafx.scene.paint.Paint
import com.goxr3plus.fxborderlessscene.borderless.{BorderlessScene => BScene}
import scala.language.implicitConversions
import scalafx.scene.Cursor
import scalafx.scene._
import scalafx.stage.Stage
import scalafx.stage.StageStyle
object BorderlessScene {
implicit def sfxScene2jfx(v: BorderlessScene): Scene =
if (v != null) v.delegate else null
}
/**
* Wraps [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html]].
*
* @constructor Create a new ScalaFX Scene with JavaFX Scene as delegate.
* @param delegate JavaFX Scene delegated. Its default value is a JavaFX Scene with a
* [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Group.html Group]] as root Node.
*/
class BorderlessScene(
override val delegate: BScene
) extends SFXDelegate[BScene] {
def this(stage: Stage, stageStyle: StageStyle, parent: Parent) =
this(new BScene(stage, stageStyle, parent))
/**
* Returns the root Node of the scene graph
*/
def root: ObjectProperty[jfxs.Parent] = delegate.rootProperty
/**
* Sets the root Node of the scene graph
*/
def root_=(v: Parent): Unit = {
root() = v
}
/**
* Returns Nodes children from this Scene's `root`.
*/
def getChildren =
root.value match {
case group: jfxs.Group => group.getChildren
case pane: jfxsl.Pane => pane.getChildren
case _ =>
throw new IllegalStateException(
"Cannot access children of root: " + root + "\n" +
"Use a class that extends Group or Pane, or override the getChildren method."
)
}
/**
* Returns scene's antialiasing setting.
*/
def antialiasing: SceneAntialiasing = delegate.getAntiAliasing
/**
* Returns Content's Node children from this Scene's `root`.
*/
def content: jfxc.ObservableList[jfxs.Node] = getChildren
/**
* Sets the list of Nodes children from this Scene's `root`, replacing the prior content. If you want append to
* current content, use `add` or similar.
*
* @param c list of Nodes children from this Scene's `root` to replace prior content.
*/
def content_=(c: Iterable[Node]): Unit = {
fillSFXCollection(this.content, c)
}
/**
* Sets a Node child, replacing the prior content. If you want append to current content, use `add` or similar.
*
* @param n Node child to replace prior content.
*/
def content_=(n: Node): Unit = {
fillSFXCollectionWithOne(this.content, n)
}
/**
* Specifies the type of camera use for rendering this `Scene`.
*/
def camera: ObjectProperty[jfxs.Camera] = delegate.cameraProperty
def camera_=(v: Camera): Unit = {
camera() = v
}
/**
* Defines the mouse cursor for this `Scene`.
*/
def cursor: ObjectProperty[jfxs.Cursor] = delegate.cursorProperty
def cursor_=(v: Cursor): Unit = {
cursor() = v
}
/** The effective node orientation of a scene resolves the inheritance of node orientation, returning either left-to-right or right-to-left. */
def effectiveNodeOrientation: ReadOnlyObjectProperty[jfxg.NodeOrientation] =
delegate.effectiveNodeOrientationProperty
/**
* Specifies the event dispatcher for this scene.
*/
def eventDispatcher: ObjectProperty[jfxe.EventDispatcher] =
delegate.eventDispatcherProperty
def eventDispatcher_=(v: jfxe.EventDispatcher): Unit = {
eventDispatcher() = v
}
/**
* Defines the background fill of this Scene.
*/
def fill: ObjectProperty[jfxsp.Paint] = delegate.fillProperty
def fill_=(v: Paint): Unit = {
fill() = v
}
/**
* The height of this Scene
*/
def height: ReadOnlyDoubleProperty = delegate.heightProperty
/**
* The width of this Scene
*/
def width: ReadOnlyDoubleProperty = delegate.widthProperty
def nodeOrientation: ObjectProperty[jfxg.NodeOrientation] =
delegate.nodeOrientationProperty
def nodeOrientation_=(v: NodeOrientation): Unit = {
ObjectProperty.fillProperty[jfxg.NodeOrientation](this.nodeOrientation, v)
}
/**
* Defines a function to be called when a mouse button has been clicked (pressed and released) on this `Scene`.
*/
def onContextMenuRequested = delegate.onContextMenuRequestedProperty
def onContextMenuRequested_=(
v: jfxe.EventHandler[_ >: jfxsi.ContextMenuEvent]
): Unit = {
onContextMenuRequested() = v
}
/**
* Defines a function to be called when drag gesture has been detected.
*/
def onDragDetected = delegate.onDragDetectedProperty
def onDragDetected_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onDragDetected() = v
}
/**
* Defines a function to be called when this `Scene` is a drag and drop gesture source after its data has been
* dropped on a drop target.
*/
def onDragDone = delegate.onDragDoneProperty
def onDragDone_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragDone() = v
}
/**
* Defines a function to be called when the mouse button is released on this `Scene` during drag and drop gesture.
*/
def onDragDropped = delegate.onDragDroppedProperty
def onDragDropped_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragDropped() = v
}
/**
* Defines a function to be called when drag gesture enters this Scene.
*/
def onDragEntered = delegate.onDragEnteredProperty
def onDragEntered_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragEntered() = v
}
/**
* Defines a function to be called when drag gesture exits this Scene.
*/
def onDragExited = delegate.onDragExitedProperty
def onDragExited_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragExited() = v
}
/**
* Defines a function to be called when drag gesture progresses within this `Scene`.
*/
def onDragOver = delegate.onDragOverProperty
def onDragOver_=(v: jfxe.EventHandler[_ >: jfxsi.DragEvent]): Unit = {
onDragOver() = v
}
/**
* Defines a function to be called when this `Node` has input focus and the input method text has changed.
*/
def onInputMethodTextChanged = delegate.onInputMethodTextChangedProperty
def onInputMethodTextChanged_=(
v: jfxe.EventHandler[_ >: jfxsi.InputMethodEvent]
): Unit = {
onInputMethodTextChanged() = v
}
/**
* Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been pressed.
*/
def onKeyPressed = delegate.onKeyPressedProperty
def onKeyPressed_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = {
onKeyPressed() = v
}
/**
* Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been released.
*/
def onKeyReleased = delegate.onKeyReleasedProperty
def onKeyReleased_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = {
onKeyReleased() = v
}
/**
* Defines a function to be called when some `Node` of this `Scene` has input focus and a key has been typed.
*/
def onKeyTyped = delegate.onKeyTypedProperty
def onKeyTyped_=(v: jfxe.EventHandler[_ >: jfxsi.KeyEvent]): Unit = {
onKeyTyped() = v
}
/**
* Defines a function to be called when a mouse button has been clicked (pressed and released) on this `Scene`.
*/
def onMouseClicked = delegate.onMouseClickedProperty
def onMouseClicked_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseClicked() = v
}
/**
* Defines a function to be called when a mouse button is pressed on this `Scene` and then dragged.
*/
def onMouseDragged = delegate.onMouseDraggedProperty
def onMouseDragged_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseDragged() = v
}
/**
* Defines a function to be called when a full press-drag-release gesture enters this `Scene`.
*/
def onMouseDragEntered = delegate.onMouseDragEnteredProperty
def onMouseDragEntered_=(
v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent]
): Unit = {
onMouseDragEntered() = v
}
/**
* Defines a function to be called when a full press-drag-release gesture exits this `Scene`.
*/
def onMouseDragExited = delegate.onMouseDragExitedProperty
def onMouseDragExited_=(
v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent]
): Unit = {
onMouseDragExited() = v
}
/**
* Defines a function to be called when a full press-drag-release gesture progresses within this `Scene`.
*/
def onMouseDragOver = delegate.onMouseDragOverProperty
def onMouseDragOver_=(
v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent]
): Unit = {
onMouseDragOver() = v
}
/**
* Defines a function to be called when a full press-drag-release gesture ends within this `Scene`.
*/
def onMouseDragReleased = delegate.onMouseDragReleasedProperty
def onMouseDragReleased_=(
v: jfxe.EventHandler[_ >: jfxsi.MouseDragEvent]
): Unit = {
onMouseDragReleased() = v
}
/**
* Defines a function to be called when the mouse enters this `Scene`.
*/
def onMouseEntered = delegate.onMouseEnteredProperty
def onMouseEntered_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseEntered() = v
}
/**
* Defines a function to be called when the mouse exits this `Scene`.
*/
def onMouseExited = delegate.onMouseExitedProperty
def onMouseExited_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseExited() = v
}
/**
* Defines a function to be called when mouse cursor moves within this `Scene` but no buttons have been pushed.
*/
def onMouseMoved = delegate.onMouseMovedProperty
def onMouseMoved_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseMoved() = v
}
/**
* Defines a function to be called when a mouse button has been pressed on this `Scene`.
*/
def onMousePressed = delegate.onMousePressedProperty
def onMousePressed_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMousePressed() = v
}
/**
* Defines a function to be called when a mouse button has been released on this `Scene`.
*/
def onMouseReleased = delegate.onMouseReleasedProperty
def onMouseReleased_=(v: jfxe.EventHandler[_ >: jfxsi.MouseEvent]): Unit = {
onMouseReleased() = v
}
/**
* Defines a function to be called when user performs a scrolling action.
*/
def onScroll = delegate.onScrollProperty
def onScroll_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = {
onScroll() = v
}
/**
* The URL of the user-agent stylesheet that will be used by this Scene in place of the the platform-default
* user-agent stylesheet. If the URL does not resolve to a valid location, the platform-default user-agent
* stylesheet will be used.
*
* For additional information about using CSS with the scene graph, see the
* [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html CSS Reference Guide]].
*
* @return The URL of the user-agent stylesheet that will be used by this SubScene, or null if has not been set.
*/
def userAgentStylesheet: ObjectProperty[String] =
delegate.userAgentStylesheetProperty
/**
* Set the URL of the user-agent stylesheet that will be used by this Scene in place of the the platform-default
* user-agent stylesheet. If the URL does not resolve to a valid location, the platform-default user-agent
* stylesheet will be used.
*
* For additional information about using CSS with the scene graph, see the
* [[http://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html CSS Reference Guide]].
*
* @param url The URL is a hierarchical URI of the form `[scheme:][//authority][path]`.
* If the URL does not have a `[scheme:]` component, the URL is considered to be the `[path]`
* component only. Any leading '/' character of the `[path]` is ignored and the `[path]` is
* treated as a path relative to the root of the application's classpath.
*/
def userAgentStylesheet_=(url: String): Unit = {
ObjectProperty.fillProperty[String](userAgentStylesheet, url)
}
/**
* The `Window` for this Scene
*/
def window: ReadOnlyObjectProperty[javafx.stage.Window] =
delegate.windowProperty
/**
* The horizontal location of this `Scene` on the `Window`.
*/
def x: ReadOnlyDoubleProperty = delegate.xProperty
/**
* The vertical location of this `Scene` on the `Window`.
*/
def y: ReadOnlyDoubleProperty = delegate.yProperty
/**
* Retrieves the depth buffer attribute for this scene.
*/
def depthBuffer = delegate.isDepthBuffer
/**
* Gets an observable list of string URLs linking to the stylesheets to use with this Parent's contents.
*/
def stylesheets: jfxc.ObservableList[String] = delegate.getStylesheets
/**
* Sets the list of stylesheets URLs, replacing the prior content. If you want append to current content, use `add` or
* similar.
*
* @param c list of stylesheets URLs to replace prior content.
*/
def stylesheets_=(c: Iterable[String]): Unit = {
fillCollection(stylesheets, c)
}
/**
* Looks for any node within the scene graph based on the specified CSS selector.
*
* @param selector The css selector to look up
* @return A [[scala.Some]] containing the Node in the scene which matches the CSS selector, or [[scala.None]]
* if none is found.
*/
def lookup(selector: String): Option[Node] = Option(delegate.lookup(selector))
/**
* Registers the specified mnemonic.
*
* @param m The Mnemonic
*/
def addMnemonic(m: Mnemonic): Unit = {
delegate.addMnemonic(m)
}
/**
* Unregisters the specified mnemonic.
*
* @param m The Mnemonic to be removed.
*/
def removeMnemonic(m: Mnemonic): Unit = {
delegate.removeMnemonic(m)
}
/**
* Gets the list of mnemonics for this `Scene`.
*/
def getMnemonics
: jfxc.ObservableMap[jfxsi.KeyCombination, jfxc.ObservableList[
jfxsi.Mnemonic
]] = delegate.getMnemonics
/**
* Gets the list of accelerators for this Scene.
*/
def accelerators: jfxc.ObservableMap[jfxsi.KeyCombination, Runnable] =
delegate.getAccelerators
/**
* Confirms a potential drag and drop gesture that is recognized over this `Scene`.
*
* @param transferModes The supported `TransferMode`(s) of this `Node`
* @return A `Dragboard` to place this `Scene`'s data on
*/
def startDragAndDrop(transferModes: TransferMode*): Dragboard =
delegate.startDragAndDrop(transferModes.map(_.delegate): _*)
/**
* Starts a full press-drag-release gesture with this scene as gesture source.
*/
def startFullDrag(): Unit = {
delegate.startFullDrag()
}
/**
* The scene's current focus owner node. This node's "focused" variable might be false if this scene has no window,
* or if the window is inactive (window.focused == false).
*
* @since 2.2
*/
def focusOwner: ReadOnlyObjectProperty[jfxs.Node] =
delegate.focusOwnerProperty()
/**
* Defines a function to be called when user performs a rotation action.
*
* @since 2.2
*/
def onRotate = delegate.onRotateProperty
def onRotate_=(v: jfxe.EventHandler[_ >: jfxsi.RotateEvent]): Unit = {
onRotate() = v
}
/**
* Defines a function to be called when a rotation gesture ends.
*
* @since 2.2
*/
def onRotationFinished = delegate.onRotationFinishedProperty()
def onRotationFinished_=(
v: jfxe.EventHandler[_ >: jfxsi.RotateEvent]
): Unit = {
onRotationFinished() = v
}
/**
* Defines a function to be called when a rotation gesture starts.
*
* @since 2.2
*/
def onRotationStarted = delegate.onRotationFinishedProperty()
def onRotationStarted_=(
v: jfxe.EventHandler[_ >: jfxsi.RotateEvent]
): Unit = {
onRotationStarted() = v
}
/**
* Defines a function to be called when a Scroll gesture ends.
*
* @since 2.2
*/
def onScrollFinished = delegate.onScrollFinishedProperty()
def onScrollFinished_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = {
onScrollFinished() = v
}
/**
* Defines a function to be called when a Scroll gesture starts.
*
* @since 2.2
*/
def onScrollStarted = delegate.onScrollStartedProperty()
def onScrollStarted_=(v: jfxe.EventHandler[_ >: jfxsi.ScrollEvent]): Unit = {
onScrollStarted() = v
}
/**
* Defines a function to be called when a Swipe Down gesture starts.
*
* @since 2.2
*/
def onSwipeDown = delegate.onSwipeDownProperty()
def onSwipeDown_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = {
onSwipeDown() = v
}
/**
* Defines a function to be called when a Swipe Down gesture starts.
*
* @since 2.2
*/
def onSwipeLeft = delegate.onSwipeLeftProperty()
def onSwipeLeft_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = {
onSwipeLeft() = v
}
/**
* Defines a function to be called when a Swipe Up gesture starts.
*
* @since 2.2
*/
def onSwipeUp = delegate.onSwipeUpProperty()
def onSwipeUp_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = {
onSwipeUp() = v
}
/**
* Defines a function to be called when a Swipe Right gesture starts.
*
* @since 2.2
*/
def onSwipeRight = delegate.onSwipeRightProperty()
def onSwipeRight_=(v: jfxe.EventHandler[_ >: jfxsi.SwipeEvent]): Unit = {
onSwipeRight() = v
}
/**
* Defines a function to be called when user performs a Touch action.
*
* @since 2.2
*/
def onZoom = delegate.onZoomProperty()
def onZoom_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = {
onZoom() = v
}
/**
* Defines a function to be called when a Zoom gesture ends.
*
* @since 2.2
*/
def onZoomFinished = delegate.onZoomFinishedProperty()
def onZoomFinished_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = {
onZoomFinished() = v
}
/**
* Defines a function to be called when a Zoom gesture starts.
*
* @since 2.2
*/
def onZoomStarted = delegate.onZoomStartedProperty()
def onZoomStarted_=(v: jfxe.EventHandler[_ >: jfxsi.ZoomEvent]): Unit = {
onZoomStarted() = v
}
/**
* Defines a function to be called when user performs a Touch Moved action.
*
* @since 2.2
*/
def onTouchMoved = delegate.onTouchMovedProperty()
def onTouchMoved_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = {
onTouchMoved() = v
}
/**
* Defines a function to be called when user performs a Touch Pressed action.
*
* @since 2.2
*/
def onTouchPressed = delegate.onTouchPressedProperty()
def onTouchPressed_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = {
onTouchPressed() = v
}
/**
* Defines a function to be called when user performs a Touch Released action.
*
* @since 2.2
*/
def onTouchReleased = delegate.onTouchReleasedProperty()
def onTouchReleased_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = {
onTouchReleased() = v
}
/**
* Defines a function to be called when user performs a Touch Stationary action.
*
* @since 2.2
*/
def onTouchStationary = delegate.onTouchStationaryProperty()
def onTouchStationary_=(v: jfxe.EventHandler[_ >: jfxsi.TouchEvent]): Unit = {
onTouchStationary() = v
}
/**
* Takes a snapshot of this scene and returns the rendered image when it is ready.
*
* @param image The writable image that will be used to hold the rendered scene.
* @return the rendered image
*
* @since 2.2
*/
def snapshot(image: WritableImage): WritableImage = delegate.snapshot(image)
/**
* Takes a snapshot of this scene at the next frame and calls the specified callback method when the image is ready.
*
* @param callback A function to be called when the image is ready.
* @param image The writable image that will be used to hold the rendered scene.
*
* @since 2.2
*/
def snapshot(callback: SnapshotResult => Unit, image: WritableImage): Unit = {
val javaCallback = new jfxu.Callback[jfxs.SnapshotResult, java.lang.Void] {
def call(result: jfxs.SnapshotResult): java.lang.Void = {
callback(new SnapshotResult(result))
null
}
}
delegate.snapshot(javaCallback, image)
}
}

View File

@ -0,0 +1,104 @@
package wow.doge.mygame.utils
import java.io.ByteArrayOutputStream
import java.io.OutputStream
import java.io.PrintStream
import cats.effect.Resource
import monix.bio.Task
import scalafx.scene.control.TextArea
import scalafx.application.Platform
trait ConsoleStreamable[T] {
def println(inst: T, text: String): Unit
def print(inst: T, text: String): Unit
}
class GenericConsoleStream[T](
outputStream: OutputStream,
val config: GenericConsoleStream.Config =
GenericConsoleStream.Config.default,
private var streamable: Option[T] = None
)(implicit
cs: ConsoleStreamable[T]
) extends PrintStream(outputStream, true) {
private lazy val defaultOut = System.out
def printToStreamable(stble: Option[T], text: String) =
stble.foreach(s => cs.println(s, text))
override def println(text: String): Unit =
if (config.exclusive) {
printToStreamable(streamable, text)
} else {
defaultOut.println(text)
printToStreamable(streamable, text)
}
override def print(text: String): Unit =
streamable.foreach(s =>
if (config.exclusive) {
printToStreamable(streamable, text)
} else {
defaultOut.println(text)
printToStreamable(streamable, text)
}
)
def :=(s: T) = {
streamable match {
case Some(value) =>
case None => streamable = Some(s)
}
}
}
object GenericConsoleStream {
/**
* for future use
*/
case class Config(exclusive: Boolean = false)
object Config {
lazy val default = Config()
}
implicit val implJFXConsoleStreamForTextArea =
new ConsoleStreamable[scalafx.scene.control.TextArea] {
override def println(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text + "\n")
override def print(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text)
}
// def textAreaStreamResource(ta: TextArea) =
// Resource.make(
// Task(
// new GenericConsoleStream(
// outputStream = new ByteArrayOutputStream(),
// streamable = ta
// )
// )
// )(s => Task(s.close()))
def textAreaStream(
// ta: TextArea
) =
// Task(
new GenericConsoleStream[TextArea](
outputStream = new ByteArrayOutputStream()
// streamable = ta
)
// )
// (s => Task(s.close()))
}

View File

@ -1,71 +0,0 @@
package wow.doge.mygame.utils
import java.io.ByteArrayOutputStream
import java.io.OutputStream
import java.io.PrintStream
import cats.effect.Resource
import monix.bio.Task
import scalafx.scene.control.TextArea
trait JFXConsoleStreamable[T] {
def println(inst: T, text: String): Unit
def print(inst: T, text: String): Unit
}
class JFXConsoleStream[T](
outputStream: OutputStream,
val config: JFXConsoleStream.Config = JFXConsoleStream.Config.default,
control: T
)(implicit
jcs: JFXConsoleStreamable[T]
) extends PrintStream(outputStream, true) {
private lazy val defaultOut = System.out
override def println(text: String): Unit =
if (config.exclusive) {
jcs.println(control, text + "\n")
} else {
defaultOut.println(text)
jcs.println(control, text + "\n")
}
override def print(text: String): Unit = jcs.println(control, text)
}
object JFXConsoleStream {
/**
* for future use
*/
case class Config(exclusive: Boolean = false)
object Config {
lazy val default = Config()
}
implicit val implJFXConsoleStreamForTextArea =
new JFXConsoleStreamable[scalafx.scene.control.TextArea] {
override def println(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text + "\n")
override def print(
ta: scalafx.scene.control.TextArea,
text: String
): Unit =
ta.appendText(text)
}
def textAreaStream(ta: TextArea) =
Resource.make(
Task(
new JFXConsoleStream(
outputStream = new ByteArrayOutputStream(),
control = ta
)
)
)(s => Task(s.close()))
}

View File

@ -0,0 +1,130 @@
package wow.doge.mygame.utils;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
//created by Alexander Berg
public class ResizeHelper {
public static ResizeListener addResizeListener(Stage stage) {
ResizeListener resizeListener = new ResizeListener(stage);
Scene scene = stage.getScene();
scene.addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
scene.addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
scene.addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
scene.addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
scene.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
return resizeListener;
}
public static class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;
private Scene scene;
private Cursor cursorEvent = Cursor.DEFAULT;
private int border = 4;
private double startX = 0;
private double startY = 0;
private double sceneOffsetX = 0;
private double sceneOffsetY = 0;
private double padTop = 0;
private double padRight = 0;
private double padBottom = 0;
private double padLeft = 0;
public ResizeListener(Stage stage) {
this.stage = stage;
this.scene = stage.getScene();
}
public void setPadding(Insets padding) {
padTop = padding.getTop();
padRight = padding.getRight();
padBottom = padding.getBottom();
padLeft = padding.getLeft();
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
double mouseEventX = mouseEvent.getSceneX(), mouseEventY = mouseEvent.getSceneY(),
viewWidth = stage.getWidth() - padLeft - padRight,
viewHeight = stage.getHeight() - padTop - padBottom;
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
if (mouseEventX < border + padLeft && mouseEventY < border + padTop) {
cursorEvent = Cursor.NW_RESIZE;
} else if (mouseEventX < border + padLeft && mouseEventY > viewHeight - border + padTop) {
cursorEvent = Cursor.SW_RESIZE;
} else if (mouseEventX > viewWidth - border + padLeft && mouseEventY < border + padTop) {
cursorEvent = Cursor.NE_RESIZE;
} else if (mouseEventX > viewWidth - border + padLeft && mouseEventY > viewHeight - border + padTop) {
cursorEvent = Cursor.SE_RESIZE;
} else if (mouseEventX < border + padLeft) {
cursorEvent = Cursor.W_RESIZE;
} else if (mouseEventX > viewWidth - border + padLeft) {
cursorEvent = Cursor.E_RESIZE;
} else if (mouseEventY < border + padTop) {
cursorEvent = Cursor.N_RESIZE;
} else if (mouseEventY > viewHeight - border + padTop) {
cursorEvent = Cursor.S_RESIZE;
} else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
} else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType)
|| MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
scene.setCursor(Cursor.DEFAULT);
} else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
startX = viewWidth - mouseEventX;
startY = viewHeight - mouseEventY;
sceneOffsetX = mouseEvent.getSceneX();
sceneOffsetY = mouseEvent.getSceneY();
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && !Cursor.DEFAULT.equals(cursorEvent)) {
if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent)
|| Cursor.NE_RESIZE.equals(cursorEvent)) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
double height = stage.getY() - mouseEvent.getScreenY() + stage.getHeight() + sceneOffsetY;
double y = mouseEvent.getScreenY() - sceneOffsetY;
stage.setHeight(height);
stage.setY(y);
}
} else {
if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
stage.setHeight(mouseEventY + startY + padBottom + padTop);
}
}
}
if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent)
|| Cursor.SW_RESIZE.equals(cursorEvent)) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
double width = stage.getX() - mouseEvent.getScreenX() + stage.getWidth() + sceneOffsetX;
double x = mouseEvent.getScreenX() - sceneOffsetX;
stage.setWidth(width);
stage.setX(x);
}
} else {
if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
stage.setWidth(mouseEventX + startX + padLeft + padRight);
}
}
}
}
}
}
}

View File

@ -0,0 +1,24 @@
package wow.doge.mygame.utils
import monix.execution.atomic.AtomicAny
/**
* Useless
*/
sealed abstract class Tree[+T]
case class Node[T](data: T, children: AtomicAny[LazyList[Tree[T]]])
extends Tree[T] {
def add(data: T) = {
children.transform(children =>
Node(data, AtomicAny(LazyList[Tree[T]]())) #:: children
)
}
}
// case object Leaf extends Tree[Nothing]
case class Data(data: Int)
class TreeManager[T] {
// val root: AtomicAny[Tree[T]] = AtomicAny(Leaf)
def add(data: T, node: Node[T]) = {}
}