mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-20 02:59:19 +01:00
Add scalafmt
This commit is contained in:
parent
edd9916905
commit
ee4f2dcc3b
@ -29,6 +29,13 @@ test:
|
|||||||
script:
|
script:
|
||||||
- sbt test
|
- sbt test
|
||||||
|
|
||||||
|
scalafmt:
|
||||||
|
stage: test
|
||||||
|
before_script:
|
||||||
|
- sbt -v sbtVersion
|
||||||
|
script:
|
||||||
|
- sbt scalafmtCheckAll
|
||||||
|
|
||||||
build:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
before_script:
|
before_script:
|
||||||
|
|||||||
35
.scalafmt.conf
Normal file
35
.scalafmt.conf
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
version = 3.8.6
|
||||||
|
runner.dialect = scala213
|
||||||
|
preset = default
|
||||||
|
maxColumn = 120
|
||||||
|
indent.defnSite = 2
|
||||||
|
|
||||||
|
align = {
|
||||||
|
preset = none
|
||||||
|
openParenDefnSite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
newlines = {
|
||||||
|
source = keep
|
||||||
|
topLevelStatementBlankLines = [
|
||||||
|
{ blanks { before = 1, after = 1, beforeAll = -1, afterAll = -1 } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
binPack = {
|
||||||
|
preset = Oneline
|
||||||
|
literalsExclude = []
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrite = {
|
||||||
|
rules = [SortModifiers, Imports]
|
||||||
|
sortModifiers.preset = styleGuide
|
||||||
|
imports.sort = scalastyle
|
||||||
|
trailingCommas.style = multiple
|
||||||
|
insertBraces.minLines = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
docstrings = {
|
||||||
|
oneline = fold
|
||||||
|
wrap = keep
|
||||||
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")
|
|
||||||
@ -1,2 +1,2 @@
|
|||||||
# suppress inspection "UnusedProperty" for whole file
|
# suppress inspection "UnusedProperty" for whole file
|
||||||
sbt.version = 1.8.3
|
sbt.version = 1.10.7
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")
|
|
||||||
3
project/plugins.sbt
Normal file
3
project/plugins.sbt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1")
|
||||||
|
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
|
||||||
|
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4")
|
||||||
@ -28,7 +28,7 @@ class ColorScheme extends Logging {
|
|||||||
val bytes = java.lang.Long.parseLong(value.substring(1), 16)
|
val bytes = java.lang.Long.parseLong(value.substring(1), 16)
|
||||||
val color = if (value.length == 9) {
|
val color = if (value.length == 9) {
|
||||||
val rgb = bytes >> 8
|
val rgb = bytes >> 8
|
||||||
IntColor(rgb.toInt).toRGBANorm.withAlpha((bytes & 0xFF).toFloat / 255f)
|
IntColor(rgb.toInt).toRGBANorm.withAlpha((bytes & 0xff).toFloat / 255f)
|
||||||
} else IntColor(bytes.toInt).toRGBANorm
|
} else IntColor(bytes.toInt).toRGBANorm
|
||||||
entries.addOne((key, color))
|
entries.addOne((key, color))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,11 +30,8 @@ import scala.io.Source
|
|||||||
import scala.jdk.CollectionConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
import scala.util.{Failure, Success, Try, Using}
|
import scala.util.{Failure, Success, Try, Using}
|
||||||
|
|
||||||
object OcelotDesktop
|
// LoggingConfiguration configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
|
||||||
// This configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
|
object OcelotDesktop extends LoggingConfiguration with Logging {
|
||||||
extends LoggingConfiguration
|
|
||||||
with Logging
|
|
||||||
{
|
|
||||||
System.setProperty("awt.useSystemAAFontSettings", "on")
|
System.setProperty("awt.useSystemAAFontSettings", "on")
|
||||||
System.setProperty("swing.aatext", "true")
|
System.setProperty("swing.aatext", "true")
|
||||||
System.setProperty("LWJGL_WM_CLASS", "Ocelot Desktop")
|
System.setProperty("LWJGL_WM_CLASS", "Ocelot Desktop")
|
||||||
@ -70,11 +67,10 @@ object OcelotDesktop
|
|||||||
splashScreen.setStatus("Loading configuration...", 0.10f)
|
splashScreen.setStatus("Loading configuration...", 0.10f)
|
||||||
val customConfigPath = args.get(CommandLine.ConfigPath).flatten
|
val customConfigPath = args.get(CommandLine.ConfigPath).flatten
|
||||||
|
|
||||||
val desktopConfigPath: Path =
|
val desktopConfigPath: Path = {
|
||||||
if (customConfigPath.isDefined) {
|
if (customConfigPath.isDefined) {
|
||||||
Paths.get(customConfigPath.get)
|
Paths.get(customConfigPath.get)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// TODO: migration for old locations of ocelot.conf, can be safely removed later
|
// TODO: migration for old locations of ocelot.conf, can be safely removed later
|
||||||
// TODO: uncomment this line and delete everything below it when you're ready!
|
// TODO: uncomment this line and delete everything below it when you're ready!
|
||||||
// OcelotPaths.desktopConfig
|
// OcelotPaths.desktopConfig
|
||||||
@ -83,23 +79,24 @@ object OcelotDesktop
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (!Files.exists(newConfigPath)) {
|
if (!Files.exists(newConfigPath)) {
|
||||||
val oldConfigPath =
|
val oldConfigPath = {
|
||||||
if (SystemUtils.IS_OS_WINDOWS)
|
if (SystemUtils.IS_OS_WINDOWS)
|
||||||
Paths.get(OcelotPaths.windowsAppDataDirectoryName, "Ocelot", "ocelot.conf")
|
Paths.get(OcelotPaths.windowsAppDataDirectoryName, "Ocelot", "ocelot.conf")
|
||||||
else
|
else
|
||||||
Paths.get(OcelotPaths.linuxHomeDirectoryName, ".config", "ocelot", "ocelot.conf")
|
Paths.get(OcelotPaths.linuxHomeDirectoryName, ".config", "ocelot", "ocelot.conf")
|
||||||
|
}
|
||||||
|
|
||||||
if (Files.exists(oldConfigPath))
|
if (Files.exists(oldConfigPath))
|
||||||
Files.move(oldConfigPath, newConfigPath)
|
Files.move(oldConfigPath, newConfigPath)
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch {
|
|
||||||
case _: Throwable =>
|
case _: Throwable =>
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: end of upper todo <3
|
// TODO: end of upper todo <3
|
||||||
newConfigPath
|
newConfigPath
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Settings.load(desktopConfigPath)
|
Settings.load(desktopConfigPath)
|
||||||
|
|
||||||
@ -157,24 +154,28 @@ object OcelotDesktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val updateThread = new Thread(() => try {
|
val updateThread = new Thread(
|
||||||
val currentThread = Thread.currentThread()
|
() =>
|
||||||
|
try {
|
||||||
|
val currentThread = Thread.currentThread()
|
||||||
|
|
||||||
while (!currentThread.isInterrupted) {
|
while (!currentThread.isInterrupted) {
|
||||||
Profiler.measure("tick") {
|
Profiler.measure("tick") {
|
||||||
withTickLockAcquired {
|
withTickLockAcquired {
|
||||||
workspace.update()
|
workspace.update()
|
||||||
updateThreadTasks.run()
|
updateThreadTasks.run()
|
||||||
|
|
||||||
tpsCounter.tick()
|
tpsCounter.tick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker.waitNext()
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
|
case _: InterruptedException => // ignore
|
||||||
ticker.waitNext()
|
},
|
||||||
}
|
"update-thread",
|
||||||
} catch {
|
)
|
||||||
case _: InterruptedException => // ignore
|
|
||||||
}, "update-thread")
|
|
||||||
|
|
||||||
updateThread.start()
|
updateThread.start()
|
||||||
splashScreen.dispose()
|
splashScreen.dispose()
|
||||||
@ -184,7 +185,8 @@ object OcelotDesktop
|
|||||||
logger.info("Cleaning up")
|
logger.info("Cleaning up")
|
||||||
updateThread.interrupt()
|
updateThread.interrupt()
|
||||||
|
|
||||||
try updateThread.join() catch {
|
try updateThread.join()
|
||||||
|
catch {
|
||||||
case _: InterruptedException =>
|
case _: InterruptedException =>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +236,9 @@ object OcelotDesktop
|
|||||||
root.workspaceView.load(frontendNBT)
|
root.workspaceView.load(frontendNBT)
|
||||||
if (frontendNBT.hasKey("players")) {
|
if (frontendNBT.hasKey("players")) {
|
||||||
players.clear()
|
players.clear()
|
||||||
players.addAll(frontendNBT.getTagList("players", NBT.TAG_STRING).map((player: NBTTagString) => User(player.getString)))
|
players.addAll(
|
||||||
|
frontendNBT.getTagList("players", NBT.TAG_STRING).map((player: NBTTagString) => User(player.getString))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,12 +292,13 @@ object OcelotDesktop
|
|||||||
val oldPath = workspace.path
|
val oldPath = workspace.path
|
||||||
|
|
||||||
if (oldPath != outputPath) {
|
if (oldPath != outputPath) {
|
||||||
val (oldFiles, newFiles) =
|
val (oldFiles, newFiles) = {
|
||||||
Using.resources(Files.list(oldPath), Files.list(outputPath)) { (oldDirStream, newDirStream) =>
|
Using.resources(Files.list(oldPath), Files.list(outputPath)) { (oldDirStream, newDirStream) =>
|
||||||
val oldFiles = oldDirStream.iterator.asScala.toArray
|
val oldFiles = oldDirStream.iterator.asScala.toArray
|
||||||
val newFiles = newDirStream.iterator.asScala.toArray
|
val newFiles = newDirStream.iterator.asScala.toArray
|
||||||
(oldFiles, newFiles)
|
(oldFiles, newFiles)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val toRemove = newFiles.intersect(oldFiles)
|
val toRemove = newFiles.intersect(oldFiles)
|
||||||
|
|
||||||
@ -311,8 +316,7 @@ object OcelotDesktop
|
|||||||
|
|
||||||
if (Files.isDirectory(path)) {
|
if (Files.isDirectory(path)) {
|
||||||
FileUtils.copyDirectory(oldFile, newFile)
|
FileUtils.copyDirectory(oldFile, newFile)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
FileUtils.copyFile(oldFile, newFile)
|
FileUtils.copyFile(oldFile, newFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,11 +359,12 @@ object OcelotDesktop
|
|||||||
|
|
||||||
def saveAs(): Unit = showSaveDialog()
|
def saveAs(): Unit = showSaveDialog()
|
||||||
|
|
||||||
def showOpenDialog(): Unit =
|
def showOpenDialog(): Unit = {
|
||||||
showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) {
|
showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) {
|
||||||
case Some(dir) => load(dir)
|
case Some(dir) => load(dir)
|
||||||
case None => Success(())
|
case None => Success(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def load(dir: File): Try[Unit] = {
|
def load(dir: File): Try[Unit] = {
|
||||||
val path = Paths.get(dir.getCanonicalPath, "workspace.nbt")
|
val path = Paths.get(dir.getCanonicalPath, "workspace.nbt")
|
||||||
@ -410,7 +415,7 @@ object OcelotDesktop
|
|||||||
val result = f(selectedFile)
|
val result = f(selectedFile)
|
||||||
|
|
||||||
result match {
|
result match {
|
||||||
case f@Failure(_) => showFailureMessage(f)
|
case f @ Failure(_) => showFailureMessage(f)
|
||||||
case Success(_) =>
|
case Success(_) =>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -424,13 +429,13 @@ object OcelotDesktop
|
|||||||
|
|
||||||
new NotificationDialog(
|
new NotificationDialog(
|
||||||
s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.",
|
s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.",
|
||||||
NotificationType.Error
|
NotificationType.Error,
|
||||||
).addCloseButton().show()
|
).addCloseButton().show()
|
||||||
}
|
}
|
||||||
|
|
||||||
def showAddPlayerDialog(): Unit = new InputDialog(
|
def showAddPlayerDialog(): Unit = new InputDialog(
|
||||||
"Add new player",
|
"Add new player",
|
||||||
text => OcelotDesktop.selectPlayer(text)
|
text => OcelotDesktop.selectPlayer(text),
|
||||||
).show()
|
).show()
|
||||||
|
|
||||||
def player: User = if (players.nonEmpty) players.head else User("myself")
|
def player: User = if (players.nonEmpty) players.head else User("myself")
|
||||||
@ -464,12 +469,15 @@ object OcelotDesktop
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def prepareSavePath(path: Path)(continuation: => Unit): Unit = {
|
private def prepareSavePath(path: Path)(continuation: => Unit): Unit = {
|
||||||
val nonEmpty = try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty) catch {
|
val nonEmpty = {
|
||||||
case _: FileNotFoundException | _: NoSuchFileException =>
|
try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty)
|
||||||
logger.info(s"Save path $path does not exist: creating a new directory")
|
catch {
|
||||||
Files.createDirectory(path)
|
case _: FileNotFoundException | _: NoSuchFileException =>
|
||||||
|
logger.info(s"Save path $path does not exist: creating a new directory")
|
||||||
|
Files.createDirectory(path)
|
||||||
|
|
||||||
false
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonEmpty) {
|
if (nonEmpty) {
|
||||||
@ -478,7 +486,7 @@ object OcelotDesktop
|
|||||||
|Files in the save directory will be included in the workspace.
|
|Files in the save directory will be included in the workspace.
|
||||||
|They may be overwritten, causing loss of data.
|
|They may be overwritten, causing loss of data.
|
||||||
|Proceed with saving anyway?""".stripMargin,
|
|Proceed with saving anyway?""".stripMargin,
|
||||||
NotificationType.Warning
|
NotificationType.Warning,
|
||||||
) {
|
) {
|
||||||
addButton("Cancel") {
|
addButton("Cancel") {
|
||||||
close()
|
close()
|
||||||
@ -491,7 +499,7 @@ object OcelotDesktop
|
|||||||
} else continuation
|
} else continuation
|
||||||
}
|
}
|
||||||
|
|
||||||
private def showSaveDialog(continuation: => Unit): Unit =
|
private def showSaveDialog(continuation: => Unit): Unit = {
|
||||||
showFileChooserDialog(JFileChooser.SAVE_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
showFileChooserDialog(JFileChooser.SAVE_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
||||||
Try {
|
Try {
|
||||||
if (dir.nonEmpty) {
|
if (dir.nonEmpty) {
|
||||||
@ -512,6 +520,7 @@ object OcelotDesktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def showCloseConfirmationDialog(prompt: Option[String])(continuation: => Unit): Unit = {
|
private def showCloseConfirmationDialog(prompt: Option[String])(continuation: => Unit): Unit = {
|
||||||
if (UiHandler.root.modalDialogPool.children.exists(_.isInstanceOf[CloseConfirmationDialog])) {
|
if (UiHandler.root.modalDialogPool.children.exists(_.isInstanceOf[CloseConfirmationDialog])) {
|
||||||
|
|||||||
@ -81,19 +81,23 @@ object Settings extends Logging {
|
|||||||
if (config.hasPath(path)) config.getDouble(path)
|
if (config.hasPath(path)) config.getDouble(path)
|
||||||
else default
|
else default
|
||||||
|
|
||||||
def withValuePreserveOrigin(path: String, value: Any): Config =
|
def withValuePreserveOrigin(path: String, value: Any): Config = {
|
||||||
config.withValue(path,
|
config.withValue(
|
||||||
|
path,
|
||||||
if (config.hasPath(path))
|
if (config.hasPath(path))
|
||||||
ConfigValueFactory.fromAnyRef(value).withOrigin(config.getValue(path).origin())
|
ConfigValueFactory.fromAnyRef(value).withOrigin(config.getValue(path).origin())
|
||||||
else ConfigValueFactory.fromAnyRef(value)
|
else ConfigValueFactory.fromAnyRef(value),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def withValuePreserveOrigin(path: String, value: Option[Any]): Config =
|
def withValuePreserveOrigin(path: String, value: Option[Any]): Config = {
|
||||||
config.withValue(path,
|
config.withValue(
|
||||||
|
path,
|
||||||
if (config.hasPath(path))
|
if (config.hasPath(path))
|
||||||
ConfigValueFactory.fromAnyRef(value.orNull).withOrigin(config.getValue(path).origin())
|
ConfigValueFactory.fromAnyRef(value.orNull).withOrigin(config.getValue(path).origin())
|
||||||
else ConfigValueFactory.fromAnyRef(value.orNull)
|
else ConfigValueFactory.fromAnyRef(value.orNull),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def withValue(path: String, value: Int2D): Config = {
|
def withValue(path: String, value: Int2D): Config = {
|
||||||
config.withValue(path, ConfigValueFactory.fromIterable(util.Arrays.asList(value.x, value.y)))
|
config.withValue(path, ConfigValueFactory.fromIterable(util.Arrays.asList(value.x, value.y)))
|
||||||
@ -107,6 +111,7 @@ object Settings extends Logging {
|
|||||||
var isSet: Boolean = false
|
var isSet: Boolean = false
|
||||||
|
|
||||||
def this() = this(0, 0)
|
def this() = this(0, 0)
|
||||||
|
|
||||||
def this(list: util.List[Integer]) = {
|
def this(list: util.List[Integer]) = {
|
||||||
this()
|
this()
|
||||||
if (list.size() == 2) {
|
if (list.size() == 2) {
|
||||||
@ -143,12 +148,10 @@ object Settings extends Logging {
|
|||||||
logger.info(s"Loaded Ocelot Desktop configuration from: $path")
|
logger.info(s"Loaded Ocelot Desktop configuration from: $path")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
} catch {
|
||||||
catch {
|
|
||||||
case _: Throwable =>
|
case _: Throwable =>
|
||||||
logger.info(s"Failed to parse $path, using default Ocelot Desktop configuration.")
|
logger.info(s"Failed to parse $path, using default Ocelot Desktop configuration.")
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
if (stream != null)
|
if (stream != null)
|
||||||
stream.close()
|
stream.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,9 +15,7 @@ object Audio extends Logging {
|
|||||||
private val sources = new mutable.HashMap[SoundSource, Int]
|
private val sources = new mutable.HashMap[SoundSource, Int]
|
||||||
private var _disabled = true
|
private var _disabled = true
|
||||||
|
|
||||||
/**
|
/** Should be called _before_ initializing any sound-related resources */
|
||||||
* Should be called _before_ initializing any sound-related resources
|
|
||||||
*/
|
|
||||||
def init(): Unit = {
|
def init(): Unit = {
|
||||||
try {
|
try {
|
||||||
AL.create()
|
AL.create()
|
||||||
@ -39,7 +37,7 @@ object Audio extends Logging {
|
|||||||
def newStream(
|
def newStream(
|
||||||
soundCategory: SoundCategory.Value,
|
soundCategory: SoundCategory.Value,
|
||||||
pitch: Float = 1f,
|
pitch: Float = 1f,
|
||||||
volume: Float = 1f
|
volume: Float = 1f,
|
||||||
): (SoundStream, SoundSource) = {
|
): (SoundStream, SoundSource) = {
|
||||||
var source: SoundSource = null
|
var source: SoundSource = null
|
||||||
|
|
||||||
@ -188,7 +186,7 @@ object Audio extends Logging {
|
|||||||
AL10W.alSourcef(
|
AL10W.alSourcef(
|
||||||
sourceId,
|
sourceId,
|
||||||
AL10.AL_GAIN,
|
AL10.AL_GAIN,
|
||||||
source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster
|
source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,8 +19,8 @@ object BeepGenerator {
|
|||||||
val value = (math.signum(math.sin(angle)) * 8192).toShort
|
val value = (math.signum(math.sin(angle)) * 8192).toShort
|
||||||
offset += step
|
offset += step
|
||||||
if (offset > 1) offset -= 1
|
if (offset > 1) offset -= 1
|
||||||
data.put((value & 0xFF).toByte)
|
data.put((value & 0xff).toByte)
|
||||||
data.put(((value >> 8) & 0xFF).toByte)
|
data.put(((value >> 8) & 0xff).toByte)
|
||||||
}
|
}
|
||||||
if (data.hasRemaining) {
|
if (data.hasRemaining) {
|
||||||
for (_ <- 0 until pauseSampleCount) {
|
for (_ <- 0 until pauseSampleCount) {
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import java.nio.ByteBuffer
|
|||||||
|
|
||||||
object OggDecoder {
|
object OggDecoder {
|
||||||
def decode(input: InputStream): SoundSamples = {
|
def decode(input: InputStream): SoundSamples = {
|
||||||
val stream = new VorbisStream(new BasicStream(input).getLogicalStreams.iterator().next().asInstanceOf[LogicalOggStream])
|
val stream =
|
||||||
|
new VorbisStream(new BasicStream(input).getLogicalStreams.iterator().next().asInstanceOf[LogicalOggStream])
|
||||||
|
|
||||||
val rate = stream.getIdentificationHeader.getSampleRate
|
val rate = stream.getIdentificationHeader.getSampleRate
|
||||||
val channels = stream.getIdentificationHeader.getChannels
|
val channels = stream.getIdentificationHeader.getChannels
|
||||||
|
|||||||
@ -3,12 +3,10 @@ package ocelot.desktop.audio
|
|||||||
import scala.util.control
|
import scala.util.control
|
||||||
import scala.util.control.Exception.Catch
|
import scala.util.control.Exception.Catch
|
||||||
|
|
||||||
case class OpenAlException(func: String, errName: String, code: Int)
|
case class OpenAlException(func: String, errName: String, code: Int) extends Exception(s"OpenAL error: $func: $errName")
|
||||||
extends Exception(s"OpenAL error: $func: $errName")
|
|
||||||
|
|
||||||
object OpenAlException {
|
object OpenAlException {
|
||||||
def defaulting[T](default: => T): Catch[T] = control.Exception.failAsValue(classOf[OpenAlException])(default)
|
def defaulting[T](default: => T): Catch[T] = control.Exception.failAsValue(classOf[OpenAlException])(default)
|
||||||
|
|
||||||
def ignoring: Catch[Unit] = defaulting(())
|
def ignoring: Catch[Unit] = defaulting(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,8 +47,8 @@ class SoundBuffer(val file: String) extends Resource with Logging {
|
|||||||
val channels = AL10W.alGetBufferi(bufferId, AL10.AL_CHANNELS)
|
val channels = AL10W.alGetBufferi(bufferId, AL10.AL_CHANNELS)
|
||||||
val bits = AL10W.alGetBufferi(bufferId, AL10.AL_BITS)
|
val bits = AL10W.alGetBufferi(bufferId, AL10.AL_BITS)
|
||||||
|
|
||||||
sizeBytes * 8 / channels / bits
|
sizeBytes * 8 / channels / bits
|
||||||
}
|
}
|
||||||
|
|
||||||
case None => 0
|
case None => 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import scala.collection.mutable.ArrayBuffer
|
|||||||
|
|
||||||
object SoundBuffers extends Resource {
|
object SoundBuffers extends Resource {
|
||||||
lazy val MachineComputerRunning: SoundBuffer = load("/ocelot/desktop/sounds/machine/computer_running.ogg")
|
lazy val MachineComputerRunning: SoundBuffer = load("/ocelot/desktop/sounds/machine/computer_running.ogg")
|
||||||
|
|
||||||
lazy val MachineFloppyAccess: Array[SoundBuffer] = Array(
|
lazy val MachineFloppyAccess: Array[SoundBuffer] = Array(
|
||||||
load("/ocelot/desktop/sounds/machine/floppy_access1.ogg"),
|
load("/ocelot/desktop/sounds/machine/floppy_access1.ogg"),
|
||||||
load("/ocelot/desktop/sounds/machine/floppy_access2.ogg"),
|
load("/ocelot/desktop/sounds/machine/floppy_access2.ogg"),
|
||||||
@ -14,8 +15,10 @@ object SoundBuffers extends Resource {
|
|||||||
load("/ocelot/desktop/sounds/machine/floppy_access5.ogg"),
|
load("/ocelot/desktop/sounds/machine/floppy_access5.ogg"),
|
||||||
load("/ocelot/desktop/sounds/machine/floppy_access6.ogg"),
|
load("/ocelot/desktop/sounds/machine/floppy_access6.ogg"),
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val MachineFloppyEject: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_eject.ogg")
|
lazy val MachineFloppyEject: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_eject.ogg")
|
||||||
lazy val MachineFloppyInsert: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_insert.ogg")
|
lazy val MachineFloppyInsert: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_insert.ogg")
|
||||||
|
|
||||||
lazy val MachineHDDAccess: Array[SoundBuffer] = Array(
|
lazy val MachineHDDAccess: Array[SoundBuffer] = Array(
|
||||||
load("/ocelot/desktop/sounds/machine/hdd_access1.ogg"),
|
load("/ocelot/desktop/sounds/machine/hdd_access1.ogg"),
|
||||||
load("/ocelot/desktop/sounds/machine/hdd_access2.ogg"),
|
load("/ocelot/desktop/sounds/machine/hdd_access2.ogg"),
|
||||||
@ -43,7 +46,7 @@ object SoundBuffers extends Resource {
|
|||||||
|
|
||||||
lazy val NoteBlock: Map[String, SoundBuffer] = List(
|
lazy val NoteBlock: Map[String, SoundBuffer] = List(
|
||||||
"banjo", "basedrum", "bass", "bell", "bit", "chime", "cow_bell", "didgeridoo", "flute", "guitar",
|
"banjo", "basedrum", "bass", "bell", "bit", "chime", "cow_bell", "didgeridoo", "flute", "guitar",
|
||||||
"harp", "hat", "iron_xylophone", "pling", "snare", "xylophone"
|
"harp", "hat", "iron_xylophone", "pling", "snare", "xylophone",
|
||||||
).map(name => {
|
).map(name => {
|
||||||
(name, load(s"/ocelot/desktop/sounds/minecraft/note_block/$name.ogg"))
|
(name, load(s"/ocelot/desktop/sounds/minecraft/note_block/$name.ogg"))
|
||||||
}).toMap
|
}).toMap
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class SoundSource(
|
|||||||
val looping: Boolean,
|
val looping: Boolean,
|
||||||
val pitch: Float,
|
val pitch: Float,
|
||||||
var volume: Float,
|
var volume: Float,
|
||||||
var position: Vector3D = Vector3D(0, 0, 0)
|
var position: Vector3D = Vector3D(0, 0, 0),
|
||||||
) {
|
) {
|
||||||
def duration: Option[Duration] = kind match {
|
def duration: Option[Duration] = kind match {
|
||||||
case SoundSource.KindSoundBuffer(buffer) =>
|
case SoundSource.KindSoundBuffer(buffer) =>
|
||||||
@ -124,8 +124,12 @@ object SoundSource {
|
|||||||
SoundSource.fromBuffer(SoundBuffers.MinecraftClickRelease, SoundCategory.Interface)
|
SoundSource.fromBuffer(SoundBuffers.MinecraftClickRelease, SoundCategory.Interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val MinecraftExplosion: SoundSource = SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)
|
lazy val MinecraftExplosion: SoundSource =
|
||||||
|
SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)
|
||||||
|
|
||||||
lazy val MachineFloppyInsert: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
|
lazy val MachineFloppyInsert: SoundSource =
|
||||||
lazy val MachineFloppyEject: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, SoundCategory.Environment)
|
SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
|
||||||
|
|
||||||
|
lazy val MachineFloppyEject: SoundSource =
|
||||||
|
SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, SoundCategory.Environment)
|
||||||
}
|
}
|
||||||
@ -6,8 +6,8 @@ case class IntColor(color: Int) extends Color {
|
|||||||
override def toRGBA: RGBAColor = {
|
override def toRGBA: RGBAColor = {
|
||||||
RGBAColor(
|
RGBAColor(
|
||||||
(color >> 16).toShort,
|
(color >> 16).toShort,
|
||||||
((color >> 8) & 0xFF).toShort,
|
((color >> 8) & 0xff).toShort,
|
||||||
(color & 0xFF).toShort,
|
(color & 0xff).toShort,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ case class RGBAColor(r: Short, g: Short, b: Short, a: Short = 255) extends Color
|
|||||||
r.toFloat / 255f,
|
r.toFloat / 255f,
|
||||||
g.toFloat / 255f,
|
g.toFloat / 255f,
|
||||||
b.toFloat / 255f,
|
b.toFloat / 255f,
|
||||||
a.toFloat / 255f
|
a.toFloat / 255f,
|
||||||
)
|
)
|
||||||
|
|
||||||
override def toHSVA: HSVAColor = toRGBANorm.toHSVA
|
override def toHSVA: HSVAColor = toRGBANorm.toHSVA
|
||||||
|
|||||||
@ -18,7 +18,7 @@ case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Co
|
|||||||
|
|
||||||
def mapA(f: Float => Float): RGBAColorNorm = copy(a = f(a))
|
def mapA(f: Float => Float): RGBAColorNorm = copy(a = f(a))
|
||||||
|
|
||||||
final private def componentToLinear(x: Float): Float = {
|
private final def componentToLinear(x: Float): Float = {
|
||||||
if (x <= 0.0404482362771082)
|
if (x <= 0.0404482362771082)
|
||||||
x / 12.92f
|
x / 12.92f
|
||||||
else
|
else
|
||||||
|
|||||||
@ -43,7 +43,7 @@ class Camera extends Entity with GenericCamera with DeviceInfo {
|
|||||||
DeviceAttribute.Class -> DeviceClass.Multimedia,
|
DeviceAttribute.Class -> DeviceClass.Multimedia,
|
||||||
DeviceAttribute.Description -> "Dungeon Scanner 2.5D",
|
DeviceAttribute.Description -> "Dungeon Scanner 2.5D",
|
||||||
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
|
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
|
||||||
DeviceAttribute.Product -> webcamCapture.map(_.name).getOrElse("Blind Pirate")
|
DeviceAttribute.Product -> webcamCapture.map(_.name).getOrElse("Blind Pirate"),
|
||||||
)
|
)
|
||||||
|
|
||||||
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {
|
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {
|
||||||
|
|||||||
@ -19,11 +19,12 @@ import javax.sound.sampled.AudioFormat.Encoding
|
|||||||
import javax.sound.sampled.{AudioFormat, AudioSystem}
|
import javax.sound.sampled.{AudioFormat, AudioSystem}
|
||||||
|
|
||||||
class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
||||||
override val node: Component =
|
override val node: Component = {
|
||||||
Network
|
Network
|
||||||
.newNode(this, Visibility.Network)
|
.newNode(this, Visibility.Network)
|
||||||
.withComponent("openfm_radio", Visibility.Network)
|
.withComponent("openfm_radio", Visibility.Network)
|
||||||
.create()
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------- URL ---------------------------
|
// --------------------------- URL ---------------------------
|
||||||
|
|
||||||
@ -44,14 +45,18 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
private def playSynchronously(): Unit = {
|
private def playSynchronously(): Unit = {
|
||||||
try {
|
try {
|
||||||
// Trying to parse URL and sending request to host
|
// Trying to parse URL and sending request to host
|
||||||
val connection =
|
val connection = {
|
||||||
new URI(url.get)
|
new URI(url.get)
|
||||||
.toURL
|
.toURL
|
||||||
.openConnection
|
.openConnection
|
||||||
.asInstanceOf[HttpURLConnection]
|
.asInstanceOf[HttpURLConnection]
|
||||||
|
}
|
||||||
|
|
||||||
connection.setRequestMethod("GET")
|
connection.setRequestMethod("GET")
|
||||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
|
connection.setRequestProperty(
|
||||||
|
"User-Agent",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
|
||||||
|
)
|
||||||
connection.setRequestProperty("Content-Language", "en-US")
|
connection.setRequestProperty("Content-Language", "en-US")
|
||||||
connection.setDoInput(true)
|
connection.setDoInput(true)
|
||||||
connection.setDoOutput(true)
|
connection.setDoOutput(true)
|
||||||
@ -65,21 +70,22 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
2,
|
2,
|
||||||
4,
|
4,
|
||||||
44100,
|
44100,
|
||||||
false
|
false,
|
||||||
),
|
),
|
||||||
// Obtaining audio input stream from HTTP connection
|
// Obtaining audio input stream from HTTP connection
|
||||||
AudioSystem.getAudioInputStream(new BufferedInputStream(connection.getInputStream))
|
AudioSystem.getAudioInputStream(new BufferedInputStream(connection.getInputStream)),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Keeping input stream format parameters here to offload the reading loop
|
// Keeping input stream format parameters here to offload the reading loop
|
||||||
val inputStreamFormat = inputStream.getFormat
|
val inputStreamFormat = inputStream.getFormat
|
||||||
val inputStreamSampleRate = inputStreamFormat.getSampleRate.toInt
|
val inputStreamSampleRate = inputStreamFormat.getSampleRate.toInt
|
||||||
|
|
||||||
val inputStreamSoundSampleFormat =
|
val inputStreamSoundSampleFormat = {
|
||||||
if (inputStreamFormat.getChannels > 1)
|
if (inputStreamFormat.getChannels > 1)
|
||||||
Format.Stereo16
|
Format.Stereo16
|
||||||
else
|
else
|
||||||
Format.Mono16
|
Format.Mono16
|
||||||
|
}
|
||||||
|
|
||||||
// Creating Ocelot output sound stream
|
// Creating Ocelot output sound stream
|
||||||
val (outputStream, outputSource) = Audio.newStream(SoundCategory.Records, volume = volume)
|
val (outputStream, outputSource) = Audio.newStream(SoundCategory.Records, volume = volume)
|
||||||
@ -99,7 +105,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
.flip
|
.flip
|
||||||
.asInstanceOf[ByteBuffer],
|
.asInstanceOf[ByteBuffer],
|
||||||
inputStreamSampleRate,
|
inputStreamSampleRate,
|
||||||
inputStreamSoundSampleFormat
|
inputStreamSoundSampleFormat,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,8 +113,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info("OpenFM input audio stream has reached EOF, closing thread")
|
logger.info("OpenFM input audio stream has reached EOF, closing thread")
|
||||||
}
|
} catch {
|
||||||
catch {
|
|
||||||
case _: InterruptedException =>
|
case _: InterruptedException =>
|
||||||
case e: Exception => logger.error("OpenFM playback exception", e)
|
case e: Exception => logger.error("OpenFM playback exception", e)
|
||||||
}
|
}
|
||||||
@ -168,6 +173,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
|
|
||||||
private var _volume: Float = 1
|
private var _volume: Float = 1
|
||||||
def volume: Float = _volume
|
def volume: Float = _volume
|
||||||
|
|
||||||
def volume_=(value: Float): Unit = {
|
def volume_=(value: Float): Unit = {
|
||||||
_volume = value
|
_volume = value
|
||||||
|
|
||||||
@ -211,7 +217,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
// --------------------------- Screen color/text ---------------------------
|
// --------------------------- Screen color/text ---------------------------
|
||||||
|
|
||||||
private val defaultScreenText = "OpenFM"
|
private val defaultScreenText = "OpenFM"
|
||||||
private val defaultScreenColor = IntColor(0x0AFF0A)
|
private val defaultScreenColor = IntColor(0x0aff0a)
|
||||||
|
|
||||||
var screenColor: IntColor = defaultScreenColor
|
var screenColor: IntColor = defaultScreenColor
|
||||||
private var _screenText: String = defaultScreenText
|
private var _screenText: String = defaultScreenText
|
||||||
@ -272,23 +278,26 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
if (nbt.hasKey("url"))
|
if (nbt.hasKey("url"))
|
||||||
url = Option(nbt.getString("url"))
|
url = Option(nbt.getString("url"))
|
||||||
|
|
||||||
screenColor =
|
screenColor = {
|
||||||
if (nbt.hasKey("screenColor"))
|
if (nbt.hasKey("screenColor"))
|
||||||
IntColor(nbt.getInteger("screenColor"))
|
IntColor(nbt.getInteger("screenColor"))
|
||||||
else
|
else
|
||||||
defaultScreenColor
|
defaultScreenColor
|
||||||
|
}
|
||||||
|
|
||||||
screenText =
|
screenText = {
|
||||||
if (nbt.hasKey("screenText"))
|
if (nbt.hasKey("screenText"))
|
||||||
nbt.getString("screenText")
|
nbt.getString("screenText")
|
||||||
else
|
else
|
||||||
defaultScreenText
|
defaultScreenText
|
||||||
|
}
|
||||||
|
|
||||||
volume =
|
volume = {
|
||||||
if (nbt.hasKey("volume"))
|
if (nbt.hasKey("volume"))
|
||||||
nbt.getDouble("volume").toFloat
|
nbt.getDouble("volume").toFloat
|
||||||
else
|
else
|
||||||
1
|
1
|
||||||
|
}
|
||||||
|
|
||||||
isListenRedstone = nbt.hasKey("isListenRedstone") && nbt.getBoolean("isListenRedstone")
|
isListenRedstone = nbt.hasKey("isListenRedstone") && nbt.getBoolean("isListenRedstone")
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package ocelot.desktop.entity.traits
|
|||||||
|
|
||||||
import ocelot.desktop.entity.traits.OcelotInterface.LogEvent
|
import ocelot.desktop.entity.traits.OcelotInterface.LogEvent
|
||||||
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
|
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, result}
|
import totoro.ocelot.brain.entity.traits.{result, Entity, Environment}
|
||||||
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
|
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
|
||||||
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@ -21,7 +21,8 @@ trait OcelotInterface extends Entity with Environment {
|
|||||||
result()
|
result()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Callback(direct = true, doc = """function(): integer -- Returns the current Unix timestamp (UTC, in milliseconds).""")
|
@Callback(direct = true,
|
||||||
|
doc = """function(): integer -- Returns the current Unix timestamp (UTC, in milliseconds).""")
|
||||||
def getTimestamp(context: Context, args: Arguments): Array[AnyRef] = {
|
def getTimestamp(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
result(Instant.now().toEpochMilli)
|
result(Instant.now().toEpochMilli)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,25 +27,25 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
|
|||||||
def *(rhs: Vector3D): Vector3D = Vector3D(
|
def *(rhs: Vector3D): Vector3D = Vector3D(
|
||||||
x.x * rhs.x + y.x * rhs.y + z.x * rhs.z,
|
x.x * rhs.x + y.x * rhs.y + z.x * rhs.z,
|
||||||
x.y * rhs.x + y.y * rhs.y + z.y * rhs.z,
|
x.y * rhs.x + y.y * rhs.y + z.y * rhs.z,
|
||||||
x.z * rhs.x + y.z * rhs.y + z.z * rhs.z
|
x.z * rhs.x + y.z * rhs.y + z.z * rhs.z,
|
||||||
)
|
)
|
||||||
|
|
||||||
def *(rhs: Basis3D): Basis3D = Basis3D(
|
def *(rhs: Basis3D): Basis3D = Basis3D(
|
||||||
Vector3D(
|
Vector3D(
|
||||||
x.x * rhs.x.x + y.x * rhs.x.y + z.x * rhs.x.z,
|
x.x * rhs.x.x + y.x * rhs.x.y + z.x * rhs.x.z,
|
||||||
x.y * rhs.x.x + y.y * rhs.x.y + z.y * rhs.x.z,
|
x.y * rhs.x.x + y.y * rhs.x.y + z.y * rhs.x.z,
|
||||||
x.z * rhs.x.x + y.z * rhs.x.y + z.z * rhs.x.z
|
x.z * rhs.x.x + y.z * rhs.x.y + z.z * rhs.x.z,
|
||||||
),
|
),
|
||||||
Vector3D(
|
Vector3D(
|
||||||
x.x * rhs.y.x + y.x * rhs.y.y + z.x * rhs.y.z,
|
x.x * rhs.y.x + y.x * rhs.y.y + z.x * rhs.y.z,
|
||||||
x.y * rhs.y.x + y.y * rhs.y.y + z.y * rhs.y.z,
|
x.y * rhs.y.x + y.y * rhs.y.y + z.y * rhs.y.z,
|
||||||
x.z * rhs.y.x + y.z * rhs.y.y + z.z * rhs.y.z
|
x.z * rhs.y.x + y.z * rhs.y.y + z.z * rhs.y.z,
|
||||||
),
|
),
|
||||||
Vector3D(
|
Vector3D(
|
||||||
x.x * rhs.z.x + y.x * rhs.z.y + z.x * rhs.z.z,
|
x.x * rhs.z.x + y.x * rhs.z.y + z.x * rhs.z.z,
|
||||||
x.y * rhs.z.x + y.y * rhs.z.y + z.y * rhs.z.z,
|
x.y * rhs.z.x + y.y * rhs.z.y + z.y * rhs.z.z,
|
||||||
x.z * rhs.z.x + y.z * rhs.z.y + z.z * rhs.z.z
|
x.z * rhs.z.x + y.z * rhs.z.y + z.z * rhs.z.z,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def det: Float = x.x * (y.y * z.z - z.y * y.z) -
|
def det: Float = x.x * (y.y * z.z - z.y * y.z) -
|
||||||
@ -59,18 +59,18 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
|
|||||||
Vector3D(
|
Vector3D(
|
||||||
(y.y * z.z - z.y * y.z) * s,
|
(y.y * z.z - z.y * y.z) * s,
|
||||||
(x.z * z.y - x.y * z.z) * s,
|
(x.z * z.y - x.y * z.z) * s,
|
||||||
(x.y * y.z - x.z * y.y) * s
|
(x.y * y.z - x.z * y.y) * s,
|
||||||
),
|
),
|
||||||
Vector3D(
|
Vector3D(
|
||||||
(y.z * z.x - y.x * z.z) * s,
|
(y.z * z.x - y.x * z.z) * s,
|
||||||
(x.x * z.z - x.z * z.x) * s,
|
(x.x * z.z - x.z * z.x) * s,
|
||||||
(y.x * x.z - x.x * y.z) * s
|
(y.x * x.z - x.x * y.z) * s,
|
||||||
),
|
),
|
||||||
Vector3D(
|
Vector3D(
|
||||||
(y.x * z.y - z.x * y.y) * s,
|
(y.x * z.y - z.x * y.y) * s,
|
||||||
(z.x * x.y - x.x * z.y) * s,
|
(z.x * x.y - x.x * z.y) * s,
|
||||||
(x.x * y.y - y.x * x.y) * s
|
(x.x * y.y - y.x * x.y) * s,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,11 +3,12 @@ package ocelot.desktop.geometry
|
|||||||
object ProjectionMatrix3D {
|
object ProjectionMatrix3D {
|
||||||
def perspective(aspect: Float, fovY: Float, zNear: Float, zFar: Float): ProjectionMatrix3D = {
|
def perspective(aspect: Float, fovY: Float, zNear: Float, zFar: Float): ProjectionMatrix3D = {
|
||||||
val f = (1.0 / math.tan(math.toRadians(fovY) / 2.0)).toFloat
|
val f = (1.0 / math.tan(math.toRadians(fovY) / 2.0)).toFloat
|
||||||
|
// format: off
|
||||||
ProjectionMatrix3D(
|
ProjectionMatrix3D(
|
||||||
f / aspect, 0, 0, 0,
|
f / aspect, 0, 0, 0,
|
||||||
0, f, 0, 0,
|
0, f, 0, 0,
|
||||||
0, 0, (zFar + zNear) / (zNear - zFar), (2 * zFar * zNear) / (zNear - zFar),
|
0, 0, (zFar + zNear) / (zNear - zFar), (2 * zFar * zNear) / (zNear - zFar),
|
||||||
0, 0, -1, 0
|
0, 0, -1, 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15,7 +16,6 @@ object ProjectionMatrix3D {
|
|||||||
case class ProjectionMatrix3D(m11: Float, m12: Float, m13: Float, m14: Float,
|
case class ProjectionMatrix3D(m11: Float, m12: Float, m13: Float, m14: Float,
|
||||||
m21: Float, m22: Float, m23: Float, m24: Float,
|
m21: Float, m22: Float, m23: Float, m24: Float,
|
||||||
m31: Float, m32: Float, m33: Float, m34: Float,
|
m31: Float, m32: Float, m33: Float, m34: Float,
|
||||||
m41: Float, m42: Float, m43: Float, m44: Float)
|
m41: Float, m42: Float, m43: Float, m44: Float) {
|
||||||
{
|
|
||||||
def array: Array[Float] = Array(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)
|
def array: Array[Float] = Array(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,7 @@ case class Quaternion(x: Float, y: Float, z: Float, w: Float) {
|
|||||||
y * rhs.z - z * rhs.y + x * rhs.w + w * rhs.x,
|
y * rhs.z - z * rhs.y + x * rhs.w + w * rhs.x,
|
||||||
z * rhs.x - x * rhs.z + y * rhs.w + w * rhs.y,
|
z * rhs.x - x * rhs.z + y * rhs.w + w * rhs.y,
|
||||||
x * rhs.y - y * rhs.x + z * rhs.w + w * rhs.z,
|
x * rhs.y - y * rhs.x + z * rhs.w + w * rhs.z,
|
||||||
w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z
|
w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z,
|
||||||
)
|
)
|
||||||
|
|
||||||
def conj: Quaternion = Quaternion(-x, -y, -z, w)
|
def conj: Quaternion = Quaternion(-x, -y, -z, w)
|
||||||
@ -72,7 +72,7 @@ case class Quaternion(x: Float, y: Float, z: Float, w: Float) {
|
|||||||
def basis: Basis3D = Basis3D(
|
def basis: Basis3D = Basis3D(
|
||||||
Vector3D(1 - 2 * y * y - 2 * z * z, 2 * x * y + 2 * z * w, 2 * x * z - 2 * y * w),
|
Vector3D(1 - 2 * y * y - 2 * z * z, 2 * x * y + 2 * z * w, 2 * x * z - 2 * y * w),
|
||||||
Vector3D(2 * x * y - 2 * z * w, 1 - 2 * x * x - 2 * z * z, 2 * y * z + 2 * x * w),
|
Vector3D(2 * x * y - 2 * z * w, 1 - 2 * x * x - 2 * z * z, 2 * y * z + 2 * x * w),
|
||||||
Vector3D(2 * x * z + 2 * y * w, 2 * y * z - 2 * x * w, 1 - 2 * x * x - 2 * y * y)
|
Vector3D(2 * x * z + 2 * y * w, 2 * y * z - 2 * x * w, 1 - 2 * x * x - 2 * y * y),
|
||||||
)
|
)
|
||||||
|
|
||||||
def dot(that: Quaternion): Float = x * that.x + y * that.y + z * that.z + w * that.w
|
def dot(that: Quaternion): Float = x * that.x + y * that.y + z * that.z + w * that.w
|
||||||
|
|||||||
@ -86,7 +86,8 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
|
|||||||
Vector2D(x + w / 2f, y),
|
Vector2D(x + w / 2f, y),
|
||||||
Vector2D(x + w, y + h / 2f),
|
Vector2D(x + w, y + h / 2f),
|
||||||
Vector2D(x + w / 2f, y + h),
|
Vector2D(x + w / 2f, y + h),
|
||||||
Vector2D(x, y + h / 2f))
|
Vector2D(x, y + h / 2f),
|
||||||
|
)
|
||||||
|
|
||||||
def distanceTo(that: Rect2D): Float = {
|
def distanceTo(that: Rect2D): Float = {
|
||||||
((center - that.center).abs - (extent + that.extent)).max(Vector2D(0, 0)).length
|
((center - that.center).abs - (extent + that.extent)).max(Vector2D(0, 0)).length
|
||||||
|
|||||||
@ -32,7 +32,7 @@ case class Size2D(width: Float, height: Float) {
|
|||||||
def clamped(min: Size2D, max: Size2D): Size2D = {
|
def clamped(min: Size2D, max: Size2D): Size2D = {
|
||||||
Size2D(
|
Size2D(
|
||||||
math.min(max.width, math.max(min.width, width)),
|
math.min(max.width, math.max(min.width, width)),
|
||||||
math.min(max.height, math.max(min.height, height))
|
math.min(max.height, math.max(min.height, height)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,19 +5,19 @@ import java.nio.ByteBuffer
|
|||||||
object Transform2D {
|
object Transform2D {
|
||||||
def identity: Transform2D = Transform2D(
|
def identity: Transform2D = Transform2D(
|
||||||
1, 0, 0,
|
1, 0, 0,
|
||||||
0, 1, 0
|
0, 1, 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
def scale(x: Float, y: Float): Transform2D = Transform2D(
|
def scale(x: Float, y: Float): Transform2D = Transform2D(
|
||||||
x, 0, 0,
|
x, 0, 0,
|
||||||
0, y, 0
|
0, y, 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
def scale(a: Float): Transform2D = Transform2D.scale(a, a)
|
def scale(a: Float): Transform2D = Transform2D.scale(a, a)
|
||||||
|
|
||||||
def translate(x: Float, y: Float): Transform2D = Transform2D(
|
def translate(x: Float, y: Float): Transform2D = Transform2D(
|
||||||
1, 0, x,
|
1, 0, x,
|
||||||
0, 1, y
|
0, 1, y,
|
||||||
)
|
)
|
||||||
|
|
||||||
def viewport(width: Float, height: Float): Transform2D =
|
def viewport(width: Float, height: Float): Transform2D =
|
||||||
@ -26,6 +26,7 @@ object Transform2D {
|
|||||||
def rotate(angle: Float): Transform2D = {
|
def rotate(angle: Float): Transform2D = {
|
||||||
val (s, c) = (math.sin(angle).asInstanceOf[Float], math.cos(angle).asInstanceOf[Float])
|
val (s, c) = (math.sin(angle).asInstanceOf[Float], math.cos(angle).asInstanceOf[Float])
|
||||||
|
|
||||||
|
// format: off
|
||||||
Transform2D(
|
Transform2D(
|
||||||
c, -s, 0,
|
c, -s, 0,
|
||||||
s, c, 0
|
s, c, 0
|
||||||
@ -36,13 +37,13 @@ object Transform2D {
|
|||||||
case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Float, m23: Float) {
|
case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Float, m23: Float) {
|
||||||
def array: Array[Float] = Array(m11, m12, m13, m21, m22, m23)
|
def array: Array[Float] = Array(m11, m12, m13, m21, m22, m23)
|
||||||
|
|
||||||
// :|
|
// format: off
|
||||||
def >>(that: Transform2D): Transform2D = Transform2D(
|
def >>(that: Transform2D): Transform2D = Transform2D(
|
||||||
m11 * that.m11 + m12 * that.m21, m11 * that.m12 + m12 * that.m22, m11 * that.m13 + m12 * that.m23 + m13,
|
m11 * that.m11 + m12 * that.m21, m11 * that.m12 + m12 * that.m22, m11 * that.m13 + m12 * that.m23 + m13,
|
||||||
m21 * that.m11 + m22 * that.m21, m21 * that.m12 + m22 * that.m22, m21 * that.m13 + m22 * that.m23 + m23,
|
m21 * that.m11 + m22 * that.m21, m21 * that.m12 + m22 * that.m22, m21 * that.m13 + m22 * that.m23 + m23,
|
||||||
)
|
)
|
||||||
|
|
||||||
// (●__●)
|
// format: off
|
||||||
def <<(that: Transform2D): Transform2D = Transform2D(
|
def <<(that: Transform2D): Transform2D = Transform2D(
|
||||||
m11 * that.m11 + m21 * that.m12, m12 * that.m11 + m22 * that.m12, m13 * that.m11 + m23 * that.m12 + that.m13,
|
m11 * that.m11 + m21 * that.m12, m12 * that.m11 + m22 * that.m12, m13 * that.m11 + m23 * that.m12 + that.m13,
|
||||||
m11 * that.m21 + m21 * that.m22, m12 * that.m21 + m22 * that.m22, m13 * that.m21 + m23 * that.m22 + that.m23
|
m11 * that.m21 + m21 * that.m22, m12 * that.m21 + m22 * that.m22, m13 * that.m21 + m23 * that.m22 + that.m23
|
||||||
@ -50,13 +51,14 @@ case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Floa
|
|||||||
|
|
||||||
def *(that: Vector2D): Vector2D = Vector2D(
|
def *(that: Vector2D): Vector2D = Vector2D(
|
||||||
m11 * that.x + m12 * that.y + m13,
|
m11 * that.x + m12 * that.y + m13,
|
||||||
m21 * that.x + m22 * that.y + m23
|
m21 * that.x + m22 * that.y + m23,
|
||||||
)
|
)
|
||||||
|
|
||||||
override def toString: String =
|
override def toString: String = {
|
||||||
f"""Transform2D [$m11%6.3f $m12%6.3f $m13%6.3f]
|
f"""Transform2D [$m11%6.3f $m12%6.3f $m13%6.3f]
|
||||||
| [$m21%6.3f $m22%6.3f $m23%6.3f]
|
| [$m21%6.3f $m22%6.3f $m23%6.3f]
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
|
}
|
||||||
|
|
||||||
// (╯°□°)╯︵ ┻━┻
|
// (╯°□°)╯︵ ┻━┻
|
||||||
def put(buffer: ByteBuffer): Unit = {
|
def put(buffer: ByteBuffer): Unit = {
|
||||||
|
|||||||
@ -26,6 +26,7 @@ object Transform3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case class Transform3D(basis: Basis3D, origin: Vector3D) {
|
case class Transform3D(basis: Basis3D, origin: Vector3D) {
|
||||||
|
// format: off
|
||||||
def array: Array[Float] = Array(
|
def array: Array[Float] = Array(
|
||||||
basis.x.x, basis.y.x, basis.z.x, origin.x,
|
basis.x.x, basis.y.x, basis.z.x, origin.x,
|
||||||
basis.x.y, basis.y.y, basis.z.y, origin.y,
|
basis.x.y, basis.y.y, basis.z.y, origin.y,
|
||||||
@ -44,8 +45,8 @@ case class Transform3D(basis: Basis3D, origin: Vector3D) {
|
|||||||
override def toString: String = s"Transform3D [${basis.x}, ${basis.y}, ${basis.z}, $origin]"
|
override def toString: String = s"Transform3D [${basis.x}, ${basis.y}, ${basis.z}, $origin]"
|
||||||
|
|
||||||
def put(buffer: ByteBuffer): Unit = {
|
def put(buffer: ByteBuffer): Unit = {
|
||||||
buffer.putFloat(basis.x.x); buffer.putFloat(basis.y.x); buffer.putFloat(basis.z.x); buffer.putFloat(origin.x);
|
buffer.putFloat(basis.x.x); buffer.putFloat(basis.y.x); buffer.putFloat(basis.z.x); buffer.putFloat(origin.x)
|
||||||
buffer.putFloat(basis.x.y); buffer.putFloat(basis.y.y); buffer.putFloat(basis.z.y); buffer.putFloat(origin.y);
|
buffer.putFloat(basis.x.y); buffer.putFloat(basis.y.y); buffer.putFloat(basis.z.y); buffer.putFloat(origin.y)
|
||||||
buffer.putFloat(basis.x.z); buffer.putFloat(basis.y.z); buffer.putFloat(basis.z.z); buffer.putFloat(origin.z);
|
buffer.putFloat(basis.x.z); buffer.putFloat(basis.y.z); buffer.putFloat(basis.z.z); buffer.putFloat(origin.z)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ case class Vector3D(x: Float, y: Float, z: Float) {
|
|||||||
def cross(that: Vector3D): Vector3D = Vector3D(
|
def cross(that: Vector3D): Vector3D = Vector3D(
|
||||||
y * that.z - z * that.y,
|
y * that.z - z * that.y,
|
||||||
z * that.x - x * that.z,
|
z * that.x - x * that.z,
|
||||||
x * that.y - y * that.x
|
x * that.y - y * that.x,
|
||||||
)
|
)
|
||||||
|
|
||||||
def angle(that: Vector3D): Float = {
|
def angle(that: Vector3D): Float = {
|
||||||
|
|||||||
@ -17,7 +17,8 @@ import scala.collection.mutable
|
|||||||
import scala.util.control.Breaks._
|
import scala.util.control.Breaks._
|
||||||
|
|
||||||
//noinspection ScalaWeakerAccess,ScalaUnusedSymbol
|
//noinspection ScalaWeakerAccess,ScalaUnusedSymbol
|
||||||
class Graphics(private var width: Int, private var height: Int, private var scalingFactor: Float) extends Logging with Resource {
|
class Graphics(private var width: Int, private var height: Int, private var scalingFactor: Float)
|
||||||
|
extends Logging with Resource {
|
||||||
private var time = 0f
|
private var time = 0f
|
||||||
|
|
||||||
private var projection = Transform2D.viewport(width, height)
|
private var projection = Transform2D.viewport(width, height)
|
||||||
@ -33,12 +34,15 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
private val stack = mutable.Stack[GraphicsState](GraphicsState())
|
private val stack = mutable.Stack[GraphicsState](GraphicsState())
|
||||||
|
|
||||||
private var spriteRect = Spritesheet.sprites("Empty")
|
private var spriteRect = Spritesheet.sprites("Empty")
|
||||||
private val emptySpriteTrans = Transform2D.translate(spriteRect.x, spriteRect.y) >> Transform2D.scale(spriteRect.w, spriteRect.h)
|
|
||||||
|
private val emptySpriteTrans =
|
||||||
|
Transform2D.translate(spriteRect.x, spriteRect.y) >> Transform2D.scale(spriteRect.w, spriteRect.h)
|
||||||
|
|
||||||
private val offscreenTexture = new Texture(width, height, GL21.GL_SRGB8_ALPHA8, GL11.GL_UNSIGNED_BYTE, GL11.GL_RGBA)
|
private val offscreenTexture = new Texture(width, height, GL21.GL_SRGB8_ALPHA8, GL11.GL_UNSIGNED_BYTE, GL11.GL_RGBA)
|
||||||
|
|
||||||
private val offscreenFramebuffer = ARBFramebufferObject.glGenFramebuffers()
|
private val offscreenFramebuffer = ARBFramebufferObject.glGenFramebuffers()
|
||||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, offscreenFramebuffer)
|
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, offscreenFramebuffer)
|
||||||
|
|
||||||
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D,
|
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D,
|
||||||
offscreenTexture.texture, 0)
|
offscreenTexture.texture, 0)
|
||||||
|
|
||||||
@ -152,7 +156,7 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
viewport: ScreenViewport,
|
viewport: ScreenViewport,
|
||||||
bounds: Rect2D,
|
bounds: Rect2D,
|
||||||
filteringMode: MinFilteringMode = MinFilteringMode.Nearest,
|
filteringMode: MinFilteringMode = MinFilteringMode.Nearest,
|
||||||
alpha: Float = 1.0f
|
alpha: Float = 1.0f,
|
||||||
): Unit = {
|
): Unit = {
|
||||||
flush()
|
flush()
|
||||||
foreground = RGBAColorNorm(1, 1, 1, alpha)
|
foreground = RGBAColorNorm(1, 1, 1, alpha)
|
||||||
@ -255,16 +259,17 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
|
|
||||||
val uvTransform = Transform2D.translate(
|
val uvTransform = Transform2D.translate(
|
||||||
rect.x,
|
rect.x,
|
||||||
rect.y
|
rect.y,
|
||||||
) >> Transform2D.scale(
|
) >> Transform2D.scale(
|
||||||
rect.w - 0.25f / _font.AtlasWidth,
|
rect.w - 0.25f / _font.AtlasWidth,
|
||||||
rect.h - 0.25f / _font.AtlasHeight
|
rect.h - 0.25f / _font.AtlasHeight,
|
||||||
)
|
)
|
||||||
|
|
||||||
val transform =
|
val transform = {
|
||||||
stack.head.transform >>
|
stack.head.transform >>
|
||||||
Transform2D.translate(x.round, y.round) >>
|
Transform2D.translate(x.round, y.round) >>
|
||||||
Transform2D.scale(_font.charWidth(c), fontSize)
|
Transform2D.scale(_font.charWidth(c), fontSize)
|
||||||
|
}
|
||||||
|
|
||||||
val foreground = stack.head.foreground.toRGBANorm.mapA(_ * alphaMultiplier)
|
val foreground = stack.head.foreground.toRGBANorm.mapA(_ * alphaMultiplier)
|
||||||
val background = stack.head.background.toRGBANorm.mapA(_ * alphaMultiplier)
|
val background = stack.head.background.toRGBANorm.mapA(_ * alphaMultiplier)
|
||||||
@ -333,8 +338,7 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
|
|
||||||
def sprite(name: String, x: Float, y: Float, width: Float, height: Float,
|
def sprite(name: String, x: Float, y: Float, width: Float, height: Float,
|
||||||
color: Color = Color.White,
|
color: Color = Color.White,
|
||||||
animation: Option[Animation] = None): Unit =
|
animation: Option[Animation] = None): Unit = {
|
||||||
{
|
|
||||||
sprite = name
|
sprite = name
|
||||||
foreground = color
|
foreground = color
|
||||||
_rect(x, y, width, height, fixUV = true, animation)
|
_rect(x, y, width, height, fixUV = true, animation)
|
||||||
@ -385,14 +389,15 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
|
|
||||||
val uvTransform = Transform2D.translate(spriteRect.x, spriteRect.y) >>
|
val uvTransform = Transform2D.translate(spriteRect.x, spriteRect.y) >>
|
||||||
(if (fixUV)
|
(if (fixUV)
|
||||||
Transform2D.scale(spriteRect.w - 0.25f / 1024, spriteRect.h - 0.25f / 1024)
|
Transform2D.scale(spriteRect.w - 0.25f / 1024, spriteRect.h - 0.25f / 1024)
|
||||||
else
|
else
|
||||||
Transform2D.scale(spriteRect.w, spriteRect.h))
|
Transform2D.scale(spriteRect.w, spriteRect.h))
|
||||||
|
|
||||||
val transform =
|
val transform = {
|
||||||
stack.head.transform >>
|
stack.head.transform >>
|
||||||
Transform2D.translate(x, y) >>
|
Transform2D.translate(x, y) >>
|
||||||
Transform2D.scale(width, height)
|
Transform2D.scale(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
val color = stack.head.foreground.toRGBANorm.mapA(_ * alphaMultiplier)
|
val color = stack.head.foreground.toRGBANorm.mapA(_ * alphaMultiplier)
|
||||||
|
|
||||||
@ -426,9 +431,9 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
for (y <- 0 until height) {
|
for (y <- 0 until height) {
|
||||||
for (x <- 0 until width) {
|
for (x <- 0 until width) {
|
||||||
val i = (x + (height - y - 1) * width) * 4
|
val i = (x + (height - y - 1) * width) * 4
|
||||||
val r = buffer.get(i) & 0xFF
|
val r = buffer.get(i) & 0xff
|
||||||
val g = buffer.get(i + 1) & 0xFF
|
val g = buffer.get(i + 1) & 0xff
|
||||||
val b = buffer.get(i + 2) & 0xFF
|
val b = buffer.get(i + 2) & 0xff
|
||||||
val rgb = (r << 16) | (g << 8) | b
|
val rgb = (r << 16) | (g << 8) | b
|
||||||
image.setRGB(x, y, rgb)
|
image.setRGB(x, y, rgb)
|
||||||
}
|
}
|
||||||
@ -456,7 +461,7 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
Math.round(x * scalingFactor),
|
Math.round(x * scalingFactor),
|
||||||
Math.round(height - h * scalingFactor - y * scalingFactor),
|
Math.round(height - h * scalingFactor - y * scalingFactor),
|
||||||
Math.round(w * scalingFactor),
|
Math.round(w * scalingFactor),
|
||||||
Math.round(h * scalingFactor)
|
Math.round(h * scalingFactor),
|
||||||
)
|
)
|
||||||
case _ =>
|
case _ =>
|
||||||
GL11.glDisable(GL11.GL_SCISSOR_TEST)
|
GL11.glDisable(GL11.GL_SCISSOR_TEST)
|
||||||
|
|||||||
@ -10,5 +10,5 @@ case class GraphicsState(
|
|||||||
var alphaMultiplier: Float = 1f,
|
var alphaMultiplier: Float = 1f,
|
||||||
var sprite: String = "Empty",
|
var sprite: String = "Empty",
|
||||||
var scissor: Option[(Float, Float, Float, Float)] = None,
|
var scissor: Option[(Float, Float, Float, Float)] = None,
|
||||||
var transform: Transform2D = Transform2D.identity
|
var transform: Transform2D = Transform2D.identity,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -94,7 +94,7 @@ object IconSource {
|
|||||||
|
|
||||||
val DiskDriveMountable: IconSource = IconSource("items/DiskDriveMountable")
|
val DiskDriveMountable: IconSource = IconSource("items/DiskDriveMountable")
|
||||||
|
|
||||||
//noinspection ScalaWeakerAccess
|
// noinspection ScalaWeakerAccess
|
||||||
object Animations {
|
object Animations {
|
||||||
val Apu: Animation =
|
val Apu: Animation =
|
||||||
Animation((0, 3f), (1, 3f), (2, 3f), (3, 3f), (4, 3f), (5, 3f), (4, 3f), (3, 3f), (2, 3f), (1, 3f), (0, 3f))
|
Animation((0, 3f), (1, 3f), (2, 3f), (3, 3f), (4, 3f), (5, 3f), (4, 3f), (3, 3f), (2, 3f), (1, 3f), (0, 3f))
|
||||||
@ -115,6 +115,7 @@ object IconSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case class Animation(frames: Array[(Int, Float)], frameSize: Option[Size2D])
|
case class Animation(frames: Array[(Int, Float)], frameSize: Option[Size2D])
|
||||||
|
|
||||||
object Animation {
|
object Animation {
|
||||||
def apply(frames: (Int, Float)*) = new Animation(frames.toArray, None)
|
def apply(frames: (Int, Float)*) = new Animation(frames.toArray, None)
|
||||||
def apply(size: Size2D, frames: (Int, Float)*) = new Animation(frames.toArray, Some(size))
|
def apply(size: Size2D, frames: (Int, Float)*) = new Animation(frames.toArray, Some(size))
|
||||||
@ -127,23 +128,26 @@ object IconSource {
|
|||||||
IconSource(s"icons/Notification$notificationType")
|
IconSource(s"icons/Notification$notificationType")
|
||||||
}
|
}
|
||||||
|
|
||||||
val Loading: IconSource = IconSource("Loading", animation = Some(Animation(
|
val Loading: IconSource = IconSource(
|
||||||
Size2D(48, 32),
|
"Loading",
|
||||||
(0, 0.7f),
|
animation = Some(Animation(
|
||||||
(1, 0.7f),
|
Size2D(48, 32),
|
||||||
(2, 0.7f),
|
(0, 0.7f),
|
||||||
(3, 0.7f),
|
(1, 0.7f),
|
||||||
(4, 0.7f),
|
(2, 0.7f),
|
||||||
(5, 0.7f),
|
(3, 0.7f),
|
||||||
(6, 0.7f),
|
(4, 0.7f),
|
||||||
(7, 0.7f),
|
(5, 0.7f),
|
||||||
(8, 0.7f),
|
(6, 0.7f),
|
||||||
(9, 0.7f),
|
(7, 0.7f),
|
||||||
(10, 0.7f),
|
(8, 0.7f),
|
||||||
(11, 0.7f),
|
(9, 0.7f),
|
||||||
(12, 0.7f),
|
(10, 0.7f),
|
||||||
(13, 0.7f),
|
(11, 0.7f),
|
||||||
)))
|
(12, 0.7f),
|
||||||
|
(13, 0.7f),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
|
||||||
val SettingsSystem: IconSource = IconSource("icons/SettingsSystem")
|
val SettingsSystem: IconSource = IconSource("icons/SettingsSystem")
|
||||||
val SettingsSound: IconSource = IconSource("icons/SettingsSound")
|
val SettingsSound: IconSource = IconSource("icons/SettingsSound")
|
||||||
|
|||||||
@ -21,6 +21,7 @@ class ScreenViewport(graphics: Graphics, private var _width: Int, private var _h
|
|||||||
|
|
||||||
private val _font = graphics.normalFont
|
private val _font = graphics.normalFont
|
||||||
private val spriteRect = Spritesheet.sprites("Empty")
|
private val spriteRect = Spritesheet.sprites("Empty")
|
||||||
|
|
||||||
private val emptySpriteTrans =
|
private val emptySpriteTrans =
|
||||||
Transform2D.translate(spriteRect.x, spriteRect.y) >> Transform2D.scale(spriteRect.w, spriteRect.h)
|
Transform2D.translate(spriteRect.x, spriteRect.y) >> Transform2D.scale(spriteRect.w, spriteRect.h)
|
||||||
|
|
||||||
|
|||||||
@ -12,10 +12,15 @@ class ShaderProgram(name: String) extends Logging with Resource {
|
|||||||
|
|
||||||
private val fragmentShader: Int = createShader(
|
private val fragmentShader: Int = createShader(
|
||||||
Source.fromResource(s"ocelot/desktop/shader/$name.frag", getClass.getClassLoader),
|
Source.fromResource(s"ocelot/desktop/shader/$name.frag", getClass.getClassLoader),
|
||||||
GL20.GL_FRAGMENT_SHADER, "fragment")
|
GL20.GL_FRAGMENT_SHADER,
|
||||||
|
"fragment",
|
||||||
|
)
|
||||||
|
|
||||||
private val vertexShader: Int = createShader(
|
private val vertexShader: Int = createShader(
|
||||||
Source.fromResource(s"ocelot/desktop/shader/$name.vert", getClass.getClassLoader),
|
Source.fromResource(s"ocelot/desktop/shader/$name.vert", getClass.getClassLoader),
|
||||||
GL20.GL_VERTEX_SHADER, "vertex")
|
GL20.GL_VERTEX_SHADER,
|
||||||
|
"vertex",
|
||||||
|
)
|
||||||
|
|
||||||
val shaderProgram: Int = GL20.glCreateProgram()
|
val shaderProgram: Int = GL20.glCreateProgram()
|
||||||
|
|
||||||
|
|||||||
@ -28,23 +28,25 @@ class Texture() extends Logging with Resource {
|
|||||||
for (y <- 0 until image.getHeight) {
|
for (y <- 0 until image.getHeight) {
|
||||||
for (x <- 0 until image.getWidth) {
|
for (x <- 0 until image.getWidth) {
|
||||||
val pixel = pixels(y * image.getWidth + x)
|
val pixel = pixels(y * image.getWidth + x)
|
||||||
buf.put(((pixel >> 16) & 0xFF).toByte)
|
buf.put(((pixel >> 16) & 0xff).toByte)
|
||||||
buf.put(((pixel >> 8) & 0xFF).toByte)
|
buf.put(((pixel >> 8) & 0xff).toByte)
|
||||||
buf.put((pixel & 0xFF).toByte)
|
buf.put((pixel & 0xff).toByte)
|
||||||
buf.put(((pixel >> 24) & 0xFF).toByte)
|
buf.put(((pixel >> 24) & 0xff).toByte)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.flip()
|
buf.flip()
|
||||||
|
|
||||||
bind()
|
bind()
|
||||||
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL21.GL_SRGB8_ALPHA8, image.getWidth, image.getHeight, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf)
|
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL21.GL_SRGB8_ALPHA8, image.getWidth, image.getHeight, 0, GL11.GL_RGBA,
|
||||||
|
GL11.GL_UNSIGNED_BYTE, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
def this(width: Int, height: Int, format: Int, dataType: Int, internalFormat: Int) = {
|
def this(width: Int, height: Int, format: Int, dataType: Int, internalFormat: Int) = {
|
||||||
this()
|
this()
|
||||||
bind()
|
bind()
|
||||||
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, format, width, height, 0, internalFormat, dataType, null.asInstanceOf[ByteBuffer])
|
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, format, width, height, 0, internalFormat, dataType,
|
||||||
|
null.asInstanceOf[ByteBuffer])
|
||||||
}
|
}
|
||||||
|
|
||||||
def this(width: Int, height: Int, format: Int, dataType: Int) = {
|
def this(width: Int, height: Int, format: Int, dataType: Int) = {
|
||||||
@ -88,7 +90,7 @@ class Texture() extends Logging with Resource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Texture {
|
object Texture {
|
||||||
class MinFilteringMode private(private[Texture] val glValue: Int, val needsMipmap: Boolean)
|
class MinFilteringMode private (private[Texture] val glValue: Int, val needsMipmap: Boolean)
|
||||||
|
|
||||||
object MinFilteringMode {
|
object MinFilteringMode {
|
||||||
val Nearest = new MinFilteringMode(GL11.GL_NEAREST, false)
|
val Nearest = new MinFilteringMode(GL11.GL_NEAREST, false)
|
||||||
|
|||||||
@ -14,8 +14,13 @@ class Viewport3D(width: Int, height: Int) extends Resource with Logging {
|
|||||||
|
|
||||||
private val framebuffer = ARBFramebufferObject.glGenFramebuffers()
|
private val framebuffer = ARBFramebufferObject.glGenFramebuffers()
|
||||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer)
|
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer)
|
||||||
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, textureColor.texture, 0)
|
|
||||||
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL11.GL_TEXTURE_2D, textureDepth.texture, 0)
|
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, textureColor.texture,
|
||||||
|
0)
|
||||||
|
|
||||||
|
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL11.GL_TEXTURE_2D, textureDepth.texture,
|
||||||
|
0)
|
||||||
|
|
||||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0)
|
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0)
|
||||||
|
|
||||||
private type Instancer = InstanceRenderer[MeshVertex3D, MeshInstance3D]
|
private type Instancer = InstanceRenderer[MeshVertex3D, MeshInstance3D]
|
||||||
@ -84,7 +89,8 @@ class Viewport3D(width: Int, height: Int) extends Resource with Logging {
|
|||||||
private def setupLight(scene: Scene3D): Unit = {
|
private def setupLight(scene: Scene3D): Unit = {
|
||||||
shaderProgram.set("uLightDir", scene.lightSource.basis.up)
|
shaderProgram.set("uLightDir", scene.lightSource.basis.up)
|
||||||
shaderProgram.set("uLightColor", scene.lightSource.color.toRGBANorm.toLinear.rgbVector * scene.lightSource.energy)
|
shaderProgram.set("uLightColor", scene.lightSource.color.toRGBANorm.toLinear.rgbVector * scene.lightSource.energy)
|
||||||
shaderProgram.set("uAmbientLightColor", scene.ambientLightColor.toRGBANorm.toLinear.rgbVector * scene.ambientLightEnergy)
|
shaderProgram.set("uAmbientLightColor",
|
||||||
|
scene.ambientLightColor.toRGBANorm.toLinear.rgbVector * scene.ambientLightEnergy)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def collectOpaque(scene: Scene3D): Unit = {
|
private def collectOpaque(scene: Scene3D): Unit = {
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import ocelot.desktop.graphics.buffer.Index
|
|||||||
object Mesh2D {
|
object Mesh2D {
|
||||||
val quad: Mesh2D = new Mesh2D(
|
val quad: Mesh2D = new Mesh2D(
|
||||||
Array(Vector2D(0f, 0f), Vector2D(1f, 0f), Vector2D(1f, 1f),
|
Array(Vector2D(0f, 0f), Vector2D(1f, 0f), Vector2D(1f, 1f),
|
||||||
Vector2D(1f, 1f), Vector2D(0f, 1f), Vector2D(0f, 0f)).map(v => MeshVertex2D(v, v)),
|
Vector2D(1f, 1f), Vector2D(0f, 1f), Vector2D(0f, 0f)).map(v => MeshVertex2D(v, v))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Mesh2D(override val vertices: Seq[MeshVertex2D], override val indices: Option[Seq[Index]] = None) extends Mesh[MeshVertex2D]
|
class Mesh2D(override val vertices: Seq[MeshVertex2D], override val indices: Option[Seq[Index]] = None)
|
||||||
|
extends Mesh[MeshVertex2D]
|
||||||
|
|||||||
@ -50,8 +50,8 @@ object Mesh3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Mesh3D(override val vertices: Seq[MeshVertex3D], override val indices: Option[Seq[Index]] = None,
|
class Mesh3D(override val vertices: Seq[MeshVertex3D], override val indices: Option[Seq[Index]] = None,
|
||||||
override val primitiveType: PrimitiveType = PrimitiveTriangles) extends Mesh[MeshVertex3D]
|
override val primitiveType: PrimitiveType = PrimitiveTriangles)
|
||||||
{
|
extends Mesh[MeshVertex3D] {
|
||||||
val boundingBox: Box3D = {
|
val boundingBox: Box3D = {
|
||||||
Box3D.fromVertices(vertices.iterator.map(_.pos))
|
Box3D.fromVertices(vertices.iterator.map(_.pos))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,8 +43,7 @@ class MeshBuilder3D {
|
|||||||
|
|
||||||
def triangle(a: Vector3D, aUV: Vector2D,
|
def triangle(a: Vector3D, aUV: Vector2D,
|
||||||
b: Vector3D, bUV: Vector2D,
|
b: Vector3D, bUV: Vector2D,
|
||||||
c: Vector3D, cUV: Vector2D): Unit =
|
c: Vector3D, cUV: Vector2D): Unit = {
|
||||||
{
|
|
||||||
val normal = (b - a).cross(c - a).normalize
|
val normal = (b - a).cross(c - a).normalize
|
||||||
val aIdx = vertex(a, normal, aUV)
|
val aIdx = vertex(a, normal, aUV)
|
||||||
val bIdx = vertex(b, normal, bUV)
|
val bIdx = vertex(b, normal, bUV)
|
||||||
@ -56,8 +55,7 @@ class MeshBuilder3D {
|
|||||||
b: Vector3D, bUV: Vector2D,
|
b: Vector3D, bUV: Vector2D,
|
||||||
c: Vector3D, cUV: Vector2D,
|
c: Vector3D, cUV: Vector2D,
|
||||||
d: Vector3D, dUV: Vector2D,
|
d: Vector3D, dUV: Vector2D,
|
||||||
color: Color): Unit =
|
color: Color): Unit = {
|
||||||
{
|
|
||||||
val normal = (b - a).cross(c - a).normalize
|
val normal = (b - a).cross(c - a).normalize
|
||||||
val aIdx = vertex(a, normal, aUV, color)
|
val aIdx = vertex(a, normal, aUV, color)
|
||||||
val bIdx = vertex(b, normal, bUV, color)
|
val bIdx = vertex(b, normal, bUV, color)
|
||||||
@ -74,15 +72,19 @@ class MeshBuilder3D {
|
|||||||
spriteRect.x + cutRect.x * spriteUVScale * spriteRect.w,
|
spriteRect.x + cutRect.x * spriteUVScale * spriteRect.w,
|
||||||
spriteRect.y + cutRect.y * spriteUVScale * spriteRect.w,
|
spriteRect.y + cutRect.y * spriteUVScale * spriteRect.w,
|
||||||
cutRect.w * spriteUVScale * spriteRect.w,
|
cutRect.w * spriteUVScale * spriteRect.w,
|
||||||
cutRect.h * spriteUVScale * spriteRect.h
|
cutRect.h * spriteUVScale * spriteRect.h,
|
||||||
)
|
)
|
||||||
|
|
||||||
quad(
|
quad(
|
||||||
a, Vector2D(rect.x + rect.w, rect.y),
|
a,
|
||||||
b, Vector2D(rect.x, rect.y),
|
Vector2D(rect.x + rect.w, rect.y),
|
||||||
c, Vector2D(rect.x, rect.y + rect.h),
|
b,
|
||||||
d, Vector2D(rect.x + rect.w, rect.y + rect.h),
|
Vector2D(rect.x, rect.y),
|
||||||
color
|
c,
|
||||||
|
Vector2D(rect.x, rect.y + rect.h),
|
||||||
|
d,
|
||||||
|
Vector2D(rect.x + rect.w, rect.y + rect.h),
|
||||||
|
color,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,55 +95,72 @@ class MeshBuilder3D {
|
|||||||
back: Option[(String, Rect2D)] = Some(("Empty", Rect2D.Unit)),
|
back: Option[(String, Rect2D)] = Some(("Empty", Rect2D.Unit)),
|
||||||
top: Option[(String, Rect2D)] = Some(("Empty", Rect2D.Unit)),
|
top: Option[(String, Rect2D)] = Some(("Empty", Rect2D.Unit)),
|
||||||
bottom: Option[(String, Rect2D)] = Some(("Empty", Rect2D.Unit)),
|
bottom: Option[(String, Rect2D)] = Some(("Empty", Rect2D.Unit)),
|
||||||
color: Color = Color.White): Unit =
|
color: Color = Color.White): Unit = {
|
||||||
{
|
left.foreach(sprite => {
|
||||||
left.foreach(sprite => quad(
|
quad(
|
||||||
Vector3D(min.x, max.y, max.z),
|
Vector3D(min.x, max.y, max.z),
|
||||||
Vector3D(min.x, max.y, min.z),
|
Vector3D(min.x, max.y, min.z),
|
||||||
Vector3D(min.x, min.y, min.z),
|
Vector3D(min.x, min.y, min.z),
|
||||||
Vector3D(min.x, min.y, max.z),
|
Vector3D(min.x, min.y, max.z),
|
||||||
sprite, color,
|
sprite,
|
||||||
))
|
color,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
right.foreach(sprite => quad(
|
right.foreach(sprite => {
|
||||||
Vector3D(max.x, max.y, min.z),
|
quad(
|
||||||
Vector3D(max.x, max.y, max.z),
|
Vector3D(max.x, max.y, min.z),
|
||||||
Vector3D(max.x, min.y, max.z),
|
Vector3D(max.x, max.y, max.z),
|
||||||
Vector3D(max.x, min.y, min.z),
|
Vector3D(max.x, min.y, max.z),
|
||||||
sprite, color,
|
Vector3D(max.x, min.y, min.z),
|
||||||
))
|
sprite,
|
||||||
|
color,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
front.foreach(sprite => quad(
|
front.foreach(sprite => {
|
||||||
Vector3D(min.x, max.y, min.z),
|
quad(
|
||||||
Vector3D(max.x, max.y, min.z),
|
Vector3D(min.x, max.y, min.z),
|
||||||
Vector3D(max.x, min.y, min.z),
|
Vector3D(max.x, max.y, min.z),
|
||||||
Vector3D(min.x, min.y, min.z),
|
Vector3D(max.x, min.y, min.z),
|
||||||
sprite, color,
|
Vector3D(min.x, min.y, min.z),
|
||||||
))
|
sprite,
|
||||||
|
color,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
back.foreach(sprite => quad(
|
back.foreach(sprite => {
|
||||||
Vector3D(max.x, max.y, max.z),
|
quad(
|
||||||
Vector3D(min.x, max.y, max.z),
|
Vector3D(max.x, max.y, max.z),
|
||||||
Vector3D(min.x, min.y, max.z),
|
Vector3D(min.x, max.y, max.z),
|
||||||
Vector3D(max.x, min.y, max.z),
|
Vector3D(min.x, min.y, max.z),
|
||||||
sprite, color,
|
Vector3D(max.x, min.y, max.z),
|
||||||
))
|
sprite,
|
||||||
|
color,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
top.foreach(sprite => quad(
|
top.foreach(sprite => {
|
||||||
Vector3D(max.x, max.y, min.z),
|
quad(
|
||||||
Vector3D(min.x, max.y, min.z),
|
Vector3D(max.x, max.y, min.z),
|
||||||
Vector3D(min.x, max.y, max.z),
|
Vector3D(min.x, max.y, min.z),
|
||||||
Vector3D(max.x, max.y, max.z),
|
Vector3D(min.x, max.y, max.z),
|
||||||
sprite, color,
|
Vector3D(max.x, max.y, max.z),
|
||||||
))
|
sprite,
|
||||||
|
color,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
bottom.foreach(sprite => quad(
|
bottom.foreach(sprite => {
|
||||||
Vector3D(max.x, min.y, max.z),
|
quad(
|
||||||
Vector3D(min.x, min.y, max.z),
|
Vector3D(max.x, min.y, max.z),
|
||||||
Vector3D(min.x, min.y, min.z),
|
Vector3D(min.x, min.y, max.z),
|
||||||
Vector3D(max.x, min.y, min.z),
|
Vector3D(min.x, min.y, min.z),
|
||||||
sprite, color,
|
Vector3D(max.x, min.y, min.z),
|
||||||
))
|
sprite,
|
||||||
|
color,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
def line(a: Vector3D, b: Vector3D, aColor: Color = Color.White, bColor: Color = Color.White): Unit = {
|
def line(a: Vector3D, b: Vector3D, aColor: Color = Color.White, bColor: Color = Color.White): Unit = {
|
||||||
|
|||||||
@ -22,8 +22,8 @@ object MeshInstance2D extends VertexType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case class MeshInstance2D(color: Color, textColor: Color, transform: Transform2D, uvTransform: Transform2D,
|
case class MeshInstance2D(color: Color, textColor: Color, transform: Transform2D, uvTransform: Transform2D,
|
||||||
textUvTransform: Transform2D) extends Vertex
|
textUvTransform: Transform2D)
|
||||||
{
|
extends Vertex {
|
||||||
override def stride: Int = MeshInstance2D.stride
|
override def stride: Int = MeshInstance2D.stride
|
||||||
|
|
||||||
override def vertexType: VertexType = MeshInstance2D
|
override def vertexType: VertexType = MeshInstance2D
|
||||||
|
|||||||
@ -17,13 +17,13 @@ object MeshInstance3D extends VertexType {
|
|||||||
Attribute("inUVTransform0", 3, GL11.GL_FLOAT, normalized = false, stride, 64),
|
Attribute("inUVTransform0", 3, GL11.GL_FLOAT, normalized = false, stride, 64),
|
||||||
Attribute("inUVTransform1", 3, GL11.GL_FLOAT, normalized = false, stride, 76),
|
Attribute("inUVTransform1", 3, GL11.GL_FLOAT, normalized = false, stride, 76),
|
||||||
Attribute("inLightingFactor", 1, GL11.GL_FLOAT, normalized = false, stride, 88),
|
Attribute("inLightingFactor", 1, GL11.GL_FLOAT, normalized = false, stride, 88),
|
||||||
Attribute("inTextureFactor", 1, GL11.GL_FLOAT, normalized = false, stride, 92)
|
Attribute("inTextureFactor", 1, GL11.GL_FLOAT, normalized = false, stride, 92),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class MeshInstance3D(color: Color, transform: Transform3D, uvTransform: Transform2D,
|
case class MeshInstance3D(color: Color, transform: Transform3D, uvTransform: Transform2D,
|
||||||
lightingFactor: Float, textureFactor: Float) extends Vertex
|
lightingFactor: Float, textureFactor: Float)
|
||||||
{
|
extends Vertex {
|
||||||
override def vertexType: VertexType = MeshInstance3D
|
override def vertexType: VertexType = MeshInstance3D
|
||||||
|
|
||||||
override def stride: Int = MeshInstance3D.stride
|
override def stride: Int = MeshInstance3D.stride
|
||||||
|
|||||||
@ -10,7 +10,7 @@ object MeshVertex2D extends VertexType {
|
|||||||
|
|
||||||
override val attributes: Seq[Attribute] = Array(
|
override val attributes: Seq[Attribute] = Array(
|
||||||
Attribute("inPos", 2, GL11.GL_FLOAT, normalized = false, stride, 0),
|
Attribute("inPos", 2, GL11.GL_FLOAT, normalized = false, stride, 0),
|
||||||
Attribute("inUV", 2, GL11.GL_FLOAT, normalized = false, stride, 8)
|
Attribute("inUV", 2, GL11.GL_FLOAT, normalized = false, stride, 8),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,8 @@ import org.lwjgl.opengl._
|
|||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import scala.collection.mutable.ArrayBuffer
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
class InstanceRenderer[V <: Vertex, I <: Vertex](mesh: Mesh[V], instanceType: VertexType, shader: ShaderProgram) extends Resource with Logging {
|
class InstanceRenderer[V <: Vertex, I <: Vertex](mesh: Mesh[V], instanceType: VertexType, shader: ShaderProgram)
|
||||||
|
extends Resource with Logging {
|
||||||
private val InitialCapacity: Int = 1
|
private val InitialCapacity: Int = 1
|
||||||
|
|
||||||
private val vertexBuffer = new VertexBuffer[V](mesh.vertices)
|
private val vertexBuffer = new VertexBuffer[V](mesh.vertices)
|
||||||
@ -46,18 +47,20 @@ class InstanceRenderer[V <: Vertex, I <: Vertex](mesh: Mesh[V], instanceType: Ve
|
|||||||
|
|
||||||
(
|
(
|
||||||
method.invoke(null, "glDrawArraysInstancedARB"),
|
method.invoke(null, "glDrawArraysInstancedARB"),
|
||||||
method.invoke(null, "glDrawElementsInstancedARB")
|
method.invoke(null, "glDrawElementsInstancedARB"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private lazy val nglDrawElementsInstancedARB = {
|
private lazy val nglDrawElementsInstancedARB = {
|
||||||
val method = classOf[ARBDrawInstanced].getDeclaredMethod("nglDrawElementsInstancedARB", Integer.TYPE, Integer.TYPE, Integer.TYPE, java.lang.Long.TYPE, Integer.TYPE, java.lang.Long.TYPE)
|
val method = classOf[ARBDrawInstanced].getDeclaredMethod("nglDrawElementsInstancedARB", Integer.TYPE, Integer.TYPE,
|
||||||
|
Integer.TYPE, java.lang.Long.TYPE, Integer.TYPE, java.lang.Long.TYPE)
|
||||||
method.setAccessible(true)
|
method.setAccessible(true)
|
||||||
method
|
method
|
||||||
}
|
}
|
||||||
|
|
||||||
private lazy val nglDrawArraysInstancedARB = {
|
private lazy val nglDrawArraysInstancedARB = {
|
||||||
val method = classOf[ARBDrawInstanced].getDeclaredMethod("nglDrawArraysInstancedARB", Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE, java.lang.Long.TYPE)
|
val method = classOf[ARBDrawInstanced].getDeclaredMethod("nglDrawArraysInstancedARB", Integer.TYPE, Integer.TYPE,
|
||||||
|
Integer.TYPE, Integer.TYPE, java.lang.Long.TYPE)
|
||||||
method.setAccessible(true)
|
method.setAccessible(true)
|
||||||
method
|
method
|
||||||
}
|
}
|
||||||
@ -70,10 +73,12 @@ class InstanceRenderer[V <: Vertex, I <: Vertex](mesh: Mesh[V], instanceType: Ve
|
|||||||
|
|
||||||
indexBuffer match {
|
indexBuffer match {
|
||||||
case Some(ib) =>
|
case Some(ib) =>
|
||||||
nglDrawElementsInstancedARB.invoke(null, mesh.primitiveType.toGL, ib.capacity, GL11.GL_UNSIGNED_INT, 0L, instances.length, glDrawElementsInstancedARBptr)
|
nglDrawElementsInstancedARB.invoke(null, mesh.primitiveType.toGL, ib.capacity, GL11.GL_UNSIGNED_INT, 0L,
|
||||||
|
instances.length, glDrawElementsInstancedARBptr)
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
nglDrawArraysInstancedARB.invoke(null, mesh.primitiveType.toGL, 0, vertexBuffer.capacity, instances.length, glDrawArraysInstancedARBptr)
|
nglDrawArraysInstancedARB.invoke(null, mesh.primitiveType.toGL, 0, vertexBuffer.capacity, instances.length,
|
||||||
|
glDrawArraysInstancedARBptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
instances.clear()
|
instances.clear()
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import ocelot.desktop.geometry.{ProjectionMatrix3D, Vector3D}
|
|||||||
|
|
||||||
class Camera3D(var zNear: Float = 0.1f,
|
class Camera3D(var zNear: Float = 0.1f,
|
||||||
var zFar: Float = 100.0f,
|
var zFar: Float = 100.0f,
|
||||||
var fovY: Float = 80.0f) extends SceneNode3D
|
var fovY: Float = 80.0f)
|
||||||
{
|
extends SceneNode3D {
|
||||||
def distanceTo(point: Vector3D): Float = {
|
def distanceTo(point: Vector3D): Float = {
|
||||||
(origin - point).length
|
(origin - point).length
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,4 +3,5 @@ package ocelot.desktop.graphics.scene
|
|||||||
import ocelot.desktop.color.Color
|
import ocelot.desktop.color.Color
|
||||||
|
|
||||||
class DirectionalLight3D(var color: Color = Color.White,
|
class DirectionalLight3D(var color: Color = Color.White,
|
||||||
var energy: Float = 1) extends SceneNode3D
|
var energy: Float = 1)
|
||||||
|
extends SceneNode3D
|
||||||
|
|||||||
@ -10,4 +10,5 @@ class SceneMesh3D(var mesh: Mesh3D,
|
|||||||
var lightingFactor: Float = 1,
|
var lightingFactor: Float = 1,
|
||||||
var textureFactor: Float = 1,
|
var textureFactor: Float = 1,
|
||||||
var isOpaque: Boolean = true,
|
var isOpaque: Boolean = true,
|
||||||
var priority: Int = 0) extends SceneNode3D
|
var priority: Int = 0)
|
||||||
|
extends SceneNode3D
|
||||||
|
|||||||
@ -6,16 +6,12 @@ import totoro.ocelot.brain.event.NodeEvent
|
|||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
/**
|
/** Provides an inventory — a collection of [[Item]]s indexed by slots. */
|
||||||
* Provides an inventory — a collection of [[Item]]s indexed by slots.
|
|
||||||
*/
|
|
||||||
trait Inventory extends EventAware {
|
trait Inventory extends EventAware {
|
||||||
// parallels totoro.ocelot.brain.entity.traits.Inventory
|
// parallels totoro.ocelot.brain.entity.traits.Inventory
|
||||||
// this is intentional
|
// this is intentional
|
||||||
|
|
||||||
/**
|
/** The type of items stored in this inventory. */
|
||||||
* The type of items stored in this inventory.
|
|
||||||
*/
|
|
||||||
type I <: Item
|
type I <: Item
|
||||||
|
|
||||||
private type WeakHashSet[A] = mutable.WeakHashMap[A, Unit]
|
private type WeakHashSet[A] = mutable.WeakHashMap[A, Unit]
|
||||||
@ -24,15 +20,13 @@ trait Inventory extends EventAware {
|
|||||||
private val itemSlots = mutable.HashMap.empty[I, Int]
|
private val itemSlots = mutable.HashMap.empty[I, Int]
|
||||||
private val observers = mutable.HashMap.empty[Int, WeakHashSet[SlotObserver]]
|
private val observers = mutable.HashMap.empty[Int, WeakHashSet[SlotObserver]]
|
||||||
|
|
||||||
/**
|
/** Called after a new item is added to the inventory.
|
||||||
* Called after a new item is added to the inventory.
|
|
||||||
*
|
*
|
||||||
* @param slot the slot the item was added to
|
* @param slot the slot the item was added to
|
||||||
*/
|
*/
|
||||||
def onItemAdded(slot: Slot): Unit
|
def onItemAdded(slot: Slot): Unit
|
||||||
|
|
||||||
/**
|
/** Called after an item is removed from the inventory.
|
||||||
* Called after an item is removed from the inventory.
|
|
||||||
*
|
*
|
||||||
* When the item is replaced by another one, the event are sequenced in the following order:
|
* When the item is replaced by another one, the event are sequenced in the following order:
|
||||||
*
|
*
|
||||||
@ -47,9 +41,7 @@ trait Inventory extends EventAware {
|
|||||||
*/
|
*/
|
||||||
def onItemRemoved(slot: Slot, removedItem: I, replacedBy: Option[I]): Unit
|
def onItemRemoved(slot: Slot, removedItem: I, replacedBy: Option[I]): Unit
|
||||||
|
|
||||||
/**
|
/** An iterator over all slots occupied in this inventory. */
|
||||||
* An iterator over all slots occupied in this inventory.
|
|
||||||
*/
|
|
||||||
def inventoryIterator: Iterator[Slot] = slotItems.keysIterator.map(Slot(_))
|
def inventoryIterator: Iterator[Slot] = slotItems.keysIterator.map(Slot(_))
|
||||||
|
|
||||||
def clearInventory(): Unit = {
|
def clearInventory(): Unit = {
|
||||||
@ -86,46 +78,35 @@ trait Inventory extends EventAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A proxy to access a slot of the inventory. */
|
||||||
* A proxy to access a slot of the inventory.
|
final class Slot private[Inventory] (val index: Int) {
|
||||||
*/
|
|
||||||
final class Slot private[Inventory](val index: Int) {
|
|
||||||
require(index >= 0)
|
require(index >= 0)
|
||||||
|
|
||||||
def isEmpty: Boolean = get.isEmpty
|
def isEmpty: Boolean = get.isEmpty
|
||||||
|
|
||||||
def nonEmpty: Boolean = !isEmpty
|
def nonEmpty: Boolean = !isEmpty
|
||||||
|
|
||||||
/**
|
/** Inserts the `item` into this slot (replacing the previous item if any). */
|
||||||
* Inserts the `item` into this slot (replacing the previous item if any).
|
|
||||||
*/
|
|
||||||
def put(item: inventory.I): Unit = {
|
def put(item: inventory.I): Unit = {
|
||||||
setSlot(index, Some(item))
|
setSlot(index, Some(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Allows inserting/removing the item in this slot. */
|
||||||
* Allows inserting/removing the item in this slot.
|
|
||||||
*/
|
|
||||||
def set(item: Option[inventory.I]): Unit = {
|
def set(item: Option[inventory.I]): Unit = {
|
||||||
setSlot(index, item)
|
setSlot(index, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Removes the item contained in this slot if there is one. */
|
||||||
* Removes the item contained in this slot if there is one.
|
|
||||||
*/
|
|
||||||
def remove(): Unit = {
|
def remove(): Unit = {
|
||||||
setSlot(index, None)
|
setSlot(index, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** The [[Item]] contained in this slot. */
|
||||||
* The [[Item]] contained in this slot.
|
|
||||||
*/
|
|
||||||
def get: Option[inventory.I] = slotItems.get(index)
|
def get: Option[inventory.I] = slotItems.get(index)
|
||||||
|
|
||||||
val inventory: Inventory.this.type = Inventory.this
|
val inventory: Inventory.this.type = Inventory.this
|
||||||
|
|
||||||
/**
|
/** Registers an observer to receive item added/removed events.
|
||||||
* Registers an observer to receive item added/removed events.
|
|
||||||
*
|
*
|
||||||
* @note The inventory keeps a '''weak''' reference to the `observer`.
|
* @note The inventory keeps a '''weak''' reference to the `observer`.
|
||||||
*/
|
*/
|
||||||
@ -167,9 +148,8 @@ trait Inventory extends EventAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final object Slot {
|
final object Slot {
|
||||||
/**
|
|
||||||
* Creates a proxy to an inventory slot.
|
/** Creates a proxy to an inventory slot. */
|
||||||
*/
|
|
||||||
def apply(index: Int) = new Slot(index)
|
def apply(index: Int) = new Slot(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +158,7 @@ trait Inventory extends EventAware {
|
|||||||
|
|
||||||
(slotItems.get(index), item) match {
|
(slotItems.get(index), item) match {
|
||||||
case (Some(oldItem), Some(newItem)) if oldItem == newItem =>
|
case (Some(oldItem), Some(newItem)) if oldItem == newItem =>
|
||||||
// no-op
|
// no-op
|
||||||
|
|
||||||
case (Some(oldItem), Some(newItem)) =>
|
case (Some(oldItem), Some(newItem)) =>
|
||||||
// replace old with new
|
// replace old with new
|
||||||
@ -198,7 +178,7 @@ trait Inventory extends EventAware {
|
|||||||
onItemAddedImpl(slot)
|
onItemAddedImpl(slot)
|
||||||
|
|
||||||
case (None, None) =>
|
case (None, None) =>
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,15 +205,14 @@ trait Inventory extends EventAware {
|
|||||||
|
|
||||||
object Inventory {
|
object Inventory {
|
||||||
trait SlotObserver {
|
trait SlotObserver {
|
||||||
/**
|
|
||||||
* Called after an item was inserted into this slot.
|
/** Called after an item was inserted into this slot.
|
||||||
*
|
*
|
||||||
* @note [[Inventory.onItemAdded]] is called before this method.
|
* @note [[Inventory.onItemAdded]] is called before this method.
|
||||||
*/
|
*/
|
||||||
def onItemAdded(): Unit
|
def onItemAdded(): Unit
|
||||||
|
|
||||||
/**
|
/** Called after an item was removed from this slot.
|
||||||
* Called after an item was removed from this slot.
|
|
||||||
*
|
*
|
||||||
* In particular, the slot no longer contains the removed item.
|
* In particular, the slot no longer contains the removed item.
|
||||||
*
|
*
|
||||||
@ -241,9 +220,7 @@ object Inventory {
|
|||||||
*/
|
*/
|
||||||
def onItemRemoved(removedItem: Item, replacedBy: Option[Item]): Unit
|
def onItemRemoved(removedItem: Item, replacedBy: Option[Item]): Unit
|
||||||
|
|
||||||
/**
|
/** Called when an item contained in this slot sends a notification via [[Item.notifySlot]]. */
|
||||||
* Called when an item contained in this slot sends a notification via [[Item.notifySlot]].
|
|
||||||
*/
|
|
||||||
def onItemNotification(notification: Item.Notification): Unit
|
def onItemNotification(notification: Item.Notification): Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,15 +11,11 @@ import ocelot.desktop.util.Disposable
|
|||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
/**
|
/** Something that can be stored in an [[Inventory]]. */
|
||||||
* Something that can be stored in an [[Inventory]].
|
|
||||||
*/
|
|
||||||
trait Item extends EventAware with Updatable with Disposable {
|
trait Item extends EventAware with Updatable with Disposable {
|
||||||
private var _slot: Option[Inventory#Slot] = None
|
private var _slot: Option[Inventory#Slot] = None
|
||||||
|
|
||||||
/**
|
/** The slot this item is stored in. */
|
||||||
* The slot this item is stored in.
|
|
||||||
*/
|
|
||||||
def slot: Option[Inventory#Slot] = _slot
|
def slot: Option[Inventory#Slot] = _slot
|
||||||
|
|
||||||
private[inventory] def slot_=(slot: Option[Inventory#Slot]): Unit = {
|
private[inventory] def slot_=(slot: Option[Inventory#Slot]): Unit = {
|
||||||
@ -32,9 +28,7 @@ trait Item extends EventAware with Updatable with Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Remove the item from its slot, apply `f`, and put it back into the slot. */
|
||||||
* Remove the item from its slot, apply `f`, and put it back into the slot.
|
|
||||||
*/
|
|
||||||
def reinserting(f: => Unit): Unit = {
|
def reinserting(f: => Unit): Unit = {
|
||||||
val prevSlot = slot
|
val prevSlot = slot
|
||||||
slot.foreach(_.remove())
|
slot.foreach(_.remove())
|
||||||
@ -42,8 +36,7 @@ trait Item extends EventAware with Updatable with Disposable {
|
|||||||
prevSlot.foreach(slot => slot.put(this.asInstanceOf[slot.inventory.I]))
|
prevSlot.foreach(slot => slot.put(this.asInstanceOf[slot.inventory.I]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Sends a `notification` to observers of the `slot`.
|
||||||
* Sends a `notification` to observers of the `slot`.
|
|
||||||
*
|
*
|
||||||
* (Which means, if the item is not put into any slot, nobody will receive the message.)
|
* (Which means, if the item is not put into any slot, nobody will receive the message.)
|
||||||
*/
|
*/
|
||||||
@ -55,18 +48,13 @@ trait Item extends EventAware with Updatable with Disposable {
|
|||||||
|
|
||||||
protected def onRemoved(): Unit = {}
|
protected def onRemoved(): Unit = {}
|
||||||
|
|
||||||
/**
|
/** The name of the item (as shown in the tooltip). */
|
||||||
* The name of the item (as shown in the tooltip).
|
|
||||||
*/
|
|
||||||
def name: String = factory.name
|
def name: String = factory.name
|
||||||
|
|
||||||
/**
|
/** The icon used to draw the item in a slot. */
|
||||||
* The icon used to draw the item in a slot.
|
|
||||||
*/
|
|
||||||
def icon: IconSource = factory.icon
|
def icon: IconSource = factory.icon
|
||||||
|
|
||||||
/**
|
/** The tier of the item (if it has one).
|
||||||
* The tier of the item (if it has one).
|
|
||||||
*
|
*
|
||||||
* This affects the color of the item name in the tooltip.
|
* This affects the color of the item name in the tooltip.
|
||||||
*/
|
*/
|
||||||
@ -74,8 +62,7 @@ trait Item extends EventAware with Updatable with Disposable {
|
|||||||
|
|
||||||
protected def tooltipNameColor: Color = ColorScheme(s"Tier${tier.getOrElse(Tier.One).id}")
|
protected def tooltipNameColor: Color = ColorScheme(s"Tier${tier.getOrElse(Tier.One).id}")
|
||||||
|
|
||||||
/**
|
/** Override this in subclasses to customize the contents of the tooltip.
|
||||||
* Override this in subclasses to customize the contents of the tooltip.
|
|
||||||
*
|
*
|
||||||
* @example {{{
|
* @example {{{
|
||||||
* override protected def fillTooltipBody(body: Widget): Unit = {
|
* override protected def fillTooltipBody(body: Widget): Unit = {
|
||||||
@ -91,31 +78,27 @@ trait Item extends EventAware with Updatable with Disposable {
|
|||||||
tooltip.addLine(name, tooltipNameColor)
|
tooltip.addLine(name, tooltipNameColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Override this in subclasses to add new entries to the context menu.
|
||||||
* Override this in subclasses to add new entries to the context menu.
|
|
||||||
*
|
*
|
||||||
* It usually makes sense to call `super.fillRmbMenu` ''after'' you've added your own entries.
|
* It usually makes sense to call `super.fillRmbMenu` ''after'' you've added your own entries.
|
||||||
*/
|
*/
|
||||||
def fillRmbMenu(menu: ContextMenu): Unit = {}
|
def fillRmbMenu(menu: ContextMenu): Unit = {}
|
||||||
|
|
||||||
/**
|
/** The factory that can be used to build an independent instance of this [[Item]]
|
||||||
* The factory that can be used to build an independent instance of this [[Item]]
|
|
||||||
* in a way equivalent to this one (e.g. the same tier, label, EEPROM code).
|
* in a way equivalent to this one (e.g. the same tier, label, EEPROM code).
|
||||||
*
|
*
|
||||||
* Keep this rather cheap.
|
* Keep this rather cheap.
|
||||||
*/
|
*/
|
||||||
def factory: ItemFactory
|
def factory: ItemFactory
|
||||||
|
|
||||||
/**
|
/** Override this in subclasses to implement some specific item behavior
|
||||||
* Override this in subclasses to implement some specific item behavior
|
* during UI update
|
||||||
* during UI update
|
*/
|
||||||
*/
|
|
||||||
override def update(): Unit = {}
|
override def update(): Unit = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Item {
|
object Item {
|
||||||
/**
|
|
||||||
* A notification that can be sent with [[Item.notifySlot]].
|
/** A notification that can be sent with [[Item.notifySlot]]. */
|
||||||
*/
|
|
||||||
trait Notification
|
trait Notification
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,7 @@ package ocelot.desktop.inventory
|
|||||||
import ocelot.desktop.graphics.IconSource
|
import ocelot.desktop.graphics.IconSource
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
/**
|
/** Provides information about a class of [[Item]]s and allows to build an item instance.
|
||||||
* Provides information about a class of [[Item]]s and allows to build an item instance.
|
|
||||||
*
|
*
|
||||||
* Used by [[ocelot.desktop.ui.widget.slot.SlotWidget SlotWidgets]]
|
* Used by [[ocelot.desktop.ui.widget.slot.SlotWidget SlotWidgets]]
|
||||||
* (and [[ocelot.desktop.ui.widget.slot.ItemChooser]]) to tell if you can insert an item into the slot
|
* (and [[ocelot.desktop.ui.widget.slot.ItemChooser]]) to tell if you can insert an item into the slot
|
||||||
@ -12,20 +11,17 @@ import totoro.ocelot.brain.util.Tier.Tier
|
|||||||
* In particular, make sure the [[tier]] and [[itemClass]] provided by the factory are accurate.
|
* In particular, make sure the [[tier]] and [[itemClass]] provided by the factory are accurate.
|
||||||
*/
|
*/
|
||||||
trait ItemFactory {
|
trait ItemFactory {
|
||||||
/**
|
|
||||||
* The concrete type of the [[Item]] built by the factory.
|
/** The concrete type of the [[Item]] built by the factory. */
|
||||||
*/
|
|
||||||
type I <: Item
|
type I <: Item
|
||||||
|
|
||||||
/**
|
/** The runtime class of the [[Item]] built by the factory.
|
||||||
* The runtime class of the [[Item]] built by the factory.
|
|
||||||
*
|
*
|
||||||
* @note It's expected that `build().getClass == itemClass`.
|
* @note It's expected that `build().getClass == itemClass`.
|
||||||
*/
|
*/
|
||||||
def itemClass: Class[I]
|
def itemClass: Class[I]
|
||||||
|
|
||||||
/**
|
/** A name that represents what will be built by the factory.
|
||||||
* A name that represents what will be built by the factory.
|
|
||||||
*
|
*
|
||||||
* Usually [[name]] and [[Item.name]] are the same (in fact, the latter defaults to this unless overridden).
|
* Usually [[name]] and [[Item.name]] are the same (in fact, the latter defaults to this unless overridden).
|
||||||
*
|
*
|
||||||
@ -33,27 +29,21 @@ trait ItemFactory {
|
|||||||
*/
|
*/
|
||||||
def name: String
|
def name: String
|
||||||
|
|
||||||
/**
|
/** The tier of an item this factory will construct in its [[build]] method.
|
||||||
* The tier of an item this factory will construct in its [[build]] method.
|
|
||||||
*
|
*
|
||||||
* @note It's expected that `build().tier == tier`.
|
* @note It's expected that `build().tier == tier`.
|
||||||
*/
|
*/
|
||||||
def tier: Option[Tier]
|
def tier: Option[Tier]
|
||||||
|
|
||||||
/**
|
/** The icon of an item this factory will construct in its [[build]] method.
|
||||||
* The icon of an item this factory will construct in its [[build]] method.
|
|
||||||
*
|
*
|
||||||
* Used by the [[ocelot.desktop.ui.widget.slot.ItemChooser ItemChooser]] in its entries.
|
* Used by the [[ocelot.desktop.ui.widget.slot.ItemChooser ItemChooser]] in its entries.
|
||||||
*/
|
*/
|
||||||
def icon: IconSource
|
def icon: IconSource
|
||||||
|
|
||||||
/**
|
/** Constructs a new item instance. */
|
||||||
* Constructs a new item instance.
|
|
||||||
*/
|
|
||||||
def build(): I
|
def build(): I
|
||||||
|
|
||||||
/**
|
/** Returns a list of recoverers provided by the factory, */
|
||||||
* Returns a list of recoverers provided by the factory,
|
|
||||||
*/
|
|
||||||
def recoverers: Iterable[ItemRecoverer[_, _]]
|
def recoverers: Iterable[ItemRecoverer[_, _]]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
package ocelot.desktop.inventory
|
package ocelot.desktop.inventory
|
||||||
|
|
||||||
import scala.reflect.{ClassTag, classTag}
|
import scala.reflect.{classTag, ClassTag}
|
||||||
|
|
||||||
/**
|
/** Allows recovering an [[Item]] from [[S]] (typically an [[totoro.ocelot.brain.entity.traits.Entity Entity]]).
|
||||||
* Allows recovering an [[Item]] from [[S]] (typically an [[totoro.ocelot.brain.entity.traits.Entity Entity]]).
|
|
||||||
*
|
*
|
||||||
* This is used for migration from old saves before the inventory system was introduced to Ocelot Desktop.
|
* This is used for migration from old saves before the inventory system was introduced to Ocelot Desktop.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -20,9 +20,7 @@ object Items extends Logging {
|
|||||||
// this is just to force load the class during initialization
|
// this is just to force load the class during initialization
|
||||||
def init(): Unit = {}
|
def init(): Unit = {}
|
||||||
|
|
||||||
/**
|
/** Registers a recoverer for [[ItemRecoverer.sourceClass]]. */
|
||||||
* Registers a recoverer for [[ItemRecoverer.sourceClass]].
|
|
||||||
*/
|
|
||||||
def registerRecoverer(recoverer: ItemRecoverer[_, _]): Unit = {
|
def registerRecoverer(recoverer: ItemRecoverer[_, _]): Unit = {
|
||||||
if (!_recoverers.contains(recoverer.sourceClass)) {
|
if (!_recoverers.contains(recoverer.sourceClass)) {
|
||||||
_recoverers(recoverer.sourceClass) = recoverer
|
_recoverers(recoverer.sourceClass) = recoverer
|
||||||
@ -72,8 +70,7 @@ object Items extends Logging {
|
|||||||
|
|
||||||
def groups: Iterable[ItemGroup] = _groups
|
def groups: Iterable[ItemGroup] = _groups
|
||||||
|
|
||||||
/**
|
/** Attempts to recover an [[Item]] from `source`.
|
||||||
* Attempts to recover an [[Item]] from `source`.
|
|
||||||
*
|
*
|
||||||
* Checks superclasses and traits while looking for a recoverer.
|
* Checks superclasses and traits while looking for a recoverer.
|
||||||
*/
|
*/
|
||||||
@ -111,7 +108,7 @@ object Items extends Logging {
|
|||||||
case ExtendedTier.Creative => new MagicalMemoryItem.Factory()
|
case ExtendedTier.Creative => new MagicalMemoryItem.Factory()
|
||||||
case tier => new MemoryItem.Factory(tier)
|
case tier => new MemoryItem.Factory(tier)
|
||||||
}
|
}
|
||||||
.map(factory => (factory.name, factory))
|
.map(factory => (factory.name, factory)),
|
||||||
)
|
)
|
||||||
|
|
||||||
registerTiered("HDD", Tier.One to Tier.Three)(new HddItem.Factory(managed = true, _))
|
registerTiered("HDD", Tier.One to Tier.Three)(new HddItem.Factory(managed = true, _))
|
||||||
@ -121,7 +118,7 @@ object Items extends Logging {
|
|||||||
FloppyItem.Factory.Empty.icon,
|
FloppyItem.Factory.Empty.icon,
|
||||||
Loot.Floppies.iterator
|
Loot.Floppies.iterator
|
||||||
.map(new FloppyItem.Factory.Loot(_))
|
.map(new FloppyItem.Factory.Loot(_))
|
||||||
.map(factory => (factory.name, factory)) ++ Some(("Empty", FloppyItem.Factory.Empty))
|
.map(factory => (factory.name, factory)) ++ Some(("Empty", FloppyItem.Factory.Empty)),
|
||||||
)
|
)
|
||||||
|
|
||||||
registerArbitrary(
|
registerArbitrary(
|
||||||
@ -129,26 +126,31 @@ object Items extends Logging {
|
|||||||
EepromItem.Factory.Empty.icon,
|
EepromItem.Factory.Empty.icon,
|
||||||
Loot.Eeproms.iterator
|
Loot.Eeproms.iterator
|
||||||
.map(new EepromItem.Factory.Loot(_))
|
.map(new EepromItem.Factory.Loot(_))
|
||||||
.map(factory => (factory.name, factory)) ++ Some(("Empty", EepromItem.Factory.Empty))
|
.map(factory => (factory.name, factory)) ++ Some(("Empty", EepromItem.Factory.Empty)),
|
||||||
)
|
)
|
||||||
|
|
||||||
registerTiered("Graphics Card", Tier.One to Tier.Three)(new GraphicsCardItem.Factory(_))
|
registerTiered("Graphics Card", Tier.One to Tier.Three)(new GraphicsCardItem.Factory(_))
|
||||||
registerSingleton(NetworkCardItem.Factory)
|
registerSingleton(NetworkCardItem.Factory)
|
||||||
|
|
||||||
registerTiered("Wireless Net. Card", Tier.One to Tier.Two) {
|
registerTiered("Wireless Net. Card", Tier.One to Tier.Two) {
|
||||||
case Tier.One => WirelessNetworkCardItem.Tier1.Factory
|
case Tier.One => WirelessNetworkCardItem.Tier1.Factory
|
||||||
case Tier.Two => WirelessNetworkCardItem.Tier2.Factory
|
case Tier.Two => WirelessNetworkCardItem.Tier2.Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSingleton(LinkedCardItem.Factory)
|
registerSingleton(LinkedCardItem.Factory)
|
||||||
registerSingleton(InternetCardItem.Factory)
|
registerSingleton(InternetCardItem.Factory)
|
||||||
|
|
||||||
registerTiered("Redstone Card", Tier.One to Tier.Two) {
|
registerTiered("Redstone Card", Tier.One to Tier.Two) {
|
||||||
case Tier.One => RedstoneCardItem.Tier1.Factory
|
case Tier.One => RedstoneCardItem.Tier1.Factory
|
||||||
case Tier.Two => RedstoneCardItem.Tier2.Factory
|
case Tier.Two => RedstoneCardItem.Tier2.Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
registerTiered("Data Card", Tier.One to Tier.Three) {
|
registerTiered("Data Card", Tier.One to Tier.Three) {
|
||||||
case Tier.One => DataCardItem.Tier1.Factory
|
case Tier.One => DataCardItem.Tier1.Factory
|
||||||
case Tier.Two => DataCardItem.Tier2.Factory
|
case Tier.Two => DataCardItem.Tier2.Factory
|
||||||
case Tier.Three => DataCardItem.Tier3.Factory
|
case Tier.Three => DataCardItem.Tier3.Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSingleton(SoundCardItem.Factory)
|
registerSingleton(SoundCardItem.Factory)
|
||||||
registerSingleton(SelfDestructingCardItem.Factory)
|
registerSingleton(SelfDestructingCardItem.Factory)
|
||||||
registerSingleton(OcelotCardItem.Factory)
|
registerSingleton(OcelotCardItem.Factory)
|
||||||
@ -162,6 +164,6 @@ object Items extends Logging {
|
|||||||
new TapeItem.Factory(Tape.Kind.Iron).icon,
|
new TapeItem.Factory(Tape.Kind.Iron).icon,
|
||||||
Tape.Kind.values.iterator
|
Tape.Kind.values.iterator
|
||||||
.map(new TapeItem.Factory(_))
|
.map(new TapeItem.Factory(_))
|
||||||
.map(factory => (f"${factory.name}%s (${Tape.lengthMinutes(factory.kind)}%.0f min)", factory))
|
.map(factory => (f"${factory.name}%s (${Tape.lengthMinutes(factory.kind)}%.0f min)", factory)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,7 @@ import totoro.ocelot.brain.nbt.{NBT, NBTTagCompound}
|
|||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
/**
|
/** Provides persistence for an [[Inventory]].
|
||||||
* Provides persistence for an [[Inventory]].
|
|
||||||
*
|
*
|
||||||
* [[ComponentItem]]s are treated specially: their [[ComponentItem.component component]] is persisted
|
* [[ComponentItem]]s are treated specially: their [[ComponentItem.component component]] is persisted
|
||||||
* along with the item when saving. When loading the item, it first loads the component and uses it to instantiate
|
* along with the item when saving. When loading the item, it first loads the component and uses it to instantiate
|
||||||
@ -45,7 +44,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
|||||||
case ItemLoadException(message) =>
|
case ItemLoadException(message) =>
|
||||||
logger.error(
|
logger.error(
|
||||||
s"Could not restore an item in the slot $slotIndex of " +
|
s"Could not restore an item in the slot $slotIndex of " +
|
||||||
s"the inventory $this (class ${this.getClass.getName}): $message",
|
s"the inventory $this (class ${this.getClass.getName}): $message"
|
||||||
)
|
)
|
||||||
onSlotLoadFailed(slotIndex)
|
onSlotLoadFailed(slotIndex)
|
||||||
}
|
}
|
||||||
@ -60,7 +59,8 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
|||||||
super.save(nbt)
|
super.save(nbt)
|
||||||
|
|
||||||
nbt.setNewTagList(
|
nbt.setNewTagList(
|
||||||
InventoryTag, inventoryIterator.map { slot =>
|
InventoryTag,
|
||||||
|
inventoryIterator.map { slot =>
|
||||||
val item = slot.get.get
|
val item = slot.get.get
|
||||||
|
|
||||||
val slotNbt = new NBTTagCompound
|
val slotNbt = new NBTTagCompound
|
||||||
@ -77,7 +77,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
slotNbt
|
slotNbt
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
|||||||
constructor.newInstance(entity).asInstanceOf[I]
|
constructor.newInstance(entity).asInstanceOf[I]
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection ScalaWeakerAccess
|
// noinspection ScalaWeakerAccess
|
||||||
@throws[ItemLoadException]("if the item could not be loaded")
|
@throws[ItemLoadException]("if the item could not be loaded")
|
||||||
protected def loadPlainItem(itemClass: Class[_]): I = {
|
protected def loadPlainItem(itemClass: Class[_]): I = {
|
||||||
itemClass.getConstructor().newInstance().asInstanceOf[I]
|
itemClass.getConstructor().newInstance().asInstanceOf[I]
|
||||||
|
|||||||
@ -16,8 +16,7 @@ import totoro.ocelot.brain.network.Network
|
|||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
/**
|
/** A [[PersistedInventory]] backed by a [[BrainInventory brain Inventory]].
|
||||||
* A [[PersistedInventory]] backed by a [[BrainInventory brain Inventory]].
|
|
||||||
*
|
*
|
||||||
* Synchronizes the contents of the two inventories, propagating changes from one to the other.
|
* Synchronizes the contents of the two inventories, propagating changes from one to the other.
|
||||||
*
|
*
|
||||||
@ -36,11 +35,10 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
|
|||||||
|
|
||||||
@volatile
|
@volatile
|
||||||
private var syncFuel: Int = _
|
private var syncFuel: Int = _
|
||||||
|
|
||||||
refuel()
|
refuel()
|
||||||
|
|
||||||
/**
|
/** The backing [[BrainInventory brain Inventory]]. */
|
||||||
* The backing [[BrainInventory brain Inventory]].
|
|
||||||
*/
|
|
||||||
def brainInventory: BrainInventory
|
def brainInventory: BrainInventory
|
||||||
|
|
||||||
override def load(nbt: NBTTagCompound): Unit = {
|
override def load(nbt: NBTTagCompound): Unit = {
|
||||||
@ -210,7 +208,6 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
|
|||||||
case SyncDirection.Reconcile =>
|
case SyncDirection.Reconcile =>
|
||||||
checkSlotStatus(slotIndex) match {
|
checkSlotStatus(slotIndex) match {
|
||||||
case SlotStatus.Synchronized => // no-op
|
case SlotStatus.Synchronized => // no-op
|
||||||
|
|
||||||
// let's just grab whatever we have
|
// let's just grab whatever we have
|
||||||
case SlotStatus.DesktopNonEmpty => doSync(slotIndex, SyncDirection.DesktopToBrain)
|
case SlotStatus.DesktopNonEmpty => doSync(slotIndex, SyncDirection.DesktopToBrain)
|
||||||
case SlotStatus.BrainNonEmpty => doSync(slotIndex, SyncDirection.BrainToDesktop)
|
case SlotStatus.BrainNonEmpty => doSync(slotIndex, SyncDirection.BrainToDesktop)
|
||||||
@ -249,7 +246,7 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def checkSlotStatus(slotIndex: Int): SlotStatus =
|
private def checkSlotStatus(slotIndex: Int): SlotStatus = {
|
||||||
(Slot(slotIndex).get, brainInventory.inventory(slotIndex).get) match {
|
(Slot(slotIndex).get, brainInventory.inventory(slotIndex).get) match {
|
||||||
case (Some(item), Some(entity)) if item.component eq entity => SlotStatus.Synchronized
|
case (Some(item), Some(entity)) if item.component eq entity => SlotStatus.Synchronized
|
||||||
case (Some(_), Some(_)) => SlotStatus.Conflict
|
case (Some(_), Some(_)) => SlotStatus.Conflict
|
||||||
@ -257,6 +254,7 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
|
|||||||
case (None, Some(_)) => SlotStatus.BrainNonEmpty
|
case (None, Some(_)) => SlotStatus.BrainNonEmpty
|
||||||
case (None, None) => SlotStatus.Synchronized
|
case (None, None) => SlotStatus.Synchronized
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object SyncedInventory {
|
object SyncedInventory {
|
||||||
@ -267,42 +265,29 @@ object SyncedInventory {
|
|||||||
object SyncDirection extends Enumeration {
|
object SyncDirection extends Enumeration {
|
||||||
type SyncDirection = Value
|
type SyncDirection = Value
|
||||||
|
|
||||||
/**
|
/** Apply a change from the [[SyncedInventory]] to [[SyncedInventory.brainInventory]]. */
|
||||||
* Apply a change from the [[SyncedInventory]] to [[SyncedInventory.brainInventory]].
|
|
||||||
*/
|
|
||||||
val DesktopToBrain: SyncDirection = Value
|
val DesktopToBrain: SyncDirection = Value
|
||||||
|
|
||||||
/**
|
/** Apply a change from the [[SyncedInventory.brainInventory]] to [[SyncedInventory]]. */
|
||||||
* Apply a change from the [[SyncedInventory.brainInventory]] to [[SyncedInventory]].
|
|
||||||
*/
|
|
||||||
val BrainToDesktop: SyncDirection = Value
|
val BrainToDesktop: SyncDirection = Value
|
||||||
|
|
||||||
/**
|
/** Try to reconcile conflicts between [[SyncedInventory]] and its [[SyncedInventory.brainInventory]]. */
|
||||||
* Try to reconcile conflicts between [[SyncedInventory]] and its [[SyncedInventory.brainInventory]].
|
|
||||||
*/
|
|
||||||
val Reconcile: SyncDirection = Value
|
val Reconcile: SyncDirection = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
object SlotStatus extends Enumeration {
|
object SlotStatus extends Enumeration {
|
||||||
type SlotStatus = Value
|
type SlotStatus = Value
|
||||||
|
|
||||||
/**
|
/** The slots are in sync (both are empty or they contain the same entity). */
|
||||||
* The slots are in sync (both are empty or they contain the same entity).
|
|
||||||
*/
|
|
||||||
val Synchronized: SlotStatus = Value
|
val Synchronized: SlotStatus = Value
|
||||||
|
|
||||||
/**
|
/** [[SyncedInventory]]'s slot is non-empty; [[SyncedInventory.brainInventory]]'s is empty. */
|
||||||
* [[SyncedInventory]]'s slot is non-empty; [[SyncedInventory.brainInventory]]'s is empty.
|
|
||||||
*/
|
|
||||||
val DesktopNonEmpty: SlotStatus = Value
|
val DesktopNonEmpty: SlotStatus = Value
|
||||||
|
|
||||||
/**
|
/** [[SyncedInventory]]'s slot is empty; [[SyncedInventory.brainInventory]]'s isn't. */
|
||||||
* [[SyncedInventory]]'s slot is empty; [[SyncedInventory.brainInventory]]'s isn't.
|
|
||||||
*/
|
|
||||||
val BrainNonEmpty: SlotStatus = Value
|
val BrainNonEmpty: SlotStatus = Value
|
||||||
|
|
||||||
/**
|
/** The [[SyncedInventory]] and the [[SyncedInventory.brainInventory]] are both non-empty
|
||||||
* The [[SyncedInventory]] and the [[SyncedInventory.brainInventory]] are both non-empty
|
|
||||||
* and contain different items.
|
* and contain different items.
|
||||||
*/
|
*/
|
||||||
val Conflict: SlotStatus = Value
|
val Conflict: SlotStatus = Value
|
||||||
|
|||||||
@ -8,10 +8,7 @@ import totoro.ocelot.brain.entity.ComponentBus
|
|||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class ComponentBusItem(val componentBus: ComponentBus)
|
class ComponentBusItem(val componentBus: ComponentBus) extends Item with ComponentItem {
|
||||||
extends Item
|
|
||||||
with ComponentItem
|
|
||||||
{
|
|
||||||
override def component: Entity with Environment = componentBus
|
override def component: Entity with Environment = componentBus
|
||||||
override def showAddress: Boolean = false
|
override def showAddress: Boolean = false
|
||||||
override def factory: ItemFactory = new ComponentBusItem.Factory(componentBus.tier)
|
override def factory: ItemFactory = new ComponentBusItem.Factory(componentBus.tier)
|
||||||
|
|||||||
@ -13,7 +13,9 @@ import totoro.ocelot.brain.util.Tier.Tier
|
|||||||
abstract class DataCardItem extends Item with ComponentItem with PersistableItem with CardItem {
|
abstract class DataCardItem extends Item with ComponentItem with PersistableItem with CardItem {
|
||||||
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
||||||
super.fillTooltip(tooltip)
|
super.fillTooltip(tooltip)
|
||||||
tooltip.addLine(s"Soft limit (${Settings.get.dataCardTimeout} sec slowdown): ${Settings.get.dataCardSoftLimit} bytes")
|
tooltip.addLine(
|
||||||
|
s"Soft limit (${Settings.get.dataCardTimeout} sec slowdown): ${Settings.get.dataCardSoftLimit} bytes"
|
||||||
|
)
|
||||||
tooltip.addLine(s"Hard limit (fail): ${Settings.get.dataCardHardLimit} bytes")
|
tooltip.addLine(s"Hard limit (fail): ${Settings.get.dataCardHardLimit} bytes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,7 @@ import totoro.ocelot.brain.entity.traits.Floppy
|
|||||||
import totoro.ocelot.brain.entity.{DiskDriveMountable, FloppyDiskDrive}
|
import totoro.ocelot.brain.entity.{DiskDriveMountable, FloppyDiskDrive}
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class DiskDriveMountableItem(val diskDriveMountable: DiskDriveMountable)
|
class DiskDriveMountableItem(val diskDriveMountable: DiskDriveMountable) extends RackMountableItem with DiskDriveAware {
|
||||||
extends RackMountableItem
|
|
||||||
with DiskDriveAware
|
|
||||||
{
|
|
||||||
override def floppyDiskDrive: FloppyDiskDrive = diskDriveMountable
|
override def floppyDiskDrive: FloppyDiskDrive = diskDriveMountable
|
||||||
|
|
||||||
override def component: DiskDriveMountable = diskDriveMountable
|
override def component: DiskDriveMountable = diskDriveMountable
|
||||||
|
|||||||
@ -32,8 +32,11 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (source.isEmpty && eeprom != null) {
|
if (source.isEmpty && eeprom != null) {
|
||||||
if (eeprom.codeBytes != null)
|
if (eeprom.codeBytes != null) {
|
||||||
tooltip.addLine(s"Code: ${eeprom.codeBytes.map(_.length).getOrElse(0)} bytes / ${Settings.get.eepromSize} bytes")
|
tooltip.addLine(
|
||||||
|
s"Code: ${eeprom.codeBytes.map(_.length).getOrElse(0)} bytes / ${Settings.get.eepromSize} bytes"
|
||||||
|
)
|
||||||
|
}
|
||||||
if (eeprom.volatileData != null)
|
if (eeprom.volatileData != null)
|
||||||
tooltip.addLine(s"Data: ${eeprom.volatileData.length} bytes / ${Settings.get.eepromDataSize} bytes")
|
tooltip.addLine(s"Data: ${eeprom.volatileData.length} bytes / ${Settings.get.eepromDataSize} bytes")
|
||||||
if (eeprom.readonly)
|
if (eeprom.readonly)
|
||||||
@ -59,13 +62,14 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis
|
|||||||
onConfirmed = { text =>
|
onConfirmed = { text =>
|
||||||
eeprom.codeURL = Some(new URL(text))
|
eeprom.codeURL = Some(new URL(text))
|
||||||
},
|
},
|
||||||
inputValidator = text =>
|
inputValidator = text => {
|
||||||
try {
|
try {
|
||||||
new URL(text)
|
new URL(text)
|
||||||
true
|
true
|
||||||
} catch {
|
} catch {
|
||||||
case _: MalformedURLException => false
|
case _: MalformedURLException => false
|
||||||
},
|
}
|
||||||
|
},
|
||||||
).show()
|
).show()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
|||||||
import totoro.ocelot.brain.entity.fs.ReadWriteLabel
|
import totoro.ocelot.brain.entity.fs.ReadWriteLabel
|
||||||
import totoro.ocelot.brain.entity.traits.{Disk, Entity, Floppy}
|
import totoro.ocelot.brain.entity.traits.{Disk, Entity, Floppy}
|
||||||
import totoro.ocelot.brain.entity.{FloppyManaged, FloppyUnmanaged}
|
import totoro.ocelot.brain.entity.{FloppyManaged, FloppyUnmanaged}
|
||||||
import totoro.ocelot.brain.loot.Loot.{LootFloppy, FloppyFactory => LootFloppyFactory}
|
import totoro.ocelot.brain.loot.Loot.{FloppyFactory => LootFloppyFactory, LootFloppy}
|
||||||
import totoro.ocelot.brain.util.DyeColor
|
import totoro.ocelot.brain.util.DyeColor
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,8 @@ import totoro.ocelot.brain.entity.GraphicsCard
|
|||||||
import totoro.ocelot.brain.entity.traits.{Entity, GenericGPU}
|
import totoro.ocelot.brain.entity.traits.{Entity, GenericGPU}
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class GraphicsCardItem(val gpu: GraphicsCard) extends Item with ComponentItem with PersistableItem with CardItem with GpuLikeItem {
|
class GraphicsCardItem(val gpu: GraphicsCard)
|
||||||
|
extends Item with ComponentItem with PersistableItem with CardItem with GpuLikeItem {
|
||||||
override def component: Entity with GenericGPU = gpu
|
override def component: Entity with GenericGPU = gpu
|
||||||
|
|
||||||
override def factory: GraphicsCardItem.Factory = new GraphicsCardItem.Factory(gpu.tier)
|
override def factory: GraphicsCardItem.Factory = new GraphicsCardItem.Factory(gpu.tier)
|
||||||
|
|||||||
@ -14,12 +14,7 @@ import totoro.ocelot.brain.util.Tier
|
|||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class OcelotCardItem(val ocelotCard: OcelotCard)
|
class OcelotCardItem(val ocelotCard: OcelotCard)
|
||||||
extends Item
|
extends Item with ComponentItem with OcelotInterfaceLogStorage with PersistableItem with CardItem with Logging {
|
||||||
with ComponentItem
|
|
||||||
with OcelotInterfaceLogStorage
|
|
||||||
with PersistableItem
|
|
||||||
with CardItem
|
|
||||||
with Logging {
|
|
||||||
|
|
||||||
override def component: OcelotCard = ocelotCard
|
override def component: OcelotCard = ocelotCard
|
||||||
|
|
||||||
@ -40,6 +35,7 @@ class OcelotCardItem(val ocelotCard: OcelotCard)
|
|||||||
override def factory: ItemFactory = OcelotCardItem.Factory
|
override def factory: ItemFactory = OcelotCardItem.Factory
|
||||||
|
|
||||||
private val OcelotSays = Array("meow", ":3", "♥", "meooow", "~(=^–^)", "/ᐠ。ꞈ。ᐟ\\", "=^._.^=", "=’①。①’=")
|
private val OcelotSays = Array("meow", ":3", "♥", "meooow", "~(=^–^)", "/ᐠ。ꞈ。ᐟ\\", "=^._.^=", "=’①。①’=")
|
||||||
|
|
||||||
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
||||||
super.fillTooltip(tooltip)
|
super.fillTooltip(tooltip)
|
||||||
tooltip.addLine(OcelotSays(((System.currentTimeMillis() / 5000) % OcelotSays.length).toInt))
|
tooltip.addLine(OcelotSays(((System.currentTimeMillis() / 5000) % OcelotSays.length).toInt))
|
||||||
|
|||||||
@ -13,9 +13,7 @@ import totoro.ocelot.brain.nbt.NBTTagCompound
|
|||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
abstract class RedstoneCardItem extends Item with ComponentItem with PersistableItem with CardItem {
|
abstract class RedstoneCardItem extends Item with ComponentItem with PersistableItem with CardItem {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object RedstoneCardItem {
|
object RedstoneCardItem {
|
||||||
abstract class Factory extends ItemFactory {
|
abstract class Factory extends ItemFactory {
|
||||||
|
|||||||
@ -10,10 +10,7 @@ import totoro.ocelot.brain.util.Tier
|
|||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class SelfDestructingCardItem(val card: SelfDestructingCard)
|
class SelfDestructingCardItem(val card: SelfDestructingCard)
|
||||||
extends Item
|
extends Item with ComponentItem with PersistableItem with CardItem {
|
||||||
with ComponentItem
|
|
||||||
with PersistableItem
|
|
||||||
with CardItem {
|
|
||||||
|
|
||||||
override def component: Entity with Environment = card
|
override def component: Entity with Environment = card
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,7 @@ import totoro.ocelot.brain.entity.traits.Inventory
|
|||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class ServerItem(val server: Server)
|
class ServerItem(val server: Server) extends RackMountableItem with AudibleComputerAware {
|
||||||
extends RackMountableItem
|
|
||||||
with AudibleComputerAware
|
|
||||||
{
|
|
||||||
override def component: Server = server
|
override def component: Server = server
|
||||||
override def factory: ItemFactory = new ServerItem.Factory(server.tier)
|
override def factory: ItemFactory = new ServerItem.Factory(server.tier)
|
||||||
|
|
||||||
@ -44,24 +41,26 @@ class ServerItem(val server: Server)
|
|||||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||||
|
|
||||||
case Tier.Two =>
|
case Tier.Two =>
|
||||||
cardSlots = addSlotWidgets(new CardSlotWidget(_, Tier.Three), new CardSlotWidget(_, Tier.Two), new CardSlotWidget(_, Tier.Two))
|
cardSlots = addSlotWidgets(new CardSlotWidget(_, Tier.Three), new CardSlotWidget(_, Tier.Two),
|
||||||
|
new CardSlotWidget(_, Tier.Two))
|
||||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
||||||
componentBusSlots = addSlotWidgets(new ComponentBusSlotWidget(_, Tier.Three), new ComponentBusSlotWidget(_, Tier.Three))
|
componentBusSlots =
|
||||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
|
addSlotWidgets(new ComponentBusSlotWidget(_, Tier.Three), new ComponentBusSlotWidget(_, Tier.Three))
|
||||||
|
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three),
|
||||||
|
new MemorySlotWidget(_, Tier.Three))
|
||||||
diskSlots = addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Three))
|
diskSlots = addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Three))
|
||||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
cardSlots =
|
cardSlots = {
|
||||||
if (server.tier == Tier.Three) {
|
if (server.tier == Tier.Three) {
|
||||||
addSlotWidgets(
|
addSlotWidgets(
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
new CardSlotWidget(_, Tier.Two),
|
new CardSlotWidget(_, Tier.Two),
|
||||||
new CardSlotWidget(_, Tier.Two)
|
new CardSlotWidget(_, Tier.Two),
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
addSlotWidgets(
|
addSlotWidgets(
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
@ -69,6 +68,7 @@ class ServerItem(val server: Server)
|
|||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ class ServerItem(val server: Server)
|
|||||||
new MemorySlotWidget(_, Tier.Three),
|
new MemorySlotWidget(_, Tier.Three),
|
||||||
new MemorySlotWidget(_, Tier.Three),
|
new MemorySlotWidget(_, Tier.Three),
|
||||||
new MemorySlotWidget(_, Tier.Three),
|
new MemorySlotWidget(_, Tier.Three),
|
||||||
new MemorySlotWidget(_, Tier.Three)
|
new MemorySlotWidget(_, Tier.Three),
|
||||||
)
|
)
|
||||||
|
|
||||||
diskSlots = addSlotWidgets(
|
diskSlots = addSlotWidgets(
|
||||||
|
|||||||
@ -12,11 +12,7 @@ import totoro.ocelot.brain.util.Tier
|
|||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class SoundCardItem(val soundCard: SoundCard)
|
class SoundCardItem(val soundCard: SoundCard)
|
||||||
extends Item
|
extends Item with ComponentItem with PersistableItem with CardItem with Windowed[SoundCardWindow] {
|
||||||
with ComponentItem
|
|
||||||
with PersistableItem
|
|
||||||
with CardItem
|
|
||||||
with Windowed[SoundCardWindow] {
|
|
||||||
|
|
||||||
override def createWindow(): SoundCardWindow = new SoundCardWindow(soundCard)
|
override def createWindow(): SoundCardWindow = new SoundCardWindow(soundCard)
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,7 @@ import totoro.ocelot.brain.entity.WirelessNetworkCard
|
|||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
abstract class WirelessNetworkCardItem(override val card: WirelessNetworkCard)
|
abstract class WirelessNetworkCardItem(override val card: WirelessNetworkCard) extends NetworkCardItem(card) {}
|
||||||
extends NetworkCardItem(card) {
|
|
||||||
}
|
|
||||||
|
|
||||||
object WirelessNetworkCardItem {
|
object WirelessNetworkCardItem {
|
||||||
abstract class Factory extends ItemFactory {
|
abstract class Factory extends ItemFactory {
|
||||||
|
|||||||
@ -2,7 +2,5 @@ package ocelot.desktop.inventory.traits
|
|||||||
|
|
||||||
import ocelot.desktop.inventory.Item
|
import ocelot.desktop.inventory.Item
|
||||||
|
|
||||||
/**
|
/** A marker trait implemented by [[Item]]s that can be put into card slots. */
|
||||||
* A marker trait implemented by [[Item]]s that can be put into card slots.
|
|
||||||
*/
|
|
||||||
trait CardItem extends ComponentItem
|
trait CardItem extends ComponentItem
|
||||||
|
|||||||
@ -8,14 +8,13 @@ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
|||||||
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware}
|
import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware}
|
||||||
|
|
||||||
/**
|
/** Implemented by [[Item]]s that wrap a component.
|
||||||
* Implemented by [[Item]]s that wrap a component.
|
|
||||||
*
|
*
|
||||||
* @note Subclasses must provide a unary constructor that accepts the [[component]].
|
* @note Subclasses must provide a unary constructor that accepts the [[component]].
|
||||||
*/
|
*/
|
||||||
trait ComponentItem extends Item with PersistableItem {
|
trait ComponentItem extends Item with PersistableItem {
|
||||||
/**
|
|
||||||
* The component wrapped by this item.
|
/** The component wrapped by this item.
|
||||||
*
|
*
|
||||||
* @note The value must already be available during the initialization.
|
* @note The value must already be available during the initialization.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -8,9 +8,7 @@ import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
|||||||
import totoro.ocelot.brain.entity.machine.MachineAPI
|
import totoro.ocelot.brain.entity.machine.MachineAPI
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU}
|
import totoro.ocelot.brain.entity.traits.{Entity, GenericCPU}
|
||||||
|
|
||||||
/**
|
/** An [[Item]] that acts as a CPU and can therefore be inserted into a CPU slot. */
|
||||||
* An [[Item]] that acts as a CPU and can therefore be inserted into a CPU slot.
|
|
||||||
*/
|
|
||||||
trait CpuLikeItem extends ComponentItem {
|
trait CpuLikeItem extends ComponentItem {
|
||||||
override def component: Entity with GenericCPU
|
override def component: Entity with GenericCPU
|
||||||
|
|
||||||
|
|||||||
@ -13,9 +13,7 @@ import totoro.ocelot.brain.util.DyeColor
|
|||||||
import javax.swing.JFileChooser
|
import javax.swing.JFileChooser
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
/**
|
/** A utility mixin for HDDs and floppies. */
|
||||||
* A utility mixin for HDDs and floppies.
|
|
||||||
*/
|
|
||||||
trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
||||||
override def component: Entity with Disk
|
override def component: Entity with Disk
|
||||||
|
|
||||||
@ -51,11 +49,12 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
|||||||
// Real path
|
// Real path
|
||||||
component match {
|
component match {
|
||||||
case diskManaged: DiskManaged =>
|
case diskManaged: DiskManaged =>
|
||||||
DiskItem.addRealPathContextMenuEntries(menu, diskManaged, realPathSetter => {
|
DiskItem.addRealPathContextMenuEntries(menu, diskManaged,
|
||||||
reinserting {
|
realPathSetter => {
|
||||||
realPathSetter()
|
reinserting {
|
||||||
}
|
realPathSetter()
|
||||||
})
|
}
|
||||||
|
})
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,10 +93,11 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object DiskItem {
|
object DiskItem {
|
||||||
def addRealPathContextMenuEntries(menu: ContextMenu, diskRealPathAware: DiskRealPathAware, realPathSetter: (() => Unit) => Unit): Unit = {
|
def addRealPathContextMenuEntries(menu: ContextMenu, diskRealPathAware: DiskRealPathAware,
|
||||||
|
realPathSetter: (() => Unit) => Unit): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry(
|
menu.addEntry(ContextMenuEntry(
|
||||||
if (diskRealPathAware.customRealPath.isDefined) "Change directory" else "Set directory",
|
if (diskRealPathAware.customRealPath.isDefined) "Change directory" else "Set directory",
|
||||||
IconSource.Folder
|
IconSource.Folder,
|
||||||
) {
|
) {
|
||||||
OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
||||||
Try {
|
Try {
|
||||||
|
|||||||
@ -3,7 +3,5 @@ package ocelot.desktop.inventory.traits
|
|||||||
import ocelot.desktop.inventory.Item
|
import ocelot.desktop.inventory.Item
|
||||||
import ocelot.desktop.util.Persistable
|
import ocelot.desktop.util.Persistable
|
||||||
|
|
||||||
/**
|
/** Provides serialization and deserialization for an [[Item]]. */
|
||||||
* Provides serialization and deserialization for an [[Item]].
|
|
||||||
*/
|
|
||||||
trait PersistableItem extends Item with Persistable
|
trait PersistableItem extends Item with Persistable
|
||||||
|
|||||||
@ -6,10 +6,7 @@ import ocelot.desktop.ui.widget.contextmenu.ContextMenu
|
|||||||
import totoro.ocelot.brain.entity.result
|
import totoro.ocelot.brain.entity.result
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, RackMountable}
|
import totoro.ocelot.brain.entity.traits.{Entity, RackMountable}
|
||||||
|
|
||||||
trait RackMountableItem
|
trait RackMountableItem extends Item with ComponentItem {
|
||||||
extends Item
|
|
||||||
with ComponentItem
|
|
||||||
{
|
|
||||||
override def component: Entity with RackMountable
|
override def component: Entity with RackMountable
|
||||||
|
|
||||||
def isInRack: Boolean = slot.exists(_.inventory.isInstanceOf[RackNode])
|
def isInRack: Boolean = slot.exists(_.inventory.isInstanceOf[RackNode])
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import java.util.Calendar
|
|||||||
import scala.collection.mutable.ArrayBuffer
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceAware)
|
abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceAware)
|
||||||
extends EntityNode(entity)
|
extends EntityNode(entity)
|
||||||
with SyncedInventory
|
with SyncedInventory
|
||||||
with DiskActivityHandler
|
with DiskActivityHandler
|
||||||
with OcelotLogParticleNode
|
with OcelotLogParticleNode
|
||||||
|
|||||||
@ -220,17 +220,18 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
|||||||
private def move(pos: Vector2D): Unit = {
|
private def move(pos: Vector2D): Unit = {
|
||||||
val oldPos = position
|
val oldPos = position
|
||||||
|
|
||||||
val desiredPos =
|
val desiredPos = {
|
||||||
if (KeyEvents.isControlDown)
|
if (KeyEvents.isControlDown) {
|
||||||
(pos - workspaceView.cameraOffset).snap(Size) + // snap the position to the grid relative to the origin
|
(pos - workspaceView.cameraOffset).snap(Size) + // snap the position to the grid relative to the origin
|
||||||
workspaceView.cameraOffset - // restore the camera offset
|
workspaceView.cameraOffset - // restore the camera offset
|
||||||
grabPoint.snap(Size) + // accounts for multi-block screens
|
grabPoint.snap(Size) + // accounts for multi-block screens
|
||||||
Vector2D(
|
Vector2D(
|
||||||
((Size - width) % Size) / 2,
|
((Size - width) % Size) / 2,
|
||||||
((Size - height) % Size) / 2,
|
((Size - height) % Size) / 2,
|
||||||
) // if a node is not full-size, moves it to the center of the grid cell
|
) // if a node is not full-size, moves it to the center of the grid cell
|
||||||
else
|
} else
|
||||||
pos - grabPoint
|
pos - grabPoint
|
||||||
|
}
|
||||||
|
|
||||||
position = desiredPos
|
position = desiredPos
|
||||||
workspaceView.resolveCollision(this)
|
workspaceView.resolveCollision(this)
|
||||||
@ -260,7 +261,7 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
|||||||
|
|
||||||
g.sprite(
|
g.sprite(
|
||||||
iconSource.path,
|
iconSource.path,
|
||||||
position. x + HighlightThickness,
|
position.x + HighlightThickness,
|
||||||
position.y + HighlightThickness,
|
position.y + HighlightThickness,
|
||||||
size.width - HighlightThickness * 2,
|
size.width - HighlightThickness * 2,
|
||||||
size.height - HighlightThickness * 2,
|
size.height - HighlightThickness * 2,
|
||||||
|
|||||||
@ -3,7 +3,10 @@ package ocelot.desktop.node
|
|||||||
import ocelot.desktop.entity.{Camera, OcelotBlock, OpenFMRadio}
|
import ocelot.desktop.entity.{Camera, OcelotBlock, OpenFMRadio}
|
||||||
import ocelot.desktop.graphics.IconSource
|
import ocelot.desktop.graphics.IconSource
|
||||||
import ocelot.desktop.node.nodes._
|
import ocelot.desktop.node.nodes._
|
||||||
import totoro.ocelot.brain.entity.{Cable, Case, ColorfulLamp, FloppyDiskDrive, HologramProjector, IronNoteBlock, Microcontroller, NoteBlock, Rack, Raid, Relay, Screen, TapeDrive}
|
import totoro.ocelot.brain.entity.{
|
||||||
|
Cable, Case, ColorfulLamp, FloppyDiskDrive, HologramProjector, IronNoteBlock, Microcontroller, NoteBlock, Rack, Raid,
|
||||||
|
Relay, Screen, TapeDrive,
|
||||||
|
}
|
||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import ocelot.desktop.graphics.IconSource
|
|||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class NodeType(val name: String, val icon: IconSource, val tier: Option[Tier], factory: => Node)
|
class NodeType(val name: String, val icon: IconSource, val tier: Option[Tier], factory: => Node)
|
||||||
extends Ordered[NodeType] {
|
extends Ordered[NodeType] {
|
||||||
def make(): Node = factory
|
def make(): Node = factory
|
||||||
|
|
||||||
override def compare(that: NodeType): Int = this.name.compare(that.name)
|
override def compare(that: NodeType): Int = this.name.compare(that.name)
|
||||||
|
|||||||
@ -30,10 +30,10 @@ class NodeTypeWidget(val nodeType: NodeType) extends Widget with MouseHandler wi
|
|||||||
private val labelTooltip = new LabelTooltip(nodeType.name)
|
private val labelTooltip = new LabelTooltip(nodeType.name)
|
||||||
|
|
||||||
def onHoverEnter(): Unit =
|
def onHoverEnter(): Unit =
|
||||||
root.get.tooltipPool.addTooltip(labelTooltip)
|
root.get.tooltipPool.addTooltip(labelTooltip)
|
||||||
|
|
||||||
def onHoverLeave(): Unit =
|
def onHoverLeave(): Unit =
|
||||||
root.get.tooltipPool.closeTooltip(labelTooltip)
|
root.get.tooltipPool.closeTooltip(labelTooltip)
|
||||||
|
|
||||||
override def draw(g: Graphics): Unit = {
|
override def draw(g: Graphics): Unit = {
|
||||||
val size = Spritesheet.spriteSize(nodeType.icon) * 4
|
val size = Spritesheet.spriteSize(nodeType.icon) * 4
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import scala.util.Random
|
|||||||
trait OcelotLogParticleNode extends Node {
|
trait OcelotLogParticleNode extends Node {
|
||||||
private case class LogParticle(
|
private case class LogParticle(
|
||||||
var time: Float = -LogParticleGrow,
|
var time: Float = -LogParticleGrow,
|
||||||
angle: Float = Random.between(0f, 2 * math.Pi.toFloat * LogParticleMaxAngle)
|
angle: Float = Random.between(0f, 2 * math.Pi.toFloat * LogParticleMaxAngle),
|
||||||
)
|
)
|
||||||
|
|
||||||
// access should be synchronized because log particles are added in the update thread
|
// access should be synchronized because log particles are added in the update thread
|
||||||
@ -76,5 +76,3 @@ object OcelotLogParticleNode {
|
|||||||
private val LogParticleMoveSpeed: Float = 1f
|
private val LogParticleMoveSpeed: Float = 1f
|
||||||
private val LogParticleMoveDistance: Float = 20f
|
private val LogParticleMoveDistance: Float = 20f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -29,10 +29,9 @@ trait PositionalSoundSourcesNode extends Node {
|
|||||||
soundPosition = Vector3D(
|
soundPosition = Vector3D(
|
||||||
(nodeCenterX - rootWidthHalf) / rootWidthHalf * limit,
|
(nodeCenterX - rootWidthHalf) / rootWidthHalf * limit,
|
||||||
(nodeCenterY - rootHeightHalf) / rootHeightHalf * limit,
|
(nodeCenterY - rootHeightHalf) / rootHeightHalf * limit,
|
||||||
0
|
0,
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
soundPosition = Vector3D.Zero
|
soundPosition = Vector3D.Zero
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,9 +16,9 @@ class ColorfulLampNode(val lamp: ColorfulLamp) extends EntityNode(lamp) with Lab
|
|||||||
super.draw(g)
|
super.draw(g)
|
||||||
|
|
||||||
lastColor = RGBAColor(
|
lastColor = RGBAColor(
|
||||||
(((lamp.color >>> 10) & 0x1F) << 3).toShort,
|
(((lamp.color >>> 10) & 0x1f) << 3).toShort,
|
||||||
(((lamp.color >>> 5) & 0x1F) << 3).toShort,
|
(((lamp.color >>> 5) & 0x1f) << 3).toShort,
|
||||||
(((lamp.color >>> 0) & 0x1F) << 3).toShort
|
(((lamp.color >>> 0) & 0x1f) << 3).toShort,
|
||||||
)
|
)
|
||||||
|
|
||||||
g.rect(position.x + 2, position.y + 2, size.width - 4, size.height - 4, lastColor)
|
g.rect(position.x + 2, position.y + 2, size.width - 4, size.height - 4, lastColor)
|
||||||
|
|||||||
@ -15,10 +15,7 @@ import totoro.ocelot.brain.entity.traits.Inventory
|
|||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
|
|
||||||
class ComputerNode(val computerCase: Case)
|
class ComputerNode(val computerCase: Case)
|
||||||
extends ComputerAwareNode(computerCase)
|
extends ComputerAwareNode(computerCase) with AudibleComputerAware with WindowedNode[ComputerWindow] {
|
||||||
with AudibleComputerAware
|
|
||||||
with WindowedNode[ComputerWindow]
|
|
||||||
{
|
|
||||||
override val iconSource: IconSource = IconSource.Nodes.Computer.Default
|
override val iconSource: IconSource = IconSource.Nodes.Computer.Default
|
||||||
override def iconColor: Color = TierColor.get(computerCase.tier)
|
override def iconColor: Color = TierColor.get(computerCase.tier)
|
||||||
|
|
||||||
@ -80,31 +77,31 @@ class ComputerNode(val computerCase: Case)
|
|||||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
cardSlots =
|
cardSlots = {
|
||||||
if (computerCase.tier == Tier.Three) {
|
if (computerCase.tier == Tier.Three) {
|
||||||
addSlotWidgets(
|
addSlotWidgets(
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
new CardSlotWidget(_, Tier.Two),
|
new CardSlotWidget(_, Tier.Two),
|
||||||
new CardSlotWidget(_, Tier.Two)
|
new CardSlotWidget(_, Tier.Two),
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
addSlotWidgets(
|
addSlotWidgets(
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
|
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
|
||||||
|
|
||||||
diskSlots =
|
diskSlots = {
|
||||||
if (computerCase.tier == Tier.Three) {
|
if (computerCase.tier == Tier.Three) {
|
||||||
addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Two))
|
addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Two))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Three))
|
addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Three))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
floppySlot = Some(addSlotWidget(new FloppySlotWidget(_)))
|
floppySlot = Some(addSlotWidget(new FloppySlotWidget(_)))
|
||||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import ocelot.desktop.windows.DiskDriveWindow
|
|||||||
import totoro.ocelot.brain.entity.FloppyDiskDrive
|
import totoro.ocelot.brain.entity.FloppyDiskDrive
|
||||||
|
|
||||||
class DiskDriveNode(entity: FloppyDiskDrive)
|
class DiskDriveNode(entity: FloppyDiskDrive)
|
||||||
extends EntityNode(entity)
|
extends EntityNode(entity)
|
||||||
with SyncedInventory
|
with SyncedInventory
|
||||||
with LabeledEntityNode
|
with LabeledEntityNode
|
||||||
with DiskDriveAware
|
with DiskDriveAware
|
||||||
|
|||||||
@ -6,12 +6,9 @@ import ocelot.desktop.windows.HologramProjectorWindow
|
|||||||
import totoro.ocelot.brain.entity.HologramProjector
|
import totoro.ocelot.brain.entity.HologramProjector
|
||||||
|
|
||||||
class HologramProjectorNode(val hologramProjector: HologramProjector)
|
class HologramProjectorNode(val hologramProjector: HologramProjector)
|
||||||
extends EntityNode(hologramProjector)
|
extends EntityNode(hologramProjector) with LabeledEntityNode with WindowedNode[HologramProjectorWindow] {
|
||||||
with LabeledEntityNode
|
|
||||||
with WindowedNode[HologramProjectorWindow] {
|
|
||||||
|
|
||||||
override def iconSource: IconSource = IconSource.Nodes.HologramProjector(hologramProjector.tier)
|
override def iconSource: IconSource = IconSource.Nodes.HologramProjector(hologramProjector.tier)
|
||||||
|
|
||||||
override def createWindow(): HologramProjectorWindow = new HologramProjectorWindow(this)
|
override def createWindow(): HologramProjectorWindow = new HologramProjectorWindow(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ class IronNoteBlockNode(val ironNoteBlock: IronNoteBlock) extends NoteBlockNodeB
|
|||||||
EventBus.send(NoteBlockTriggerEvent(
|
EventBus.send(NoteBlockTriggerEvent(
|
||||||
ironNoteBlock.node.address,
|
ironNoteBlock.node.address,
|
||||||
Instruments(Random.nextInt(Instruments.length))._1,
|
Instruments(Random.nextInt(Instruments.length))._1,
|
||||||
Random.nextInt(NumberOfPitches)
|
Random.nextInt(NumberOfPitches),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
|
|||||||
@ -16,11 +16,10 @@ import totoro.ocelot.brain.network
|
|||||||
import totoro.ocelot.brain.util.{Direction, Tier}
|
import totoro.ocelot.brain.util.{Direction, Tier}
|
||||||
|
|
||||||
class MicrocontrollerNode(val microcontroller: Microcontroller)
|
class MicrocontrollerNode(val microcontroller: Microcontroller)
|
||||||
extends ComputerAwareNode(microcontroller)
|
extends ComputerAwareNode(microcontroller)
|
||||||
with ComputerAware
|
with ComputerAware
|
||||||
with DefaultSlotItemsFillable
|
with DefaultSlotItemsFillable
|
||||||
with WindowedNode[ComputerWindow]
|
with WindowedNode[ComputerWindow] {
|
||||||
{
|
|
||||||
override val iconSource: IconSource = IconSource.Nodes.Microcontroller.Default
|
override val iconSource: IconSource = IconSource.Nodes.Microcontroller.Default
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
@ -49,7 +48,7 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
NodePort(Some(Direction.Top)),
|
NodePort(Some(Direction.Top)),
|
||||||
NodePort(Some(Direction.Back)),
|
NodePort(Some(Direction.Back)),
|
||||||
NodePort(Some(Direction.Right)),
|
NodePort(Some(Direction.Right)),
|
||||||
NodePort(Some(Direction.Left))
|
NodePort(Some(Direction.Left)),
|
||||||
)
|
)
|
||||||
|
|
||||||
override def getNodeByPort(port: NodePort): network.Node =
|
override def getNodeByPort(port: NodePort): network.Node =
|
||||||
@ -71,14 +70,14 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
case Tier.One =>
|
case Tier.One =>
|
||||||
cardSlots = addSlotWidgets(
|
cardSlots = addSlotWidgets(
|
||||||
new CardSlotWidget(_, Tier.One),
|
new CardSlotWidget(_, Tier.One),
|
||||||
new CardSlotWidget(_, Tier.One)
|
new CardSlotWidget(_, Tier.One),
|
||||||
)
|
)
|
||||||
|
|
||||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.One))
|
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.One))
|
||||||
|
|
||||||
memorySlots = addSlotWidgets(
|
memorySlots = addSlotWidgets(
|
||||||
new MemorySlotWidget(_, Tier.One),
|
new MemorySlotWidget(_, Tier.One),
|
||||||
new MemorySlotWidget(_, Tier.One)
|
new MemorySlotWidget(_, Tier.One),
|
||||||
)
|
)
|
||||||
|
|
||||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||||
@ -86,14 +85,14 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
case Tier.Two =>
|
case Tier.Two =>
|
||||||
cardSlots = addSlotWidgets(
|
cardSlots = addSlotWidgets(
|
||||||
new CardSlotWidget(_, Tier.Two),
|
new CardSlotWidget(_, Tier.Two),
|
||||||
new CardSlotWidget(_, Tier.One)
|
new CardSlotWidget(_, Tier.One),
|
||||||
)
|
)
|
||||||
|
|
||||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.One))
|
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.One))
|
||||||
|
|
||||||
memorySlots = addSlotWidgets(
|
memorySlots = addSlotWidgets(
|
||||||
new MemorySlotWidget(_, Tier.One),
|
new MemorySlotWidget(_, Tier.One),
|
||||||
new MemorySlotWidget(_, Tier.One)
|
new MemorySlotWidget(_, Tier.One),
|
||||||
)
|
)
|
||||||
|
|
||||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||||
@ -102,14 +101,14 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
case _ =>
|
case _ =>
|
||||||
cardSlots = addSlotWidgets(
|
cardSlots = addSlotWidgets(
|
||||||
new CardSlotWidget(_, Tier.Three),
|
new CardSlotWidget(_, Tier.Three),
|
||||||
new CardSlotWidget(_, Tier.Three)
|
new CardSlotWidget(_, Tier.Three),
|
||||||
)
|
)
|
||||||
|
|
||||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
||||||
|
|
||||||
memorySlots = addSlotWidgets(
|
memorySlots = addSlotWidgets(
|
||||||
new MemorySlotWidget(_, Tier.Three),
|
new MemorySlotWidget(_, Tier.Three),
|
||||||
new MemorySlotWidget(_, Tier.Three)
|
new MemorySlotWidget(_, Tier.Three),
|
||||||
)
|
)
|
||||||
|
|
||||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||||
@ -120,17 +119,19 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
// ---------------------------- DefaultSlotItemsFillable ----------------------------
|
// ---------------------------- DefaultSlotItemsFillable ----------------------------
|
||||||
|
|
||||||
override def fillSlotsWithDefaultItems(): Unit = {
|
override def fillSlotsWithDefaultItems(): Unit = {
|
||||||
cardSlots(0).item =
|
cardSlots(0).item = {
|
||||||
if (microcontroller.tier == Tier.Two)
|
if (microcontroller.tier == Tier.Two)
|
||||||
WirelessNetworkCardItem.Tier2.Factory.build()
|
WirelessNetworkCardItem.Tier2.Factory.build()
|
||||||
else
|
else
|
||||||
WirelessNetworkCardItem.Tier1.Factory.build()
|
WirelessNetworkCardItem.Tier1.Factory.build()
|
||||||
|
}
|
||||||
|
|
||||||
cardSlots(1).item =
|
cardSlots(1).item = {
|
||||||
if (microcontroller.tier == Tier.Two)
|
if (microcontroller.tier == Tier.Two)
|
||||||
RedstoneCardItem.Tier2.Factory.build()
|
RedstoneCardItem.Tier2.Factory.build()
|
||||||
else
|
else
|
||||||
RedstoneCardItem.Tier1.Factory.build()
|
RedstoneCardItem.Tier1.Factory.build()
|
||||||
|
}
|
||||||
|
|
||||||
cpuSlot.item = new CpuItem.Factory(Tier.One).build()
|
cpuSlot.item = new CpuItem.Factory(Tier.One).build()
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Entity
|
|||||||
SoundBuffers.NoteBlock(event.instrument),
|
SoundBuffers.NoteBlock(event.instrument),
|
||||||
SoundCategory.Records,
|
SoundCategory.Records,
|
||||||
pitch = math.pow(2f, (event.pitch - 12).toFloat / 12f).toFloat,
|
pitch = math.pow(2f, (event.pitch - 12).toFloat / 12f).toFloat,
|
||||||
volume = event.volume.toFloat.min(1f).max(0f)
|
volume = event.volume.toFloat.min(1f).max(0f),
|
||||||
).play()
|
).play()
|
||||||
|
|
||||||
addParticle(event.pitch)
|
addParticle(event.pitch)
|
||||||
@ -36,7 +36,8 @@ abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Entity
|
|||||||
override def drawParticles(g: Graphics): Unit = synchronized {
|
override def drawParticles(g: Graphics): Unit = synchronized {
|
||||||
for ((time, pitch) <- particles.reverseIterator) {
|
for ((time, pitch) <- particles.reverseIterator) {
|
||||||
val col = ColorScheme("Note" + pitch.min(24).max(0)).withAlpha(1 - (2 * time - 1).min(1).max(0))
|
val col = ColorScheme("Note" + pitch.min(24).max(0)).withAlpha(1 - (2 * time - 1).min(1).max(0))
|
||||||
g.sprite("particles/Note", position + Vector2D(pitch / 24f * 40f + 5, height / 2 - 10 - 100 * time), Size2D(14, 20), col)
|
g.sprite("particles/Note", position + Vector2D(pitch / 24f * 40f + 5, height / 2 - 10 - 100 * time),
|
||||||
|
Size2D(14, 20), col)
|
||||||
}
|
}
|
||||||
particles.mapInPlace { case (t, p) => (t + 1.2f * UiHandler.dt, p) }
|
particles.mapInPlace { case (t, p) => (t + 1.2f * UiHandler.dt, p) }
|
||||||
particles.filterInPlace(_._1 <= 1f)
|
particles.filterInPlace(_._1 <= 1f)
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import ocelot.desktop.util.OcelotInterfaceLogStorage
|
|||||||
import ocelot.desktop.windows.OcelotInterfaceWindow
|
import ocelot.desktop.windows.OcelotInterfaceWindow
|
||||||
|
|
||||||
class OcelotBlockNode(val ocelot: OcelotBlock)
|
class OcelotBlockNode(val ocelot: OcelotBlock)
|
||||||
extends EntityNode(ocelot)
|
extends EntityNode(ocelot)
|
||||||
with LabeledEntityNode
|
with LabeledEntityNode
|
||||||
with OcelotLogParticleNode
|
with OcelotLogParticleNode
|
||||||
with OcelotInterfaceLogStorage
|
with OcelotInterfaceLogStorage
|
||||||
|
|||||||
@ -7,7 +7,8 @@ import ocelot.desktop.graphics.{Graphics, IconSource}
|
|||||||
import ocelot.desktop.node.{EntityNode, LabeledEntityNode, WindowedNode}
|
import ocelot.desktop.node.{EntityNode, LabeledEntityNode, WindowedNode}
|
||||||
import ocelot.desktop.windows.OpenFMRadioWindow
|
import ocelot.desktop.windows.OpenFMRadioWindow
|
||||||
|
|
||||||
class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends EntityNode(openFMRadio) with LabeledEntityNode with WindowedNode[OpenFMRadioWindow] {
|
class OpenFMRadioNode(val openFMRadio: OpenFMRadio)
|
||||||
|
extends EntityNode(openFMRadio) with LabeledEntityNode with WindowedNode[OpenFMRadioWindow] {
|
||||||
override def iconSource: IconSource = IconSource.Nodes.OpenFMRadio
|
override def iconSource: IconSource = IconSource.Nodes.OpenFMRadio
|
||||||
|
|
||||||
private var lastTick = OcelotDesktop.ticker.tick
|
private var lastTick = OcelotDesktop.ticker.tick
|
||||||
@ -32,8 +33,7 @@ class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends EntityNode(openFMRad
|
|||||||
text = rest + first
|
text = rest + first
|
||||||
|
|
||||||
animationIndex += 1
|
animationIndex += 1
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
animationIndex = 0
|
animationIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends EntityNode(openFMRad
|
|||||||
g.text(
|
g.text(
|
||||||
(position.x + size.width / 2) / scale - textWidth / 2,
|
(position.x + size.width / 2) / scale - textWidth / 2,
|
||||||
(position.y + 18) / scale,
|
(position.y + 18) / scale,
|
||||||
text
|
text,
|
||||||
)
|
)
|
||||||
|
|
||||||
g.restore()
|
g.restore()
|
||||||
|
|||||||
@ -17,10 +17,7 @@ import totoro.ocelot.brain.event.NetworkActivityEvent
|
|||||||
import totoro.ocelot.brain.network
|
import totoro.ocelot.brain.network
|
||||||
import totoro.ocelot.brain.util.Direction
|
import totoro.ocelot.brain.util.Direction
|
||||||
|
|
||||||
class RackNode(val rack: Rack)
|
class RackNode(val rack: Rack) extends ComputerAwareNode(rack) with WindowedNode[RackWindow] {
|
||||||
extends ComputerAwareNode(rack)
|
|
||||||
with WindowedNode[RackWindow]
|
|
||||||
{
|
|
||||||
override val iconSource: IconSource = IconSource.Nodes.Rack.Empty
|
override val iconSource: IconSource = IconSource.Nodes.Rack.Empty
|
||||||
override def exposeAddress = false
|
override def exposeAddress = false
|
||||||
|
|
||||||
@ -29,20 +26,21 @@ class RackNode(val rack: Rack)
|
|||||||
NodePort(Some(Direction.Top)),
|
NodePort(Some(Direction.Top)),
|
||||||
NodePort(Some(Direction.Back)),
|
NodePort(Some(Direction.Back)),
|
||||||
NodePort(Some(Direction.Right)),
|
NodePort(Some(Direction.Right)),
|
||||||
NodePort(Some(Direction.Left))
|
NodePort(Some(Direction.Left)),
|
||||||
)
|
)
|
||||||
|
|
||||||
override def getNodeByPort(port: NodePort): network.Node = rack.sidedNode(port.direction.get)
|
override def getNodeByPort(port: NodePort): network.Node = rack.sidedNode(port.direction.get)
|
||||||
|
|
||||||
override def shouldReceiveEventsFor(address: String): Boolean =
|
override def shouldReceiveEventsFor(address: String): Boolean = {
|
||||||
super.shouldReceiveEventsFor(address) ||
|
super.shouldReceiveEventsFor(address) ||
|
||||||
rack.inventory.entities.exists {
|
rack.inventory.entities.exists {
|
||||||
case mountable: RackMountable if mountable.node.address == address => true
|
case mountable: RackMountable if mountable.node.address == address => true
|
||||||
case mountable: RackMountable with ComponentInventory => mountable.inventory.entities.exists {
|
case mountable: RackMountable with ComponentInventory => mountable.inventory.entities.exists {
|
||||||
case environment: Environment => environment.node.address == address
|
case environment: Environment => environment.node.address == address
|
||||||
}
|
}
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
RackNode.addContextMenuEntriesOfMountable(menu, getMountableByClick(event))
|
RackNode.addContextMenuEntriesOfMountable(menu, getMountableByClick(event))
|
||||||
@ -91,9 +89,9 @@ class RackNode(val rack: Rack)
|
|||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
NoHighlightSize,
|
NoHighlightSize,
|
||||||
NoHighlightSize
|
NoHighlightSize,
|
||||||
),
|
),
|
||||||
driveIconSource
|
driveIconSource,
|
||||||
)
|
)
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
@ -131,12 +129,12 @@ class RackNode(val rack: Rack)
|
|||||||
|
|
||||||
val localPosition = Vector2D(
|
val localPosition = Vector2D(
|
||||||
event.mousePos.x - position.x - horizontalMargin,
|
event.mousePos.x - position.x - horizontalMargin,
|
||||||
event.mousePos.y - position.y - verticalMargin
|
event.mousePos.y - position.y - verticalMargin,
|
||||||
)
|
)
|
||||||
|
|
||||||
val m = Size2D(
|
val m = Size2D(
|
||||||
this.width - horizontalMargin * 2,
|
this.width - horizontalMargin * 2,
|
||||||
this.height - verticalMargin * 2
|
this.height - verticalMargin * 2,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checking if click was inside mountables area
|
// Checking if click was inside mountables area
|
||||||
@ -207,7 +205,7 @@ object RackNode {
|
|||||||
"Change floppy"
|
"Change floppy"
|
||||||
else
|
else
|
||||||
"Set floppy",
|
"Set floppy",
|
||||||
IconSource.Save
|
IconSource.Save,
|
||||||
) {
|
) {
|
||||||
diskDriveMountableItem.window.open()
|
diskDriveMountableItem.window.open()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -18,14 +18,13 @@ import totoro.ocelot.brain.util.Tier
|
|||||||
|
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
class RaidNode(val raid: Raid) extends
|
class RaidNode(val raid: Raid)
|
||||||
EntityNode(raid)
|
extends EntityNode(raid)
|
||||||
with SyncedInventory
|
with SyncedInventory
|
||||||
with LabeledEntityNode
|
with LabeledEntityNode
|
||||||
with DiskActivityHandler
|
with DiskActivityHandler
|
||||||
with DefaultSlotItemsFillable
|
with DefaultSlotItemsFillable
|
||||||
with WindowedNode[RaidWindow]
|
with WindowedNode[RaidWindow] {
|
||||||
{
|
|
||||||
var diskSlots: Array[HddSlotWidget] = Array.tabulate(3)(index => new HddSlotWidget(Slot(index), Tier.Three))
|
var diskSlots: Array[HddSlotWidget] = Array.tabulate(3)(index => new HddSlotWidget(Slot(index), Tier.Three))
|
||||||
|
|
||||||
override val iconSource: IconSource = IconSource.Nodes.Raid.Default
|
override val iconSource: IconSource = IconSource.Nodes.Raid.Default
|
||||||
@ -45,7 +44,9 @@ class RaidNode(val raid: Raid) extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disk activity overlay
|
// Disk activity overlay
|
||||||
if (raid.shouldVisualizeDiskActivity && Random.nextDouble() > 0.1 && i == raid.lastDiskAccess % raid.getSizeInventory) {
|
if (
|
||||||
|
raid.shouldVisualizeDiskActivity && Random.nextDouble() > 0.1 && i == raid.lastDiskAccess % raid.getSizeInventory
|
||||||
|
) {
|
||||||
DrawUtils.drawFilesystemActivity(g, x, y, driveIconSource)
|
DrawUtils.drawFilesystemActivity(g, x, y, driveIconSource)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,9 +57,10 @@ class RaidNode(val raid: Raid) extends
|
|||||||
super.shouldReceiveEventsFor(address) || raid.filesystem.exists(_.node.address == address)
|
super.shouldReceiveEventsFor(address) || raid.filesystem.exists(_.node.address == address)
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
DiskItem.addRealPathContextMenuEntries(menu, raid, realPathSetter => {
|
DiskItem.addRealPathContextMenuEntries(menu, raid,
|
||||||
realPathSetter()
|
realPathSetter => {
|
||||||
})
|
realPathSetter()
|
||||||
|
})
|
||||||
|
|
||||||
// TODO: Implement DiskDriveWindow later, because at this moment every
|
// TODO: Implement DiskDriveWindow later, because at this moment every
|
||||||
// TODO: instance of 'Windowed' trait can have only 1 persistable window
|
// TODO: instance of 'Windowed' trait can have only 1 persistable window
|
||||||
|
|||||||
@ -9,10 +9,7 @@ import totoro.ocelot.brain.network
|
|||||||
import totoro.ocelot.brain.util.Direction
|
import totoro.ocelot.brain.util.Direction
|
||||||
|
|
||||||
class RelayNode(val relay: Relay)
|
class RelayNode(val relay: Relay)
|
||||||
extends EntityNode(relay)
|
extends EntityNode(relay) with SyncedInventory with LabeledNode with WindowedNode[RelayWindow] {
|
||||||
with SyncedInventory
|
|
||||||
with LabeledNode
|
|
||||||
with WindowedNode[RelayWindow] {
|
|
||||||
|
|
||||||
override val iconSource: IconSource = IconSource.Nodes.Relay
|
override val iconSource: IconSource = IconSource.Nodes.Relay
|
||||||
|
|
||||||
@ -22,7 +19,8 @@ class RelayNode(val relay: Relay)
|
|||||||
NodePort(Some(Direction.East)),
|
NodePort(Some(Direction.East)),
|
||||||
NodePort(Some(Direction.West)),
|
NodePort(Some(Direction.West)),
|
||||||
NodePort(Some(Direction.Up)),
|
NodePort(Some(Direction.Up)),
|
||||||
NodePort(Some(Direction.Down)))
|
NodePort(Some(Direction.Down)),
|
||||||
|
)
|
||||||
|
|
||||||
override def getNodeByPort(port: NodePort): network.Node = relay.sidedNode(port.direction.get)
|
override def getNodeByPort(port: NodePort): network.Node = relay.sidedNode(port.direction.get)
|
||||||
|
|
||||||
|
|||||||
@ -19,18 +19,14 @@ import totoro.ocelot.brain.nbt.NBTTagCompound
|
|||||||
import totoro.ocelot.brain.util.PackedColor
|
import totoro.ocelot.brain.util.PackedColor
|
||||||
|
|
||||||
class ScreenNode(val screen: Screen)
|
class ScreenNode(val screen: Screen)
|
||||||
extends EntityNode(screen)
|
extends EntityNode(screen) with LabeledEntityNode with WindowedNode[ScreenWindow] with TickUpdatable {
|
||||||
with LabeledEntityNode
|
|
||||||
with WindowedNode[ScreenWindow]
|
|
||||||
with TickUpdatable {
|
|
||||||
|
|
||||||
// NOTE: whenever you access screen's TextBuffer methods, make sure to synchronize on `screen`:
|
// NOTE: whenever you access screen's TextBuffer methods, make sure to synchronize on `screen`:
|
||||||
// computers may update the screen data concurrently via direct methods!
|
// computers may update the screen data concurrently via direct methods!
|
||||||
// conversely, if a property is only modified via indirect methods, no additional synchronization is necessary.
|
// conversely, if a property is only modified via indirect methods, no additional synchronization is necessary.
|
||||||
|
|
||||||
override def minimumSize: Size2D = Size2D(
|
override def minimumSize: Size2D = Size2D(
|
||||||
Size * screen.aspectRatio._1,
|
Size * screen.aspectRatio._1,
|
||||||
Size * screen.aspectRatio._2
|
Size * screen.aspectRatio._2,
|
||||||
)
|
)
|
||||||
|
|
||||||
override def maximumSize: Size2D = minimumSize
|
override def maximumSize: Size2D = minimumSize
|
||||||
@ -170,7 +166,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
startY: Float,
|
startY: Float,
|
||||||
scaleX: Float,
|
scaleX: Float,
|
||||||
scaleY: Float,
|
scaleY: Float,
|
||||||
filteringMode: MinFilteringMode
|
filteringMode: MinFilteringMode,
|
||||||
): Unit = {
|
): Unit = {
|
||||||
g.save()
|
g.save()
|
||||||
g.scale(scaleX, scaleY)
|
g.scale(scaleX, scaleY)
|
||||||
@ -196,7 +192,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
iconColor
|
iconColor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +229,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
position.x + HighlightThickness,
|
position.x + HighlightThickness,
|
||||||
position.y + y * Size,
|
position.y + y * Size,
|
||||||
NoHighlightSize,
|
NoHighlightSize,
|
||||||
Size
|
Size,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,14 +257,15 @@ class ScreenNode(val screen: Screen)
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Middle
|
// Middle
|
||||||
for (x <- 1 until aspectRatioWidth - 1)
|
for (x <- 1 until aspectRatioWidth - 1) {
|
||||||
drawScreenPart(
|
drawScreenPart(
|
||||||
IconSource.Nodes.Screen.RowMiddle,
|
IconSource.Nodes.Screen.RowMiddle,
|
||||||
position.x + x * Size,
|
position.x + x * Size,
|
||||||
position.y + HighlightThickness,
|
position.y + HighlightThickness,
|
||||||
Size,
|
Size,
|
||||||
NoHighlightSize
|
NoHighlightSize,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Right
|
// Right
|
||||||
drawScreenPart(
|
drawScreenPart(
|
||||||
@ -276,7 +273,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
position.x + (aspectRatioWidth - 1) * Size,
|
position.x + (aspectRatioWidth - 1) * Size,
|
||||||
position.y + HighlightThickness,
|
position.y + HighlightThickness,
|
||||||
Size - HighlightThickness,
|
Size - HighlightThickness,
|
||||||
NoHighlightSize
|
NoHighlightSize,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// n x n
|
// n x n
|
||||||
@ -286,7 +283,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
height: Float,
|
height: Float,
|
||||||
leftIcon: IconSource,
|
leftIcon: IconSource,
|
||||||
middleIcon: IconSource,
|
middleIcon: IconSource,
|
||||||
rightIcon: IconSource
|
rightIcon: IconSource,
|
||||||
): Unit = {
|
): Unit = {
|
||||||
// Left
|
// Left
|
||||||
drawScreenPart(
|
drawScreenPart(
|
||||||
@ -294,18 +291,19 @@ class ScreenNode(val screen: Screen)
|
|||||||
position.x + HighlightThickness,
|
position.x + HighlightThickness,
|
||||||
y,
|
y,
|
||||||
Size,
|
Size,
|
||||||
height
|
height,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Middle
|
// Middle
|
||||||
for (x <- 1 until aspectRatioWidth - 1)
|
for (x <- 1 until aspectRatioWidth - 1) {
|
||||||
drawScreenPart(
|
drawScreenPart(
|
||||||
middleIcon,
|
middleIcon,
|
||||||
position.x + x * Size,
|
position.x + x * Size,
|
||||||
y,
|
y,
|
||||||
Size,
|
Size,
|
||||||
height
|
height,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Right
|
// Right
|
||||||
drawScreenPart(
|
drawScreenPart(
|
||||||
@ -313,7 +311,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
position.x + (aspectRatioWidth - 1) * Size - HighlightThickness,
|
position.x + (aspectRatioWidth - 1) * Size - HighlightThickness,
|
||||||
y,
|
y,
|
||||||
Size,
|
Size,
|
||||||
height
|
height,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,18 +321,19 @@ class ScreenNode(val screen: Screen)
|
|||||||
Size - HighlightThickness,
|
Size - HighlightThickness,
|
||||||
IconSource.Nodes.Screen.TopLeft,
|
IconSource.Nodes.Screen.TopLeft,
|
||||||
IconSource.Nodes.Screen.TopMiddle,
|
IconSource.Nodes.Screen.TopMiddle,
|
||||||
IconSource.Nodes.Screen.TopRight
|
IconSource.Nodes.Screen.TopRight,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Middle
|
// Middle
|
||||||
for (y <- 1 until aspectRatioHeight - 1)
|
for (y <- 1 until aspectRatioHeight - 1) {
|
||||||
drawLine(
|
drawLine(
|
||||||
position.y + (y * Size),
|
position.y + (y * Size),
|
||||||
Size,
|
Size,
|
||||||
IconSource.Nodes.Screen.MiddleLeft,
|
IconSource.Nodes.Screen.MiddleLeft,
|
||||||
IconSource.Nodes.Screen.Middle,
|
IconSource.Nodes.Screen.Middle,
|
||||||
IconSource.Nodes.Screen.MiddleRight
|
IconSource.Nodes.Screen.MiddleRight,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Bottom
|
// Bottom
|
||||||
drawLine(
|
drawLine(
|
||||||
@ -342,7 +341,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
Size - HighlightThickness,
|
Size - HighlightThickness,
|
||||||
IconSource.Nodes.Screen.BottomLeft,
|
IconSource.Nodes.Screen.BottomLeft,
|
||||||
IconSource.Nodes.Screen.BottomMiddle,
|
IconSource.Nodes.Screen.BottomMiddle,
|
||||||
IconSource.Nodes.Screen.BottomRight
|
IconSource.Nodes.Screen.BottomRight,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,18 +364,18 @@ class ScreenNode(val screen: Screen)
|
|||||||
virtualScreenBounds.y,
|
virtualScreenBounds.y,
|
||||||
virtualScreenBounds.w,
|
virtualScreenBounds.w,
|
||||||
virtualScreenBounds.h,
|
virtualScreenBounds.h,
|
||||||
Color.Black
|
Color.Black,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Calculating pixel data bounds, so that they fit perfectly into the virtual screen
|
// Calculating pixel data bounds, so that they fit perfectly into the virtual screen
|
||||||
val pixelDataSize = Size2D(
|
val pixelDataSize = Size2D(
|
||||||
screenWidth * FontWidth,
|
screenWidth * FontWidth,
|
||||||
screenHeight * FontHeight
|
screenHeight * FontHeight,
|
||||||
)
|
)
|
||||||
|
|
||||||
val scale = Math.min(
|
val scale = Math.min(
|
||||||
virtualScreenBounds.w / pixelDataSize.width,
|
virtualScreenBounds.w / pixelDataSize.width,
|
||||||
virtualScreenBounds.h / pixelDataSize.height
|
virtualScreenBounds.h / pixelDataSize.height,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Drawing pixel data
|
// Drawing pixel data
|
||||||
@ -386,7 +385,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
virtualScreenBounds.y + virtualScreenBounds.h / 2 - (pixelDataSize.height * scale) / 2,
|
virtualScreenBounds.y + virtualScreenBounds.h / 2 - (pixelDataSize.height * scale) / 2,
|
||||||
scale,
|
scale,
|
||||||
scale,
|
scale,
|
||||||
MinFilteringMode.NearestMipmapNearest
|
MinFilteringMode.NearestMipmapNearest,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Drawing simple overlay otherwise
|
// Drawing simple overlay otherwise
|
||||||
@ -396,7 +395,7 @@ class ScreenNode(val screen: Screen)
|
|||||||
position.x + HighlightThickness,
|
position.x + HighlightThickness,
|
||||||
position.y + HighlightThickness,
|
position.y + HighlightThickness,
|
||||||
NoHighlightSize,
|
NoHighlightSize,
|
||||||
NoHighlightSize
|
NoHighlightSize,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,19 +12,18 @@ import totoro.ocelot.brain.entity.{TapeDrive, TapeDriveState}
|
|||||||
import totoro.ocelot.brain.event.{TapeDriveAudioEvent, TapeDriveStopEvent}
|
import totoro.ocelot.brain.event.{TapeDriveAudioEvent, TapeDriveStopEvent}
|
||||||
|
|
||||||
class TapeDriveNode(val tapeDrive: TapeDrive)
|
class TapeDriveNode(val tapeDrive: TapeDrive)
|
||||||
extends EntityNode(tapeDrive)
|
extends EntityNode(tapeDrive)
|
||||||
with SyncedInventory
|
with SyncedInventory
|
||||||
with LabeledEntityNode
|
with LabeledEntityNode
|
||||||
with PositionalSoundSourcesNode
|
with PositionalSoundSourcesNode
|
||||||
with WindowedNode[TapeDriveWindow]
|
with WindowedNode[TapeDriveWindow] {
|
||||||
{
|
|
||||||
|
|
||||||
override def iconSource: IconSource = IconSource.Nodes.TapeDrive
|
override def iconSource: IconSource = IconSource.Nodes.TapeDrive
|
||||||
|
|
||||||
private lazy val soundTapeRewind: SoundSource = SoundSource.fromBuffer(
|
private lazy val soundTapeRewind: SoundSource = SoundSource.fromBuffer(
|
||||||
SoundBuffers.MachineTapeRewind,
|
SoundBuffers.MachineTapeRewind,
|
||||||
SoundCategory.Records,
|
SoundCategory.Records,
|
||||||
looping = true
|
looping = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
private lazy val streams: (SoundStream, SoundSource) = Audio.newStream(SoundCategory.Records)
|
private lazy val streams: (SoundStream, SoundSource) = Audio.newStream(SoundCategory.Records)
|
||||||
@ -53,7 +52,8 @@ class TapeDriveNode(val tapeDrive: TapeDrive)
|
|||||||
override def update(): Unit = {
|
override def update(): Unit = {
|
||||||
super.update()
|
super.update()
|
||||||
|
|
||||||
val isRewinding = tapeDrive.state.state == TapeDriveState.State.Rewinding || tapeDrive.state.state == TapeDriveState.State.Forwarding
|
val isRewinding =
|
||||||
|
tapeDrive.state.state == TapeDriveState.State.Rewinding || tapeDrive.state.state == TapeDriveState.State.Forwarding
|
||||||
|
|
||||||
if (!isRewinding && soundTapeRewind.isPlaying) {
|
if (!isRewinding && soundTapeRewind.isPlaying) {
|
||||||
soundTapeRewind.stop()
|
soundTapeRewind.stop()
|
||||||
@ -78,7 +78,6 @@ class TapeDriveNode(val tapeDrive: TapeDrive)
|
|||||||
|
|
||||||
override def brainInventory: TapeDrive = tapeDrive
|
override def brainInventory: TapeDrive = tapeDrive
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------- Windowed --------------------------------
|
// -------------------------------- Windowed --------------------------------
|
||||||
|
|
||||||
override protected def createWindow(): TapeDriveWindow = new TapeDriveWindow(this)
|
override protected def createWindow(): TapeDriveWindow = new TapeDriveWindow(this)
|
||||||
|
|||||||
@ -76,12 +76,14 @@ object UiHandler extends Logging {
|
|||||||
|
|
||||||
private val _clipboard = Toolkit.getDefaultToolkit.getSystemClipboard
|
private val _clipboard = Toolkit.getDefaultToolkit.getSystemClipboard
|
||||||
|
|
||||||
def clipboard: String = try {
|
def clipboard: String = {
|
||||||
_clipboard.getData(DataFlavor.stringFlavor).toString
|
try {
|
||||||
} catch {
|
_clipboard.getData(DataFlavor.stringFlavor).toString
|
||||||
case _: UnsupportedFlavorException =>
|
} catch {
|
||||||
logger.debug("Trying to paste non-Unicode content from clipboard! Replaced with empty string.")
|
case _: UnsupportedFlavorException =>
|
||||||
""
|
logger.debug("Trying to paste non-Unicode content from clipboard! Replaced with empty string.")
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def clipboard_=(value: String): Unit = {
|
def clipboard_=(value: String): Unit = {
|
||||||
@ -155,11 +157,12 @@ object UiHandler extends Logging {
|
|||||||
|
|
||||||
private def windowGeometry: Rect2D = new Rect2D(Display.getX, Display.getY, Display.getWidth, Display.getHeight)
|
private def windowGeometry: Rect2D = new Rect2D(Display.getX, Display.getY, Display.getWidth, Display.getHeight)
|
||||||
|
|
||||||
private def sanitizeWindowSize(size: Size2D): Size2D =
|
private def sanitizeWindowSize(size: Size2D): Size2D = {
|
||||||
Size2D(
|
Size2D(
|
||||||
if (size.width < 10) 800 else size.width,
|
if (size.width < 10) 800 else size.width,
|
||||||
if (size.height < 10) 600 else size.height,
|
if (size.height < 10) 600 else size.height,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private def sanitizeWindowGeometry(currentGeometry: Rect2D): Rect2D = {
|
private def sanitizeWindowGeometry(currentGeometry: Rect2D): Rect2D = {
|
||||||
val Rect2D(x, y, w, h) = currentGeometry
|
val Rect2D(x, y, w, h) = currentGeometry
|
||||||
@ -243,21 +246,22 @@ object UiHandler extends Logging {
|
|||||||
val arch = System.getProperty("os.arch")
|
val arch = System.getProperty("os.arch")
|
||||||
val is64bit = arch.startsWith("amd64")
|
val is64bit = arch.startsWith("amd64")
|
||||||
|
|
||||||
val libs =
|
val libs = {
|
||||||
if (SystemUtils.IS_OS_WINDOWS)
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
if (is64bit)
|
if (is64bit)
|
||||||
Array("jinput-dx8_64.dll", "jinput-raw_64.dll", "jinput-wintab.dll", "lwjgl64.dll", "OpenAL64.dll")
|
Array("jinput-dx8_64.dll", "jinput-raw_64.dll", "jinput-wintab.dll", "lwjgl64.dll", "OpenAL64.dll")
|
||||||
else
|
else
|
||||||
Array("jinput-dx8.dll", "jinput-raw.dll", "jinput-wintab.dll", "lwjgl.dll", "OpenAL32.dll")
|
Array("jinput-dx8.dll", "jinput-raw.dll", "jinput-wintab.dll", "lwjgl.dll", "OpenAL32.dll")
|
||||||
else if (SystemUtils.IS_OS_MAC_OSX)
|
} else if (SystemUtils.IS_OS_MAC_OSX)
|
||||||
Array("liblwjgl.dylib")
|
Array("liblwjgl.dylib")
|
||||||
else if (SystemUtils.IS_OS_LINUX)
|
else if (SystemUtils.IS_OS_LINUX) {
|
||||||
if (is64bit)
|
if (is64bit)
|
||||||
Array("libjinput-linux64.so", "liblwjgl64.so", "libopenal64.so")
|
Array("libjinput-linux64.so", "liblwjgl64.so", "libopenal64.so")
|
||||||
else
|
else
|
||||||
Array("libjinput-linux.so", "liblwjgl.so", "libopenal.so")
|
Array("libjinput-linux.so", "liblwjgl.so", "libopenal.so")
|
||||||
else
|
} else
|
||||||
throw new Exception("Unsupported OS")
|
throw new Exception("Unsupported OS")
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug(s"Unpacking native libraries to: $librariesPath")
|
logger.debug(s"Unpacking native libraries to: $librariesPath")
|
||||||
|
|
||||||
@ -329,13 +333,14 @@ object UiHandler extends Logging {
|
|||||||
UiThreadTasks.add(updateWindowTitle)
|
UiThreadTasks.add(updateWindowTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def updateWindowTitle(): Unit =
|
private def updateWindowTitle(): Unit = {
|
||||||
Display.setTitle(
|
Display.setTitle(
|
||||||
if (_windowTitleSuffix.isEmpty)
|
if (_windowTitleSuffix.isEmpty)
|
||||||
windowTitleBase
|
windowTitleBase
|
||||||
else
|
else
|
||||||
s"$windowTitleBase - ${_windowTitleSuffix.get}"
|
s"$windowTitleBase - ${_windowTitleSuffix.get}"
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private var exitRequested = false
|
private var exitRequested = false
|
||||||
|
|
||||||
@ -441,7 +446,7 @@ object UiHandler extends Logging {
|
|||||||
|
|
||||||
ScrollEvents.events.foreach(dispatchEvent(scrollTarget))
|
ScrollEvents.events.foreach(dispatchEvent(scrollTarget))
|
||||||
|
|
||||||
for (event <- MouseEvents.events)
|
for (event <- MouseEvents.events) {
|
||||||
if (event.state == MouseEvent.State.Press) {
|
if (event.state == MouseEvent.State.Press) {
|
||||||
dispatchEvent(mouseTarget)(event)
|
dispatchEvent(mouseTarget)(event)
|
||||||
|
|
||||||
@ -452,6 +457,7 @@ object UiHandler extends Logging {
|
|||||||
} else {
|
} else {
|
||||||
dispatchEvent(hierarchy.reverseIterator)(event)
|
dispatchEvent(hierarchy.reverseIterator)(event)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hierarchy.reverseIterator.foreach {
|
hierarchy.reverseIterator.foreach {
|
||||||
case handler: HoverHandler if !mouseTarget.contains(handler) => handler.setHovered(false)
|
case handler: HoverHandler if !mouseTarget.contains(handler) => handler.setHovered(false)
|
||||||
|
|||||||
@ -3,14 +3,12 @@ package ocelot.desktop.ui.event
|
|||||||
import ocelot.desktop.geometry.Vector2D
|
import ocelot.desktop.geometry.Vector2D
|
||||||
|
|
||||||
object DragEvent {
|
object DragEvent {
|
||||||
|
|
||||||
object State extends Enumeration {
|
object State extends Enumeration {
|
||||||
val Start, Drag, Stop = Value
|
val Start, Drag, Stop = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(state: DragEvent.State.Value, button: MouseEvent.Button.Value, mousePos: Vector2D,
|
def apply(state: DragEvent.State.Value, button: MouseEvent.Button.Value, mousePos: Vector2D,
|
||||||
start: Vector2D, delta: Vector2D): DragEvent =
|
start: Vector2D, delta: Vector2D): DragEvent = {
|
||||||
{
|
|
||||||
val ev = DragEvent(state, button, mousePos)
|
val ev = DragEvent(state, button, mousePos)
|
||||||
ev._start = start
|
ev._start = start
|
||||||
ev._delta = delta
|
ev._delta = delta
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
package ocelot.desktop.ui.event
|
package ocelot.desktop.ui.event
|
||||||
|
|
||||||
object HoverEvent {
|
object HoverEvent {
|
||||||
|
|
||||||
object State extends Enumeration {
|
object State extends Enumeration {
|
||||||
val Enter, Leave = Value
|
val Enter, Leave = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class HoverEvent(state: HoverEvent.State.Value) extends Event
|
case class HoverEvent(state: HoverEvent.State.Value) extends Event
|
||||||
@ -1,8 +1,6 @@
|
|||||||
package ocelot.desktop.ui.event
|
package ocelot.desktop.ui.event
|
||||||
|
|
||||||
|
|
||||||
object MouseEvent {
|
object MouseEvent {
|
||||||
|
|
||||||
object State extends Enumeration {
|
object State extends Enumeration {
|
||||||
val Press, Release = Value
|
val Press, Release = Value
|
||||||
}
|
}
|
||||||
@ -12,7 +10,6 @@ object MouseEvent {
|
|||||||
val Right: Button.Value = Value(1)
|
val Right: Button.Value = Value(1)
|
||||||
val Middle: Button.Value = Value(2)
|
val Middle: Button.Value = Value(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class MouseEvent(state: MouseEvent.State.Value, button: MouseEvent.Button.Value) extends Event
|
case class MouseEvent(state: MouseEvent.State.Value, button: MouseEvent.Button.Value) extends Event
|
||||||
|
|||||||
@ -10,13 +10,14 @@ import scala.util.Random
|
|||||||
trait DiskActivityHandler extends EventAware {
|
trait DiskActivityHandler extends EventAware {
|
||||||
eventHandlers += {
|
eventHandlers += {
|
||||||
case BrainEvent(event: FileSystemActivityEvent) if !Audio.isDisabled =>
|
case BrainEvent(event: FileSystemActivityEvent) if !Audio.isDisabled =>
|
||||||
val sound =
|
val sound = {
|
||||||
if (event.activityType == Floppy)
|
if (event.activityType == Floppy)
|
||||||
SoundBuffers.MachineFloppyAccess
|
SoundBuffers.MachineFloppyAccess
|
||||||
.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
||||||
else
|
else
|
||||||
SoundBuffers.MachineHDDAccess
|
SoundBuffers.MachineHDDAccess
|
||||||
.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
||||||
|
}
|
||||||
|
|
||||||
sound(Random.between(0, sound.length)).play()
|
sound(Random.between(0, sound.length)).play()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,7 @@ trait MouseHandler extends Widget {
|
|||||||
|
|
||||||
protected def receiveDragEvents: Boolean = false
|
protected def receiveDragEvents: Boolean = false
|
||||||
|
|
||||||
/**
|
/** If `true`, a [[ClickEvent]] will be registered even if the mouse button is released
|
||||||
* If `true`, a [[ClickEvent]] will be registered even if the mouse button is released
|
|
||||||
* outside the tolerance threshold, as long as it stays within the widget's bounds.
|
* outside the tolerance threshold, as long as it stays within the widget's bounds.
|
||||||
*/
|
*/
|
||||||
protected def allowClickReleaseOutsideThreshold: Boolean = !receiveDragEvents
|
protected def allowClickReleaseOutsideThreshold: Boolean = !receiveDragEvents
|
||||||
@ -35,14 +34,14 @@ trait MouseHandler extends Widget {
|
|||||||
val dragStopped = receiveDragEvents && dragButtons.remove(button)
|
val dragStopped = receiveDragEvents && dragButtons.remove(button)
|
||||||
val clicked = (
|
val clicked = (
|
||||||
receiveClickEvents
|
receiveClickEvents
|
||||||
&& !dragStopped
|
&& !dragStopped
|
||||||
&& startPositions.get(button).exists(p => {
|
&& startPositions.get(button).exists(p => {
|
||||||
if (allowClickReleaseOutsideThreshold) {
|
if (allowClickReleaseOutsideThreshold) {
|
||||||
clippedBounds.contains(mousePos)
|
clippedBounds.contains(mousePos)
|
||||||
} else {
|
} else {
|
||||||
(p - mousePos).lengthSquared < Tolerance * Tolerance
|
(p - mousePos).lengthSquared < Tolerance * Tolerance
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if (dragStopped) {
|
if (dragStopped) {
|
||||||
@ -73,16 +72,18 @@ trait MouseHandler extends Widget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dragButtons.foreach(
|
dragButtons.foreach(button => {
|
||||||
button => {
|
handleEvent(
|
||||||
handleEvent(
|
DragEvent(
|
||||||
DragEvent(
|
DragEvent.State.Drag,
|
||||||
DragEvent.State.Drag, button, mousePos, startPositions(button),
|
button,
|
||||||
mousePos - prevPositions(button)
|
mousePos,
|
||||||
)
|
startPositions(button),
|
||||||
|
mousePos - prevPositions(button),
|
||||||
)
|
)
|
||||||
prevPositions(button) = mousePos
|
)
|
||||||
}
|
prevPositions(button) = mousePos
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,8 @@ class LinearLayout(widget: Widget,
|
|||||||
orientation: Orientation.Value = Orientation.Horizontal,
|
orientation: Orientation.Value = Orientation.Horizontal,
|
||||||
justifyContent: JustifyContent.Value = JustifyContent.Start,
|
justifyContent: JustifyContent.Value = JustifyContent.Start,
|
||||||
alignItems: AlignItems.Value = AlignItems.Stretch,
|
alignItems: AlignItems.Value = AlignItems.Stretch,
|
||||||
gap: Float = 0f) extends Layout(widget)
|
gap: Float = 0f)
|
||||||
{
|
extends Layout(widget) {
|
||||||
override def recalculateBounds(): Unit = {
|
override def recalculateBounds(): Unit = {
|
||||||
super.recalculateBounds()
|
super.recalculateBounds()
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ class LinearLayout(widget: Widget,
|
|||||||
|
|
||||||
(orientation, alignItems) match {
|
(orientation, alignItems) match {
|
||||||
case (Orientation.Vertical, AlignItems.Start)
|
case (Orientation.Vertical, AlignItems.Start)
|
||||||
| (Orientation.Vertical, AlignItems.Stretch) =>
|
| (Orientation.Vertical, AlignItems.Stretch) =>
|
||||||
child.rawSetPosition(child.position.setX(widget.position.x))
|
child.rawSetPosition(child.position.setX(widget.position.x))
|
||||||
case (Orientation.Vertical, AlignItems.End) =>
|
case (Orientation.Vertical, AlignItems.End) =>
|
||||||
child.rawSetPosition(child.position.setX(widget.position.x + widget.size.width - child.size.width))
|
child.rawSetPosition(child.position.setX(widget.position.x + widget.size.width - child.size.width))
|
||||||
@ -148,7 +148,7 @@ class LinearLayout(widget: Widget,
|
|||||||
child.rawSetPosition(child.position.setX(widget.position.x + widget.size.width / 2 - child.size.width / 2))
|
child.rawSetPosition(child.position.setX(widget.position.x + widget.size.width / 2 - child.size.width / 2))
|
||||||
|
|
||||||
case (Orientation.Horizontal, AlignItems.Start)
|
case (Orientation.Horizontal, AlignItems.Start)
|
||||||
| (Orientation.Horizontal, AlignItems.Stretch) =>
|
| (Orientation.Horizontal, AlignItems.Stretch) =>
|
||||||
child.rawSetPosition(child.position.setY(widget.position.y))
|
child.rawSetPosition(child.position.setY(widget.position.y))
|
||||||
case (Orientation.Horizontal, AlignItems.End) =>
|
case (Orientation.Horizontal, AlignItems.End) =>
|
||||||
child.rawSetPosition(child.position.setY(widget.position.y + widget.size.height - child.size.height))
|
child.rawSetPosition(child.position.setY(widget.position.y + widget.size.height - child.size.height))
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user