mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-19 18:49:19 +01:00
Add scalafmt
This commit is contained in:
parent
edd9916905
commit
ee4f2dcc3b
@ -29,6 +29,13 @@ test:
|
||||
script:
|
||||
- sbt test
|
||||
|
||||
scalafmt:
|
||||
stage: test
|
||||
before_script:
|
||||
- sbt -v sbtVersion
|
||||
script:
|
||||
- sbt scalafmtCheckAll
|
||||
|
||||
build:
|
||||
stage: build
|
||||
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
|
||||
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 color = if (value.length == 9) {
|
||||
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
|
||||
entries.addOne((key, color))
|
||||
}
|
||||
|
||||
@ -30,11 +30,8 @@ import scala.io.Source
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.{Failure, Success, Try, Using}
|
||||
|
||||
object OcelotDesktop
|
||||
// This configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
|
||||
extends LoggingConfiguration
|
||||
with Logging
|
||||
{
|
||||
// LoggingConfiguration configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
|
||||
object OcelotDesktop extends LoggingConfiguration with Logging {
|
||||
System.setProperty("awt.useSystemAAFontSettings", "on")
|
||||
System.setProperty("swing.aatext", "true")
|
||||
System.setProperty("LWJGL_WM_CLASS", "Ocelot Desktop")
|
||||
@ -70,11 +67,10 @@ object OcelotDesktop
|
||||
splashScreen.setStatus("Loading configuration...", 0.10f)
|
||||
val customConfigPath = args.get(CommandLine.ConfigPath).flatten
|
||||
|
||||
val desktopConfigPath: Path =
|
||||
val desktopConfigPath: Path = {
|
||||
if (customConfigPath.isDefined) {
|
||||
Paths.get(customConfigPath.get)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// 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!
|
||||
// OcelotPaths.desktopConfig
|
||||
@ -83,23 +79,24 @@ object OcelotDesktop
|
||||
|
||||
try {
|
||||
if (!Files.exists(newConfigPath)) {
|
||||
val oldConfigPath =
|
||||
val oldConfigPath = {
|
||||
if (SystemUtils.IS_OS_WINDOWS)
|
||||
Paths.get(OcelotPaths.windowsAppDataDirectoryName, "Ocelot", "ocelot.conf")
|
||||
else
|
||||
Paths.get(OcelotPaths.linuxHomeDirectoryName, ".config", "ocelot", "ocelot.conf")
|
||||
}
|
||||
|
||||
if (Files.exists(oldConfigPath))
|
||||
Files.move(oldConfigPath, newConfigPath)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case _: Throwable =>
|
||||
}
|
||||
|
||||
// TODO: end of upper todo <3
|
||||
newConfigPath
|
||||
}
|
||||
}
|
||||
|
||||
Settings.load(desktopConfigPath)
|
||||
|
||||
@ -157,24 +154,28 @@ object OcelotDesktop
|
||||
}
|
||||
}
|
||||
|
||||
val updateThread = new Thread(() => try {
|
||||
val currentThread = Thread.currentThread()
|
||||
val updateThread = new Thread(
|
||||
() =>
|
||||
try {
|
||||
val currentThread = Thread.currentThread()
|
||||
|
||||
while (!currentThread.isInterrupted) {
|
||||
Profiler.measure("tick") {
|
||||
withTickLockAcquired {
|
||||
workspace.update()
|
||||
updateThreadTasks.run()
|
||||
while (!currentThread.isInterrupted) {
|
||||
Profiler.measure("tick") {
|
||||
withTickLockAcquired {
|
||||
workspace.update()
|
||||
updateThreadTasks.run()
|
||||
|
||||
tpsCounter.tick()
|
||||
tpsCounter.tick()
|
||||
}
|
||||
}
|
||||
|
||||
ticker.waitNext()
|
||||
}
|
||||
}
|
||||
|
||||
ticker.waitNext()
|
||||
}
|
||||
} catch {
|
||||
case _: InterruptedException => // ignore
|
||||
}, "update-thread")
|
||||
} catch {
|
||||
case _: InterruptedException => // ignore
|
||||
},
|
||||
"update-thread",
|
||||
)
|
||||
|
||||
updateThread.start()
|
||||
splashScreen.dispose()
|
||||
@ -184,7 +185,8 @@ object OcelotDesktop
|
||||
logger.info("Cleaning up")
|
||||
updateThread.interrupt()
|
||||
|
||||
try updateThread.join() catch {
|
||||
try updateThread.join()
|
||||
catch {
|
||||
case _: InterruptedException =>
|
||||
}
|
||||
|
||||
@ -234,7 +236,9 @@ object OcelotDesktop
|
||||
root.workspaceView.load(frontendNBT)
|
||||
if (frontendNBT.hasKey("players")) {
|
||||
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
|
||||
|
||||
if (oldPath != outputPath) {
|
||||
val (oldFiles, newFiles) =
|
||||
val (oldFiles, newFiles) = {
|
||||
Using.resources(Files.list(oldPath), Files.list(outputPath)) { (oldDirStream, newDirStream) =>
|
||||
val oldFiles = oldDirStream.iterator.asScala.toArray
|
||||
val newFiles = newDirStream.iterator.asScala.toArray
|
||||
(oldFiles, newFiles)
|
||||
}
|
||||
}
|
||||
|
||||
val toRemove = newFiles.intersect(oldFiles)
|
||||
|
||||
@ -311,8 +316,7 @@ object OcelotDesktop
|
||||
|
||||
if (Files.isDirectory(path)) {
|
||||
FileUtils.copyDirectory(oldFile, newFile)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
FileUtils.copyFile(oldFile, newFile)
|
||||
}
|
||||
}
|
||||
@ -355,11 +359,12 @@ object OcelotDesktop
|
||||
|
||||
def saveAs(): Unit = showSaveDialog()
|
||||
|
||||
def showOpenDialog(): Unit =
|
||||
def showOpenDialog(): Unit = {
|
||||
showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) {
|
||||
case Some(dir) => load(dir)
|
||||
case None => Success(())
|
||||
}
|
||||
}
|
||||
|
||||
def load(dir: File): Try[Unit] = {
|
||||
val path = Paths.get(dir.getCanonicalPath, "workspace.nbt")
|
||||
@ -410,7 +415,7 @@ object OcelotDesktop
|
||||
val result = f(selectedFile)
|
||||
|
||||
result match {
|
||||
case f@Failure(_) => showFailureMessage(f)
|
||||
case f @ Failure(_) => showFailureMessage(f)
|
||||
case Success(_) =>
|
||||
}
|
||||
})
|
||||
@ -424,13 +429,13 @@ object OcelotDesktop
|
||||
|
||||
new NotificationDialog(
|
||||
s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.",
|
||||
NotificationType.Error
|
||||
NotificationType.Error,
|
||||
).addCloseButton().show()
|
||||
}
|
||||
|
||||
def showAddPlayerDialog(): Unit = new InputDialog(
|
||||
"Add new player",
|
||||
text => OcelotDesktop.selectPlayer(text)
|
||||
text => OcelotDesktop.selectPlayer(text),
|
||||
).show()
|
||||
|
||||
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 = {
|
||||
val nonEmpty = try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty) catch {
|
||||
case _: FileNotFoundException | _: NoSuchFileException =>
|
||||
logger.info(s"Save path $path does not exist: creating a new directory")
|
||||
Files.createDirectory(path)
|
||||
val nonEmpty = {
|
||||
try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty)
|
||||
catch {
|
||||
case _: FileNotFoundException | _: NoSuchFileException =>
|
||||
logger.info(s"Save path $path does not exist: creating a new directory")
|
||||
Files.createDirectory(path)
|
||||
|
||||
false
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
if (nonEmpty) {
|
||||
@ -478,7 +486,7 @@ object OcelotDesktop
|
||||
|Files in the save directory will be included in the workspace.
|
||||
|They may be overwritten, causing loss of data.
|
||||
|Proceed with saving anyway?""".stripMargin,
|
||||
NotificationType.Warning
|
||||
NotificationType.Warning,
|
||||
) {
|
||||
addButton("Cancel") {
|
||||
close()
|
||||
@ -491,7 +499,7 @@ object OcelotDesktop
|
||||
} else continuation
|
||||
}
|
||||
|
||||
private def showSaveDialog(continuation: => Unit): Unit =
|
||||
private def showSaveDialog(continuation: => Unit): Unit = {
|
||||
showFileChooserDialog(JFileChooser.SAVE_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
||||
Try {
|
||||
if (dir.nonEmpty) {
|
||||
@ -512,6 +520,7 @@ object OcelotDesktop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def showCloseConfirmationDialog(prompt: Option[String])(continuation: => Unit): Unit = {
|
||||
if (UiHandler.root.modalDialogPool.children.exists(_.isInstanceOf[CloseConfirmationDialog])) {
|
||||
|
||||
@ -81,19 +81,23 @@ object Settings extends Logging {
|
||||
if (config.hasPath(path)) config.getDouble(path)
|
||||
else default
|
||||
|
||||
def withValuePreserveOrigin(path: String, value: Any): Config =
|
||||
config.withValue(path,
|
||||
def withValuePreserveOrigin(path: String, value: Any): Config = {
|
||||
config.withValue(
|
||||
path,
|
||||
if (config.hasPath(path))
|
||||
ConfigValueFactory.fromAnyRef(value).withOrigin(config.getValue(path).origin())
|
||||
else ConfigValueFactory.fromAnyRef(value)
|
||||
else ConfigValueFactory.fromAnyRef(value),
|
||||
)
|
||||
}
|
||||
|
||||
def withValuePreserveOrigin(path: String, value: Option[Any]): Config =
|
||||
config.withValue(path,
|
||||
def withValuePreserveOrigin(path: String, value: Option[Any]): Config = {
|
||||
config.withValue(
|
||||
path,
|
||||
if (config.hasPath(path))
|
||||
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 = {
|
||||
config.withValue(path, ConfigValueFactory.fromIterable(util.Arrays.asList(value.x, value.y)))
|
||||
@ -107,6 +111,7 @@ object Settings extends Logging {
|
||||
var isSet: Boolean = false
|
||||
|
||||
def this() = this(0, 0)
|
||||
|
||||
def this(list: util.List[Integer]) = {
|
||||
this()
|
||||
if (list.size() == 2) {
|
||||
@ -143,12 +148,10 @@ object Settings extends Logging {
|
||||
logger.info(s"Loaded Ocelot Desktop configuration from: $path")
|
||||
|
||||
return
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case _: Throwable =>
|
||||
logger.info(s"Failed to parse $path, using default Ocelot Desktop configuration.")
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
if (stream != null)
|
||||
stream.close()
|
||||
}
|
||||
|
||||
@ -15,9 +15,7 @@ object Audio extends Logging {
|
||||
private val sources = new mutable.HashMap[SoundSource, Int]
|
||||
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 = {
|
||||
try {
|
||||
AL.create()
|
||||
@ -39,7 +37,7 @@ object Audio extends Logging {
|
||||
def newStream(
|
||||
soundCategory: SoundCategory.Value,
|
||||
pitch: Float = 1f,
|
||||
volume: Float = 1f
|
||||
volume: Float = 1f,
|
||||
): (SoundStream, SoundSource) = {
|
||||
var source: SoundSource = null
|
||||
|
||||
@ -188,7 +186,7 @@ object Audio extends Logging {
|
||||
AL10W.alSourcef(
|
||||
sourceId,
|
||||
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
|
||||
offset += step
|
||||
if (offset > 1) offset -= 1
|
||||
data.put((value & 0xFF).toByte)
|
||||
data.put(((value >> 8) & 0xFF).toByte)
|
||||
data.put((value & 0xff).toByte)
|
||||
data.put(((value >> 8) & 0xff).toByte)
|
||||
}
|
||||
if (data.hasRemaining) {
|
||||
for (_ <- 0 until pauseSampleCount) {
|
||||
|
||||
@ -8,7 +8,8 @@ import java.nio.ByteBuffer
|
||||
|
||||
object OggDecoder {
|
||||
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 channels = stream.getIdentificationHeader.getChannels
|
||||
|
||||
@ -3,12 +3,10 @@ package ocelot.desktop.audio
|
||||
import scala.util.control
|
||||
import scala.util.control.Exception.Catch
|
||||
|
||||
case class OpenAlException(func: String, errName: String, code: Int)
|
||||
extends Exception(s"OpenAL error: $func: $errName")
|
||||
case class OpenAlException(func: String, errName: String, code: Int) extends Exception(s"OpenAL error: $func: $errName")
|
||||
|
||||
object OpenAlException {
|
||||
def defaulting[T](default: => T): Catch[T] = control.Exception.failAsValue(classOf[OpenAlException])(default)
|
||||
|
||||
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 bits = AL10W.alGetBufferi(bufferId, AL10.AL_BITS)
|
||||
|
||||
sizeBytes * 8 / channels / bits
|
||||
}
|
||||
sizeBytes * 8 / channels / bits
|
||||
}
|
||||
|
||||
case None => 0
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object SoundBuffers extends Resource {
|
||||
lazy val MachineComputerRunning: SoundBuffer = load("/ocelot/desktop/sounds/machine/computer_running.ogg")
|
||||
|
||||
lazy val MachineFloppyAccess: Array[SoundBuffer] = Array(
|
||||
load("/ocelot/desktop/sounds/machine/floppy_access1.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_access6.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 MachineHDDAccess: Array[SoundBuffer] = Array(
|
||||
load("/ocelot/desktop/sounds/machine/hdd_access1.ogg"),
|
||||
load("/ocelot/desktop/sounds/machine/hdd_access2.ogg"),
|
||||
@ -43,7 +46,7 @@ object SoundBuffers extends Resource {
|
||||
|
||||
lazy val NoteBlock: Map[String, SoundBuffer] = List(
|
||||
"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 => {
|
||||
(name, load(s"/ocelot/desktop/sounds/minecraft/note_block/$name.ogg"))
|
||||
}).toMap
|
||||
|
||||
@ -11,7 +11,7 @@ class SoundSource(
|
||||
val looping: Boolean,
|
||||
val pitch: Float,
|
||||
var volume: Float,
|
||||
var position: Vector3D = Vector3D(0, 0, 0)
|
||||
var position: Vector3D = Vector3D(0, 0, 0),
|
||||
) {
|
||||
def duration: Option[Duration] = kind match {
|
||||
case SoundSource.KindSoundBuffer(buffer) =>
|
||||
@ -124,8 +124,12 @@ object SoundSource {
|
||||
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 MachineFloppyEject: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, SoundCategory.Environment)
|
||||
}
|
||||
lazy val MachineFloppyInsert: SoundSource =
|
||||
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 = {
|
||||
RGBAColor(
|
||||
(color >> 16).toShort,
|
||||
((color >> 8) & 0xFF).toShort,
|
||||
(color & 0xFF).toShort,
|
||||
((color >> 8) & 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,
|
||||
g.toFloat / 255f,
|
||||
b.toFloat / 255f,
|
||||
a.toFloat / 255f
|
||||
a.toFloat / 255f,
|
||||
)
|
||||
|
||||
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))
|
||||
|
||||
final private def componentToLinear(x: Float): Float = {
|
||||
private final def componentToLinear(x: Float): Float = {
|
||||
if (x <= 0.0404482362771082)
|
||||
x / 12.92f
|
||||
else
|
||||
|
||||
@ -43,7 +43,7 @@ class Camera extends Entity with GenericCamera with DeviceInfo {
|
||||
DeviceAttribute.Class -> DeviceClass.Multimedia,
|
||||
DeviceAttribute.Description -> "Dungeon Scanner 2.5D",
|
||||
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 = {
|
||||
|
||||
@ -19,11 +19,12 @@ import javax.sound.sampled.AudioFormat.Encoding
|
||||
import javax.sound.sampled.{AudioFormat, AudioSystem}
|
||||
|
||||
class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
||||
override val node: Component =
|
||||
override val node: Component = {
|
||||
Network
|
||||
.newNode(this, Visibility.Network)
|
||||
.withComponent("openfm_radio", Visibility.Network)
|
||||
.create()
|
||||
.newNode(this, Visibility.Network)
|
||||
.withComponent("openfm_radio", Visibility.Network)
|
||||
.create()
|
||||
}
|
||||
|
||||
// --------------------------- URL ---------------------------
|
||||
|
||||
@ -44,14 +45,18 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
||||
private def playSynchronously(): Unit = {
|
||||
try {
|
||||
// Trying to parse URL and sending request to host
|
||||
val connection =
|
||||
val connection = {
|
||||
new URI(url.get)
|
||||
.toURL
|
||||
.openConnection
|
||||
.asInstanceOf[HttpURLConnection]
|
||||
.toURL
|
||||
.openConnection
|
||||
.asInstanceOf[HttpURLConnection]
|
||||
}
|
||||
|
||||
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.setDoInput(true)
|
||||
connection.setDoOutput(true)
|
||||
@ -65,21 +70,22 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
||||
2,
|
||||
4,
|
||||
44100,
|
||||
false
|
||||
false,
|
||||
),
|
||||
// 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
|
||||
val inputStreamFormat = inputStream.getFormat
|
||||
val inputStreamSampleRate = inputStreamFormat.getSampleRate.toInt
|
||||
|
||||
val inputStreamSoundSampleFormat =
|
||||
val inputStreamSoundSampleFormat = {
|
||||
if (inputStreamFormat.getChannels > 1)
|
||||
Format.Stereo16
|
||||
else
|
||||
Format.Mono16
|
||||
}
|
||||
|
||||
// Creating Ocelot output sound stream
|
||||
val (outputStream, outputSource) = Audio.newStream(SoundCategory.Records, volume = volume)
|
||||
@ -99,7 +105,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
||||
.flip
|
||||
.asInstanceOf[ByteBuffer],
|
||||
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")
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
case _: InterruptedException =>
|
||||
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
|
||||
def volume: Float = _volume
|
||||
|
||||
def volume_=(value: Float): Unit = {
|
||||
_volume = value
|
||||
|
||||
@ -211,7 +217,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
||||
// --------------------------- Screen color/text ---------------------------
|
||||
|
||||
private val defaultScreenText = "OpenFM"
|
||||
private val defaultScreenColor = IntColor(0x0AFF0A)
|
||||
private val defaultScreenColor = IntColor(0x0aff0a)
|
||||
|
||||
var screenColor: IntColor = defaultScreenColor
|
||||
private var _screenText: String = defaultScreenText
|
||||
@ -272,23 +278,26 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
||||
if (nbt.hasKey("url"))
|
||||
url = Option(nbt.getString("url"))
|
||||
|
||||
screenColor =
|
||||
screenColor = {
|
||||
if (nbt.hasKey("screenColor"))
|
||||
IntColor(nbt.getInteger("screenColor"))
|
||||
else
|
||||
defaultScreenColor
|
||||
}
|
||||
|
||||
screenText =
|
||||
screenText = {
|
||||
if (nbt.hasKey("screenText"))
|
||||
nbt.getString("screenText")
|
||||
else
|
||||
defaultScreenText
|
||||
}
|
||||
|
||||
volume =
|
||||
volume = {
|
||||
if (nbt.hasKey("volume"))
|
||||
nbt.getDouble("volume").toFloat
|
||||
else
|
||||
1
|
||||
}
|
||||
|
||||
isListenRedstone = nbt.hasKey("isListenRedstone") && nbt.getBoolean("isListenRedstone")
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ package ocelot.desktop.entity.traits
|
||||
|
||||
import ocelot.desktop.entity.traits.OcelotInterface.LogEvent
|
||||
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 java.time.Instant
|
||||
@ -21,7 +21,8 @@ trait OcelotInterface extends Entity with Environment {
|
||||
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] = {
|
||||
result(Instant.now().toEpochMilli)
|
||||
}
|
||||
|
||||
@ -27,25 +27,25 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
|
||||
def *(rhs: Vector3D): Vector3D = Vector3D(
|
||||
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.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(
|
||||
Vector3D(
|
||||
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.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(
|
||||
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.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(
|
||||
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.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) -
|
||||
@ -59,18 +59,18 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
|
||||
Vector3D(
|
||||
(y.y * z.z - z.y * y.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(
|
||||
(y.z * z.x - y.x * z.z) * 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(
|
||||
(y.x * z.y - z.x * y.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 {
|
||||
def perspective(aspect: Float, fovY: Float, zNear: Float, zFar: Float): ProjectionMatrix3D = {
|
||||
val f = (1.0 / math.tan(math.toRadians(fovY) / 2.0)).toFloat
|
||||
// format: off
|
||||
ProjectionMatrix3D(
|
||||
f / aspect, 0, 0, 0,
|
||||
0, f, 0, 0,
|
||||
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,
|
||||
m21: Float, m22: Float, m23: Float, m24: 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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
z * rhs.x - x * rhs.z + y * rhs.w + w * rhs.y,
|
||||
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)
|
||||
@ -72,7 +72,7 @@ case class Quaternion(x: Float, y: Float, z: Float, w: Float) {
|
||||
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(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
|
||||
|
||||
@ -86,7 +86,8 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
|
||||
Vector2D(x + w / 2f, y),
|
||||
Vector2D(x + w, y + h / 2f),
|
||||
Vector2D(x + w / 2f, y + h),
|
||||
Vector2D(x, y + h / 2f))
|
||||
Vector2D(x, y + h / 2f),
|
||||
)
|
||||
|
||||
def distanceTo(that: Rect2D): Float = {
|
||||
((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 = {
|
||||
Size2D(
|
||||
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)),
|
||||
)
|
||||
}
|
||||
|
||||
@ -51,4 +51,4 @@ case class Size2D(width: Float, height: Float) {
|
||||
def /(mul: Float): Size2D = Size2D(width / mul, height / mul)
|
||||
|
||||
override def toString: String = f"Size2D [ $width%8.2f x $height%8.2f ]"
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,19 +5,19 @@ import java.nio.ByteBuffer
|
||||
object Transform2D {
|
||||
def identity: Transform2D = Transform2D(
|
||||
1, 0, 0,
|
||||
0, 1, 0
|
||||
0, 1, 0,
|
||||
)
|
||||
|
||||
def scale(x: Float, y: Float): Transform2D = Transform2D(
|
||||
x, 0, 0,
|
||||
0, y, 0
|
||||
0, y, 0,
|
||||
)
|
||||
|
||||
def scale(a: Float): Transform2D = Transform2D.scale(a, a)
|
||||
|
||||
def translate(x: Float, y: Float): Transform2D = Transform2D(
|
||||
1, 0, x,
|
||||
0, 1, y
|
||||
0, 1, y,
|
||||
)
|
||||
|
||||
def viewport(width: Float, height: Float): Transform2D =
|
||||
@ -26,6 +26,7 @@ object Transform2D {
|
||||
def rotate(angle: Float): Transform2D = {
|
||||
val (s, c) = (math.sin(angle).asInstanceOf[Float], math.cos(angle).asInstanceOf[Float])
|
||||
|
||||
// format: off
|
||||
Transform2D(
|
||||
c, -s, 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) {
|
||||
def array: Array[Float] = Array(m11, m12, m13, m21, m22, m23)
|
||||
|
||||
// :|
|
||||
// format: off
|
||||
def >>(that: Transform2D): Transform2D = Transform2D(
|
||||
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,
|
||||
)
|
||||
|
||||
// (●__●)
|
||||
// format: off
|
||||
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.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(
|
||||
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]
|
||||
| [$m21%6.3f $m22%6.3f $m23%6.3f]
|
||||
""".stripMargin
|
||||
}
|
||||
|
||||
// (╯°□°)╯︵ ┻━┻
|
||||
def put(buffer: ByteBuffer): Unit = {
|
||||
|
||||
@ -26,6 +26,7 @@ object Transform3D {
|
||||
}
|
||||
|
||||
case class Transform3D(basis: Basis3D, origin: Vector3D) {
|
||||
// format: off
|
||||
def array: Array[Float] = Array(
|
||||
basis.x.x, basis.y.x, basis.z.x, origin.x,
|
||||
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]"
|
||||
|
||||
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.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.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.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(
|
||||
y * that.z - z * that.y,
|
||||
z * that.x - x * that.z,
|
||||
x * that.y - y * that.x
|
||||
x * that.y - y * that.x,
|
||||
)
|
||||
|
||||
def angle(that: Vector3D): Float = {
|
||||
|
||||
@ -17,7 +17,8 @@ import scala.collection.mutable
|
||||
import scala.util.control.Breaks._
|
||||
|
||||
//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 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 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 offscreenFramebuffer = ARBFramebufferObject.glGenFramebuffers()
|
||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, offscreenFramebuffer)
|
||||
|
||||
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D,
|
||||
offscreenTexture.texture, 0)
|
||||
|
||||
@ -152,7 +156,7 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
||||
viewport: ScreenViewport,
|
||||
bounds: Rect2D,
|
||||
filteringMode: MinFilteringMode = MinFilteringMode.Nearest,
|
||||
alpha: Float = 1.0f
|
||||
alpha: Float = 1.0f,
|
||||
): Unit = {
|
||||
flush()
|
||||
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(
|
||||
rect.x,
|
||||
rect.y
|
||||
rect.y,
|
||||
) >> Transform2D.scale(
|
||||
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 >>
|
||||
Transform2D.translate(x.round, y.round) >>
|
||||
Transform2D.scale(_font.charWidth(c), fontSize)
|
||||
}
|
||||
|
||||
val foreground = stack.head.foreground.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,
|
||||
color: Color = Color.White,
|
||||
animation: Option[Animation] = None): Unit =
|
||||
{
|
||||
animation: Option[Animation] = None): Unit = {
|
||||
sprite = name
|
||||
foreground = color
|
||||
_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) >>
|
||||
(if (fixUV)
|
||||
Transform2D.scale(spriteRect.w - 0.25f / 1024, spriteRect.h - 0.25f / 1024)
|
||||
else
|
||||
Transform2D.scale(spriteRect.w, spriteRect.h))
|
||||
Transform2D.scale(spriteRect.w - 0.25f / 1024, spriteRect.h - 0.25f / 1024)
|
||||
else
|
||||
Transform2D.scale(spriteRect.w, spriteRect.h))
|
||||
|
||||
val transform =
|
||||
val transform = {
|
||||
stack.head.transform >>
|
||||
Transform2D.translate(x, y) >>
|
||||
Transform2D.scale(width, height)
|
||||
}
|
||||
|
||||
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 (x <- 0 until width) {
|
||||
val i = (x + (height - y - 1) * width) * 4
|
||||
val r = buffer.get(i) & 0xFF
|
||||
val g = buffer.get(i + 1) & 0xFF
|
||||
val b = buffer.get(i + 2) & 0xFF
|
||||
val r = buffer.get(i) & 0xff
|
||||
val g = buffer.get(i + 1) & 0xff
|
||||
val b = buffer.get(i + 2) & 0xff
|
||||
val rgb = (r << 16) | (g << 8) | b
|
||||
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(height - h * scalingFactor - y * scalingFactor),
|
||||
Math.round(w * scalingFactor),
|
||||
Math.round(h * scalingFactor)
|
||||
Math.round(h * scalingFactor),
|
||||
)
|
||||
case _ =>
|
||||
GL11.glDisable(GL11.GL_SCISSOR_TEST)
|
||||
|
||||
@ -10,5 +10,5 @@ case class GraphicsState(
|
||||
var alphaMultiplier: Float = 1f,
|
||||
var sprite: String = "Empty",
|
||||
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")
|
||||
|
||||
//noinspection ScalaWeakerAccess
|
||||
// noinspection ScalaWeakerAccess
|
||||
object Animations {
|
||||
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))
|
||||
@ -115,6 +115,7 @@ object IconSource {
|
||||
}
|
||||
|
||||
case class Animation(frames: Array[(Int, Float)], frameSize: Option[Size2D])
|
||||
|
||||
object Animation {
|
||||
def apply(frames: (Int, Float)*) = new Animation(frames.toArray, None)
|
||||
def apply(size: Size2D, frames: (Int, Float)*) = new Animation(frames.toArray, Some(size))
|
||||
@ -127,23 +128,26 @@ object IconSource {
|
||||
IconSource(s"icons/Notification$notificationType")
|
||||
}
|
||||
|
||||
val Loading: IconSource = IconSource("Loading", animation = Some(Animation(
|
||||
Size2D(48, 32),
|
||||
(0, 0.7f),
|
||||
(1, 0.7f),
|
||||
(2, 0.7f),
|
||||
(3, 0.7f),
|
||||
(4, 0.7f),
|
||||
(5, 0.7f),
|
||||
(6, 0.7f),
|
||||
(7, 0.7f),
|
||||
(8, 0.7f),
|
||||
(9, 0.7f),
|
||||
(10, 0.7f),
|
||||
(11, 0.7f),
|
||||
(12, 0.7f),
|
||||
(13, 0.7f),
|
||||
)))
|
||||
val Loading: IconSource = IconSource(
|
||||
"Loading",
|
||||
animation = Some(Animation(
|
||||
Size2D(48, 32),
|
||||
(0, 0.7f),
|
||||
(1, 0.7f),
|
||||
(2, 0.7f),
|
||||
(3, 0.7f),
|
||||
(4, 0.7f),
|
||||
(5, 0.7f),
|
||||
(6, 0.7f),
|
||||
(7, 0.7f),
|
||||
(8, 0.7f),
|
||||
(9, 0.7f),
|
||||
(10, 0.7f),
|
||||
(11, 0.7f),
|
||||
(12, 0.7f),
|
||||
(13, 0.7f),
|
||||
)),
|
||||
)
|
||||
|
||||
val SettingsSystem: IconSource = IconSource("icons/SettingsSystem")
|
||||
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 spriteRect = Spritesheet.sprites("Empty")
|
||||
|
||||
private val emptySpriteTrans =
|
||||
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(
|
||||
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(
|
||||
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()
|
||||
|
||||
|
||||
@ -28,23 +28,25 @@ class Texture() extends Logging with Resource {
|
||||
for (y <- 0 until image.getHeight) {
|
||||
for (x <- 0 until image.getWidth) {
|
||||
val pixel = pixels(y * image.getWidth + x)
|
||||
buf.put(((pixel >> 16) & 0xFF).toByte)
|
||||
buf.put(((pixel >> 8) & 0xFF).toByte)
|
||||
buf.put((pixel & 0xFF).toByte)
|
||||
buf.put(((pixel >> 24) & 0xFF).toByte)
|
||||
buf.put(((pixel >> 16) & 0xff).toByte)
|
||||
buf.put(((pixel >> 8) & 0xff).toByte)
|
||||
buf.put((pixel & 0xff).toByte)
|
||||
buf.put(((pixel >> 24) & 0xff).toByte)
|
||||
}
|
||||
}
|
||||
|
||||
buf.flip()
|
||||
|
||||
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) = {
|
||||
this()
|
||||
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) = {
|
||||
@ -88,7 +90,7 @@ class Texture() extends Logging with Resource {
|
||||
}
|
||||
|
||||
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 {
|
||||
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()
|
||||
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)
|
||||
|
||||
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 = {
|
||||
shaderProgram.set("uLightDir", scene.lightSource.basis.up)
|
||||
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 = {
|
||||
|
||||
@ -21,4 +21,4 @@ class VertexBuffer[V <: Vertex] extends Buffer[V] {
|
||||
ty = head.vertexType
|
||||
super.extractMeta(head)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,8 +6,9 @@ import ocelot.desktop.graphics.buffer.Index
|
||||
object Mesh2D {
|
||||
val quad: Mesh2D = new Mesh2D(
|
||||
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,
|
||||
override val primitiveType: PrimitiveType = PrimitiveTriangles) extends Mesh[MeshVertex3D]
|
||||
{
|
||||
override val primitiveType: PrimitiveType = PrimitiveTriangles)
|
||||
extends Mesh[MeshVertex3D] {
|
||||
val boundingBox: Box3D = {
|
||||
Box3D.fromVertices(vertices.iterator.map(_.pos))
|
||||
}
|
||||
|
||||
@ -43,8 +43,7 @@ class MeshBuilder3D {
|
||||
|
||||
def triangle(a: Vector3D, aUV: Vector2D,
|
||||
b: Vector3D, bUV: Vector2D,
|
||||
c: Vector3D, cUV: Vector2D): Unit =
|
||||
{
|
||||
c: Vector3D, cUV: Vector2D): Unit = {
|
||||
val normal = (b - a).cross(c - a).normalize
|
||||
val aIdx = vertex(a, normal, aUV)
|
||||
val bIdx = vertex(b, normal, bUV)
|
||||
@ -56,8 +55,7 @@ class MeshBuilder3D {
|
||||
b: Vector3D, bUV: Vector2D,
|
||||
c: Vector3D, cUV: Vector2D,
|
||||
d: Vector3D, dUV: Vector2D,
|
||||
color: Color): Unit =
|
||||
{
|
||||
color: Color): Unit = {
|
||||
val normal = (b - a).cross(c - a).normalize
|
||||
val aIdx = vertex(a, normal, aUV, color)
|
||||
val bIdx = vertex(b, normal, bUV, color)
|
||||
@ -74,15 +72,19 @@ class MeshBuilder3D {
|
||||
spriteRect.x + cutRect.x * spriteUVScale * spriteRect.w,
|
||||
spriteRect.y + cutRect.y * spriteUVScale * spriteRect.w,
|
||||
cutRect.w * spriteUVScale * spriteRect.w,
|
||||
cutRect.h * spriteUVScale * spriteRect.h
|
||||
cutRect.h * spriteUVScale * spriteRect.h,
|
||||
)
|
||||
|
||||
quad(
|
||||
a, Vector2D(rect.x + rect.w, rect.y),
|
||||
b, Vector2D(rect.x, rect.y),
|
||||
c, Vector2D(rect.x, rect.y + rect.h),
|
||||
d, Vector2D(rect.x + rect.w, rect.y + rect.h),
|
||||
color
|
||||
a,
|
||||
Vector2D(rect.x + rect.w, rect.y),
|
||||
b,
|
||||
Vector2D(rect.x, rect.y),
|
||||
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)),
|
||||
top: Option[(String, Rect2D)] = Some(("Empty", Rect2D.Unit)),
|
||||
bottom: Option[(String, Rect2D)] = Some(("Empty", Rect2D.Unit)),
|
||||
color: Color = Color.White): Unit =
|
||||
{
|
||||
left.foreach(sprite => quad(
|
||||
Vector3D(min.x, max.y, max.z),
|
||||
Vector3D(min.x, max.y, min.z),
|
||||
Vector3D(min.x, min.y, min.z),
|
||||
Vector3D(min.x, min.y, max.z),
|
||||
sprite, color,
|
||||
))
|
||||
color: Color = Color.White): Unit = {
|
||||
left.foreach(sprite => {
|
||||
quad(
|
||||
Vector3D(min.x, max.y, max.z),
|
||||
Vector3D(min.x, max.y, min.z),
|
||||
Vector3D(min.x, min.y, min.z),
|
||||
Vector3D(min.x, min.y, max.z),
|
||||
sprite,
|
||||
color,
|
||||
)
|
||||
})
|
||||
|
||||
right.foreach(sprite => quad(
|
||||
Vector3D(max.x, max.y, min.z),
|
||||
Vector3D(max.x, max.y, max.z),
|
||||
Vector3D(max.x, min.y, max.z),
|
||||
Vector3D(max.x, min.y, min.z),
|
||||
sprite, color,
|
||||
))
|
||||
right.foreach(sprite => {
|
||||
quad(
|
||||
Vector3D(max.x, max.y, min.z),
|
||||
Vector3D(max.x, max.y, max.z),
|
||||
Vector3D(max.x, min.y, max.z),
|
||||
Vector3D(max.x, min.y, min.z),
|
||||
sprite,
|
||||
color,
|
||||
)
|
||||
})
|
||||
|
||||
front.foreach(sprite => quad(
|
||||
Vector3D(min.x, max.y, min.z),
|
||||
Vector3D(max.x, max.y, min.z),
|
||||
Vector3D(max.x, min.y, min.z),
|
||||
Vector3D(min.x, min.y, min.z),
|
||||
sprite, color,
|
||||
))
|
||||
front.foreach(sprite => {
|
||||
quad(
|
||||
Vector3D(min.x, max.y, min.z),
|
||||
Vector3D(max.x, max.y, min.z),
|
||||
Vector3D(max.x, min.y, min.z),
|
||||
Vector3D(min.x, min.y, min.z),
|
||||
sprite,
|
||||
color,
|
||||
)
|
||||
})
|
||||
|
||||
back.foreach(sprite => quad(
|
||||
Vector3D(max.x, max.y, max.z),
|
||||
Vector3D(min.x, max.y, max.z),
|
||||
Vector3D(min.x, min.y, max.z),
|
||||
Vector3D(max.x, min.y, max.z),
|
||||
sprite, color,
|
||||
))
|
||||
back.foreach(sprite => {
|
||||
quad(
|
||||
Vector3D(max.x, max.y, max.z),
|
||||
Vector3D(min.x, max.y, max.z),
|
||||
Vector3D(min.x, min.y, max.z),
|
||||
Vector3D(max.x, min.y, max.z),
|
||||
sprite,
|
||||
color,
|
||||
)
|
||||
})
|
||||
|
||||
top.foreach(sprite => quad(
|
||||
Vector3D(max.x, max.y, min.z),
|
||||
Vector3D(min.x, max.y, min.z),
|
||||
Vector3D(min.x, max.y, max.z),
|
||||
Vector3D(max.x, max.y, max.z),
|
||||
sprite, color,
|
||||
))
|
||||
top.foreach(sprite => {
|
||||
quad(
|
||||
Vector3D(max.x, max.y, min.z),
|
||||
Vector3D(min.x, max.y, min.z),
|
||||
Vector3D(min.x, max.y, max.z),
|
||||
Vector3D(max.x, max.y, max.z),
|
||||
sprite,
|
||||
color,
|
||||
)
|
||||
})
|
||||
|
||||
bottom.foreach(sprite => quad(
|
||||
Vector3D(max.x, min.y, max.z),
|
||||
Vector3D(min.x, min.y, max.z),
|
||||
Vector3D(min.x, min.y, min.z),
|
||||
Vector3D(max.x, min.y, min.z),
|
||||
sprite, color,
|
||||
))
|
||||
bottom.foreach(sprite => {
|
||||
quad(
|
||||
Vector3D(max.x, min.y, max.z),
|
||||
Vector3D(min.x, min.y, max.z),
|
||||
Vector3D(min.x, min.y, min.z),
|
||||
Vector3D(max.x, min.y, min.z),
|
||||
sprite,
|
||||
color,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
def line(a: Vector3D, b: Vector3D, aColor: Color = Color.White, bColor: Color = Color.White): Unit = {
|
||||
@ -154,4 +173,4 @@ class MeshBuilder3D {
|
||||
new Mesh3D(vertices.toSeq, indices = Some(indices.toSeq),
|
||||
primitiveType = primitiveType.getOrElse(PrimitiveTriangles))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,8 +22,8 @@ object MeshInstance2D extends VertexType {
|
||||
}
|
||||
|
||||
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 vertexType: VertexType = MeshInstance2D
|
||||
|
||||
@ -17,13 +17,13 @@ object MeshInstance3D extends VertexType {
|
||||
Attribute("inUVTransform0", 3, GL11.GL_FLOAT, normalized = false, stride, 64),
|
||||
Attribute("inUVTransform1", 3, GL11.GL_FLOAT, normalized = false, stride, 76),
|
||||
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,
|
||||
lightingFactor: Float, textureFactor: Float) extends Vertex
|
||||
{
|
||||
lightingFactor: Float, textureFactor: Float)
|
||||
extends Vertex {
|
||||
override def vertexType: VertexType = MeshInstance3D
|
||||
|
||||
override def stride: Int = MeshInstance3D.stride
|
||||
|
||||
@ -10,7 +10,7 @@ object MeshVertex2D extends VertexType {
|
||||
|
||||
override val attributes: Seq[Attribute] = Array(
|
||||
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),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -12,4 +12,4 @@ object PrimitiveTriangles extends PrimitiveType {
|
||||
|
||||
object PrimitiveLines extends PrimitiveType {
|
||||
override def toGL: Int = GL11.GL_LINES
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,8 @@ import org.lwjgl.opengl._
|
||||
import java.nio.ByteBuffer
|
||||
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 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, "glDrawElementsInstancedARB")
|
||||
method.invoke(null, "glDrawElementsInstancedARB"),
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@ -70,10 +73,12 @@ class InstanceRenderer[V <: Vertex, I <: Vertex](mesh: Mesh[V], instanceType: Ve
|
||||
|
||||
indexBuffer match {
|
||||
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 =>
|
||||
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()
|
||||
|
||||
@ -4,8 +4,8 @@ import ocelot.desktop.geometry.{ProjectionMatrix3D, Vector3D}
|
||||
|
||||
class Camera3D(var zNear: Float = 0.1f,
|
||||
var zFar: Float = 100.0f,
|
||||
var fovY: Float = 80.0f) extends SceneNode3D
|
||||
{
|
||||
var fovY: Float = 80.0f)
|
||||
extends SceneNode3D {
|
||||
def distanceTo(point: Vector3D): Float = {
|
||||
(origin - point).length
|
||||
}
|
||||
|
||||
@ -3,4 +3,5 @@ package ocelot.desktop.graphics.scene
|
||||
import ocelot.desktop.color.Color
|
||||
|
||||
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 textureFactor: Float = 1,
|
||||
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
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
// parallels totoro.ocelot.brain.entity.traits.Inventory
|
||||
// this is intentional
|
||||
|
||||
/**
|
||||
* The type of items stored in this inventory.
|
||||
*/
|
||||
/** The type of items stored in this inventory. */
|
||||
type I <: Item
|
||||
|
||||
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 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
|
||||
*/
|
||||
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:
|
||||
*
|
||||
@ -47,9 +41,7 @@ trait Inventory extends EventAware {
|
||||
*/
|
||||
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 clearInventory(): Unit = {
|
||||
@ -86,46 +78,35 @@ trait Inventory extends EventAware {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A proxy to access a slot of the inventory.
|
||||
*/
|
||||
final class Slot private[Inventory](val index: Int) {
|
||||
/** A proxy to access a slot of the inventory. */
|
||||
final class Slot private[Inventory] (val index: Int) {
|
||||
require(index >= 0)
|
||||
|
||||
def isEmpty: Boolean = get.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 = {
|
||||
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 = {
|
||||
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 = {
|
||||
setSlot(index, None)
|
||||
}
|
||||
|
||||
/**
|
||||
* The [[Item]] contained in this slot.
|
||||
*/
|
||||
/** The [[Item]] contained in this slot. */
|
||||
def get: Option[inventory.I] = slotItems.get(index)
|
||||
|
||||
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`.
|
||||
*/
|
||||
@ -167,9 +148,8 @@ trait Inventory extends EventAware {
|
||||
}
|
||||
|
||||
final object Slot {
|
||||
/**
|
||||
* Creates a proxy to an inventory slot.
|
||||
*/
|
||||
|
||||
/** Creates a proxy to an inventory slot. */
|
||||
def apply(index: Int) = new Slot(index)
|
||||
}
|
||||
|
||||
@ -178,7 +158,7 @@ trait Inventory extends EventAware {
|
||||
|
||||
(slotItems.get(index), item) match {
|
||||
case (Some(oldItem), Some(newItem)) if oldItem == newItem =>
|
||||
// no-op
|
||||
// no-op
|
||||
|
||||
case (Some(oldItem), Some(newItem)) =>
|
||||
// replace old with new
|
||||
@ -198,7 +178,7 @@ trait Inventory extends EventAware {
|
||||
onItemAddedImpl(slot)
|
||||
|
||||
case (None, None) =>
|
||||
// no-op
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,15 +205,14 @@ trait Inventory extends EventAware {
|
||||
|
||||
object Inventory {
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -241,9 +220,7 @@ object Inventory {
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,15 +11,11 @@ import ocelot.desktop.util.Disposable
|
||||
import totoro.ocelot.brain.util.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 {
|
||||
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
|
||||
|
||||
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 = {
|
||||
val prevSlot = slot
|
||||
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]))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.)
|
||||
*/
|
||||
@ -55,18 +48,13 @@ trait Item extends EventAware with Updatable with Disposable {
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@ -74,8 +62,7 @@ trait Item extends EventAware with Updatable with Disposable {
|
||||
|
||||
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 {{{
|
||||
* override protected def fillTooltipBody(body: Widget): Unit = {
|
||||
@ -91,31 +78,27 @@ trait Item extends EventAware with Updatable with Disposable {
|
||||
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.
|
||||
*/
|
||||
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).
|
||||
*
|
||||
* Keep this rather cheap.
|
||||
*/
|
||||
def factory: ItemFactory
|
||||
|
||||
/**
|
||||
* Override this in subclasses to implement some specific item behavior
|
||||
* during UI update
|
||||
*/
|
||||
/** Override this in subclasses to implement some specific item behavior
|
||||
* during UI update
|
||||
*/
|
||||
override def update(): Unit = {}
|
||||
}
|
||||
|
||||
object Item {
|
||||
/**
|
||||
* A notification that can be sent with [[Item.notifySlot]].
|
||||
*/
|
||||
|
||||
/** A notification that can be sent with [[Item.notifySlot]]. */
|
||||
trait Notification
|
||||
}
|
||||
|
||||
@ -3,8 +3,7 @@ package ocelot.desktop.inventory
|
||||
import ocelot.desktop.graphics.IconSource
|
||||
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]]
|
||||
* (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.
|
||||
*/
|
||||
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
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
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).
|
||||
*
|
||||
@ -33,27 +29,21 @@ trait ItemFactory {
|
||||
*/
|
||||
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`.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
def icon: IconSource
|
||||
|
||||
/**
|
||||
* Constructs a new item instance.
|
||||
*/
|
||||
/** Constructs a new item instance. */
|
||||
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[_, _]]
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
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.
|
||||
*/
|
||||
|
||||
@ -20,9 +20,7 @@ object Items extends Logging {
|
||||
// this is just to force load the class during initialization
|
||||
def init(): Unit = {}
|
||||
|
||||
/**
|
||||
* Registers a recoverer for [[ItemRecoverer.sourceClass]].
|
||||
*/
|
||||
/** Registers a recoverer for [[ItemRecoverer.sourceClass]]. */
|
||||
def registerRecoverer(recoverer: ItemRecoverer[_, _]): Unit = {
|
||||
if (!_recoverers.contains(recoverer.sourceClass)) {
|
||||
_recoverers(recoverer.sourceClass) = recoverer
|
||||
@ -72,8 +70,7 @@ object Items extends Logging {
|
||||
|
||||
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.
|
||||
*/
|
||||
@ -111,7 +108,7 @@ object Items extends Logging {
|
||||
case ExtendedTier.Creative => new MagicalMemoryItem.Factory()
|
||||
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, _))
|
||||
@ -121,7 +118,7 @@ object Items extends Logging {
|
||||
FloppyItem.Factory.Empty.icon,
|
||||
Loot.Floppies.iterator
|
||||
.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(
|
||||
@ -129,26 +126,31 @@ object Items extends Logging {
|
||||
EepromItem.Factory.Empty.icon,
|
||||
Loot.Eeproms.iterator
|
||||
.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(_))
|
||||
registerSingleton(NetworkCardItem.Factory)
|
||||
|
||||
registerTiered("Wireless Net. Card", Tier.One to Tier.Two) {
|
||||
case Tier.One => WirelessNetworkCardItem.Tier1.Factory
|
||||
case Tier.Two => WirelessNetworkCardItem.Tier2.Factory
|
||||
}
|
||||
|
||||
registerSingleton(LinkedCardItem.Factory)
|
||||
registerSingleton(InternetCardItem.Factory)
|
||||
|
||||
registerTiered("Redstone Card", Tier.One to Tier.Two) {
|
||||
case Tier.One => RedstoneCardItem.Tier1.Factory
|
||||
case Tier.Two => RedstoneCardItem.Tier2.Factory
|
||||
}
|
||||
|
||||
registerTiered("Data Card", Tier.One to Tier.Three) {
|
||||
case Tier.One => DataCardItem.Tier1.Factory
|
||||
case Tier.Two => DataCardItem.Tier2.Factory
|
||||
case Tier.One => DataCardItem.Tier1.Factory
|
||||
case Tier.Two => DataCardItem.Tier2.Factory
|
||||
case Tier.Three => DataCardItem.Tier3.Factory
|
||||
}
|
||||
|
||||
registerSingleton(SoundCardItem.Factory)
|
||||
registerSingleton(SelfDestructingCardItem.Factory)
|
||||
registerSingleton(OcelotCardItem.Factory)
|
||||
@ -162,6 +164,6 @@ object Items extends Logging {
|
||||
new TapeItem.Factory(Tape.Kind.Iron).icon,
|
||||
Tape.Kind.values.iterator
|
||||
.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
|
||||
|
||||
/**
|
||||
* Provides persistence for an [[Inventory]].
|
||||
/** Provides persistence for an [[Inventory]].
|
||||
*
|
||||
* [[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
|
||||
@ -45,7 +44,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
||||
case ItemLoadException(message) =>
|
||||
logger.error(
|
||||
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)
|
||||
}
|
||||
@ -60,7 +59,8 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
||||
super.save(nbt)
|
||||
|
||||
nbt.setNewTagList(
|
||||
InventoryTag, inventoryIterator.map { slot =>
|
||||
InventoryTag,
|
||||
inventoryIterator.map { slot =>
|
||||
val item = slot.get.get
|
||||
|
||||
val slotNbt = new NBTTagCompound
|
||||
@ -77,7 +77,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
||||
}
|
||||
|
||||
slotNbt
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
||||
constructor.newInstance(entity).asInstanceOf[I]
|
||||
}
|
||||
|
||||
//noinspection ScalaWeakerAccess
|
||||
// noinspection ScalaWeakerAccess
|
||||
@throws[ItemLoadException]("if the item could not be loaded")
|
||||
protected def loadPlainItem(itemClass: Class[_]): I = {
|
||||
itemClass.getConstructor().newInstance().asInstanceOf[I]
|
||||
|
||||
@ -16,8 +16,7 @@ import totoro.ocelot.brain.network.Network
|
||||
|
||||
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.
|
||||
*
|
||||
@ -36,11 +35,10 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
|
||||
|
||||
@volatile
|
||||
private var syncFuel: Int = _
|
||||
|
||||
refuel()
|
||||
|
||||
/**
|
||||
* The backing [[BrainInventory brain Inventory]].
|
||||
*/
|
||||
/** The backing [[BrainInventory brain Inventory]]. */
|
||||
def brainInventory: BrainInventory
|
||||
|
||||
override def load(nbt: NBTTagCompound): Unit = {
|
||||
@ -210,7 +208,6 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
|
||||
case SyncDirection.Reconcile =>
|
||||
checkSlotStatus(slotIndex) match {
|
||||
case SlotStatus.Synchronized => // no-op
|
||||
|
||||
// let's just grab whatever we have
|
||||
case SlotStatus.DesktopNonEmpty => doSync(slotIndex, SyncDirection.DesktopToBrain)
|
||||
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 {
|
||||
case (Some(item), Some(entity)) if item.component eq entity => SlotStatus.Synchronized
|
||||
case (Some(_), Some(_)) => SlotStatus.Conflict
|
||||
@ -257,6 +254,7 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
|
||||
case (None, Some(_)) => SlotStatus.BrainNonEmpty
|
||||
case (None, None) => SlotStatus.Synchronized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SyncedInventory {
|
||||
@ -267,42 +265,29 @@ object SyncedInventory {
|
||||
object SyncDirection extends Enumeration {
|
||||
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
|
||||
|
||||
/**
|
||||
* Apply a change from the [[SyncedInventory.brainInventory]] to [[SyncedInventory]].
|
||||
*/
|
||||
/** Apply a change from the [[SyncedInventory.brainInventory]] to [[SyncedInventory]]. */
|
||||
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
|
||||
}
|
||||
|
||||
object SlotStatus extends Enumeration {
|
||||
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
|
||||
|
||||
/**
|
||||
* [[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
|
||||
|
||||
/**
|
||||
* [[SyncedInventory]]'s slot is empty; [[SyncedInventory.brainInventory]]'s isn't.
|
||||
*/
|
||||
/** [[SyncedInventory]]'s slot is empty; [[SyncedInventory.brainInventory]]'s isn't. */
|
||||
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.
|
||||
*/
|
||||
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.util.Tier.Tier
|
||||
|
||||
class ComponentBusItem(val componentBus: ComponentBus)
|
||||
extends Item
|
||||
with ComponentItem
|
||||
{
|
||||
class ComponentBusItem(val componentBus: ComponentBus) extends Item with ComponentItem {
|
||||
override def component: Entity with Environment = componentBus
|
||||
override def showAddress: Boolean = false
|
||||
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 {
|
||||
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,10 +9,7 @@ import totoro.ocelot.brain.entity.traits.Floppy
|
||||
import totoro.ocelot.brain.entity.{DiskDriveMountable, FloppyDiskDrive}
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class DiskDriveMountableItem(val diskDriveMountable: DiskDriveMountable)
|
||||
extends RackMountableItem
|
||||
with DiskDriveAware
|
||||
{
|
||||
class DiskDriveMountableItem(val diskDriveMountable: DiskDriveMountable) extends RackMountableItem with DiskDriveAware {
|
||||
override def floppyDiskDrive: FloppyDiskDrive = 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 (eeprom.codeBytes != null)
|
||||
tooltip.addLine(s"Code: ${eeprom.codeBytes.map(_.length).getOrElse(0)} bytes / ${Settings.get.eepromSize} bytes")
|
||||
if (eeprom.codeBytes != null) {
|
||||
tooltip.addLine(
|
||||
s"Code: ${eeprom.codeBytes.map(_.length).getOrElse(0)} bytes / ${Settings.get.eepromSize} bytes"
|
||||
)
|
||||
}
|
||||
if (eeprom.volatileData != null)
|
||||
tooltip.addLine(s"Data: ${eeprom.volatileData.length} bytes / ${Settings.get.eepromDataSize} bytes")
|
||||
if (eeprom.readonly)
|
||||
@ -59,13 +62,14 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis
|
||||
onConfirmed = { text =>
|
||||
eeprom.codeURL = Some(new URL(text))
|
||||
},
|
||||
inputValidator = text =>
|
||||
inputValidator = text => {
|
||||
try {
|
||||
new URL(text)
|
||||
true
|
||||
} catch {
|
||||
case _: MalformedURLException => false
|
||||
},
|
||||
}
|
||||
},
|
||||
).show()
|
||||
})
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
||||
import totoro.ocelot.brain.entity.fs.ReadWriteLabel
|
||||
import totoro.ocelot.brain.entity.traits.{Disk, Entity, Floppy}
|
||||
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.Tier.Tier
|
||||
|
||||
|
||||
@ -7,7 +7,8 @@ import totoro.ocelot.brain.entity.GraphicsCard
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, GenericGPU}
|
||||
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 factory: GraphicsCardItem.Factory = new GraphicsCardItem.Factory(gpu.tier)
|
||||
|
||||
@ -30,4 +30,4 @@ object MagicalMemoryItem {
|
||||
|
||||
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new MagicalMemoryItem(_)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,12 +14,7 @@ import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class OcelotCardItem(val ocelotCard: OcelotCard)
|
||||
extends Item
|
||||
with ComponentItem
|
||||
with OcelotInterfaceLogStorage
|
||||
with PersistableItem
|
||||
with CardItem
|
||||
with Logging {
|
||||
extends Item with ComponentItem with OcelotInterfaceLogStorage with PersistableItem with CardItem with Logging {
|
||||
|
||||
override def component: OcelotCard = ocelotCard
|
||||
|
||||
@ -40,6 +35,7 @@ class OcelotCardItem(val ocelotCard: OcelotCard)
|
||||
override def factory: ItemFactory = OcelotCardItem.Factory
|
||||
|
||||
private val OcelotSays = Array("meow", ":3", "♥", "meooow", "~(=^–^)", "/ᐠ。ꞈ。ᐟ\\", "=^._.^=", "=’①。①’=")
|
||||
|
||||
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
||||
super.fillTooltip(tooltip)
|
||||
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.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 {
|
||||
abstract class Factory extends ItemFactory {
|
||||
|
||||
@ -10,10 +10,7 @@ import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class SelfDestructingCardItem(val card: SelfDestructingCard)
|
||||
extends Item
|
||||
with ComponentItem
|
||||
with PersistableItem
|
||||
with CardItem {
|
||||
extends Item with ComponentItem with PersistableItem with CardItem {
|
||||
|
||||
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.Tier
|
||||
|
||||
class ServerItem(val server: Server)
|
||||
extends RackMountableItem
|
||||
with AudibleComputerAware
|
||||
{
|
||||
class ServerItem(val server: Server) extends RackMountableItem with AudibleComputerAware {
|
||||
override def component: Server = server
|
||||
override def factory: ItemFactory = new ServerItem.Factory(server.tier)
|
||||
|
||||
@ -44,24 +41,26 @@ class ServerItem(val server: Server)
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
|
||||
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))
|
||||
componentBusSlots = addSlotWidgets(new ComponentBusSlotWidget(_, Tier.Three), new ComponentBusSlotWidget(_, Tier.Three))
|
||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
|
||||
componentBusSlots =
|
||||
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))
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
|
||||
case _ =>
|
||||
cardSlots =
|
||||
cardSlots = {
|
||||
if (server.tier == Tier.Three) {
|
||||
addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Two),
|
||||
new CardSlotWidget(_, Tier.Two)
|
||||
new CardSlotWidget(_, Tier.Two),
|
||||
)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
@ -69,6 +68,7 @@ class ServerItem(val server: Server)
|
||||
new CardSlotWidget(_, 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),
|
||||
)
|
||||
|
||||
diskSlots = addSlotWidgets(
|
||||
|
||||
@ -12,11 +12,7 @@ import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class SoundCardItem(val soundCard: SoundCard)
|
||||
extends Item
|
||||
with ComponentItem
|
||||
with PersistableItem
|
||||
with CardItem
|
||||
with Windowed[SoundCardWindow] {
|
||||
extends Item with ComponentItem with PersistableItem with CardItem with Windowed[SoundCardWindow] {
|
||||
|
||||
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.Tier
|
||||
|
||||
abstract class WirelessNetworkCardItem(override val card: WirelessNetworkCard)
|
||||
extends NetworkCardItem(card) {
|
||||
}
|
||||
abstract class WirelessNetworkCardItem(override val card: WirelessNetworkCard) extends NetworkCardItem(card) {}
|
||||
|
||||
object WirelessNetworkCardItem {
|
||||
abstract class Factory extends ItemFactory {
|
||||
|
||||
@ -2,7 +2,5 @@ package ocelot.desktop.inventory.traits
|
||||
|
||||
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
|
||||
|
||||
@ -8,14 +8,13 @@ import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
||||
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]].
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@ -8,9 +8,7 @@ import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
||||
import totoro.ocelot.brain.entity.machine.MachineAPI
|
||||
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 {
|
||||
override def component: Entity with GenericCPU
|
||||
|
||||
|
||||
@ -13,9 +13,7 @@ import totoro.ocelot.brain.util.DyeColor
|
||||
import javax.swing.JFileChooser
|
||||
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] {
|
||||
override def component: Entity with Disk
|
||||
|
||||
@ -51,11 +49,12 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
||||
// Real path
|
||||
component match {
|
||||
case diskManaged: DiskManaged =>
|
||||
DiskItem.addRealPathContextMenuEntries(menu, diskManaged, realPathSetter => {
|
||||
reinserting {
|
||||
realPathSetter()
|
||||
}
|
||||
})
|
||||
DiskItem.addRealPathContextMenuEntries(menu, diskManaged,
|
||||
realPathSetter => {
|
||||
reinserting {
|
||||
realPathSetter()
|
||||
}
|
||||
})
|
||||
case _ =>
|
||||
}
|
||||
|
||||
@ -94,10 +93,11 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
||||
}
|
||||
|
||||
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(
|
||||
if (diskRealPathAware.customRealPath.isDefined) "Change directory" else "Set directory",
|
||||
IconSource.Folder
|
||||
IconSource.Folder,
|
||||
) {
|
||||
OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
||||
Try {
|
||||
|
||||
@ -3,7 +3,5 @@ package ocelot.desktop.inventory.traits
|
||||
import ocelot.desktop.inventory.Item
|
||||
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
|
||||
|
||||
@ -6,10 +6,7 @@ import ocelot.desktop.ui.widget.contextmenu.ContextMenu
|
||||
import totoro.ocelot.brain.entity.result
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, RackMountable}
|
||||
|
||||
trait RackMountableItem
|
||||
extends Item
|
||||
with ComponentItem
|
||||
{
|
||||
trait RackMountableItem extends Item with ComponentItem {
|
||||
override def component: Entity with RackMountable
|
||||
|
||||
def isInRack: Boolean = slot.exists(_.inventory.isInstanceOf[RackNode])
|
||||
@ -19,4 +16,4 @@ trait RackMountableItem
|
||||
|
||||
super.fillRmbMenu(menu)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ import java.util.Calendar
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceAware)
|
||||
extends EntityNode(entity)
|
||||
extends EntityNode(entity)
|
||||
with SyncedInventory
|
||||
with DiskActivityHandler
|
||||
with OcelotLogParticleNode
|
||||
|
||||
@ -220,17 +220,18 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
||||
private def move(pos: Vector2D): Unit = {
|
||||
val oldPos = position
|
||||
|
||||
val desiredPos =
|
||||
if (KeyEvents.isControlDown)
|
||||
val desiredPos = {
|
||||
if (KeyEvents.isControlDown) {
|
||||
(pos - workspaceView.cameraOffset).snap(Size) + // snap the position to the grid relative to the origin
|
||||
workspaceView.cameraOffset - // restore the camera offset
|
||||
grabPoint.snap(Size) + // accounts for multi-block screens
|
||||
Vector2D(
|
||||
((Size - width) % Size) / 2,
|
||||
((Size - height) % Size) / 2,
|
||||
) // if a node is not full-size, moves it to the center of the grid cell
|
||||
else
|
||||
workspaceView.cameraOffset - // restore the camera offset
|
||||
grabPoint.snap(Size) + // accounts for multi-block screens
|
||||
Vector2D(
|
||||
((Size - width) % Size) / 2,
|
||||
((Size - height) % Size) / 2,
|
||||
) // if a node is not full-size, moves it to the center of the grid cell
|
||||
} else
|
||||
pos - grabPoint
|
||||
}
|
||||
|
||||
position = desiredPos
|
||||
workspaceView.resolveCollision(this)
|
||||
@ -260,7 +261,7 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
||||
|
||||
g.sprite(
|
||||
iconSource.path,
|
||||
position. x + HighlightThickness,
|
||||
position.x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
size.width - HighlightThickness * 2,
|
||||
size.height - HighlightThickness * 2,
|
||||
|
||||
@ -27,4 +27,4 @@ object NodePort {
|
||||
def fromByte(byte: Byte): NodePort = {
|
||||
if (byte == -1) NodePort(None) else NodePort(Some(Direction(byte)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,10 @@ package ocelot.desktop.node
|
||||
import ocelot.desktop.entity.{Camera, OcelotBlock, OpenFMRadio}
|
||||
import ocelot.desktop.graphics.IconSource
|
||||
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 scala.collection.mutable
|
||||
@ -17,11 +20,11 @@ object NodeRegistry {
|
||||
group = new NodeTypeGroup(name)
|
||||
groups += group
|
||||
}
|
||||
|
||||
|
||||
private def addType(nodeType: NodeType): Unit = {
|
||||
group.types += nodeType
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------ Original nodes ------------------------------
|
||||
|
||||
nextGroup("Original")
|
||||
|
||||
@ -4,7 +4,7 @@ import ocelot.desktop.graphics.IconSource
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class NodeType(val name: String, val icon: IconSource, val tier: Option[Tier], factory: => Node)
|
||||
extends Ordered[NodeType] {
|
||||
extends Ordered[NodeType] {
|
||||
def make(): Node = factory
|
||||
|
||||
override def compare(that: NodeType): Int = this.name.compare(that.name)
|
||||
|
||||
@ -4,4 +4,4 @@ import scala.collection.mutable
|
||||
|
||||
class NodeTypeGroup(val name: String) {
|
||||
val types: mutable.ArrayBuffer[NodeType] = mutable.ArrayBuffer[NodeType]()
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,10 +30,10 @@ class NodeTypeWidget(val nodeType: NodeType) extends Widget with MouseHandler wi
|
||||
private val labelTooltip = new LabelTooltip(nodeType.name)
|
||||
|
||||
def onHoverEnter(): Unit =
|
||||
root.get.tooltipPool.addTooltip(labelTooltip)
|
||||
root.get.tooltipPool.addTooltip(labelTooltip)
|
||||
|
||||
def onHoverLeave(): Unit =
|
||||
root.get.tooltipPool.closeTooltip(labelTooltip)
|
||||
root.get.tooltipPool.closeTooltip(labelTooltip)
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
val size = Spritesheet.spriteSize(nodeType.icon) * 4
|
||||
|
||||
@ -15,7 +15,7 @@ import scala.util.Random
|
||||
trait OcelotLogParticleNode extends Node {
|
||||
private case class LogParticle(
|
||||
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
|
||||
@ -76,5 +76,3 @@ object OcelotLogParticleNode {
|
||||
private val LogParticleMoveSpeed: Float = 1f
|
||||
private val LogParticleMoveDistance: Float = 20f
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -29,10 +29,9 @@ trait PositionalSoundSourcesNode extends Node {
|
||||
soundPosition = Vector3D(
|
||||
(nodeCenterX - rootWidthHalf) / rootWidthHalf * limit,
|
||||
(nodeCenterY - rootHeightHalf) / rootHeightHalf * limit,
|
||||
0
|
||||
0,
|
||||
)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
soundPosition = Vector3D.Zero
|
||||
}
|
||||
|
||||
|
||||
@ -16,9 +16,9 @@ class ColorfulLampNode(val lamp: ColorfulLamp) extends EntityNode(lamp) with Lab
|
||||
super.draw(g)
|
||||
|
||||
lastColor = RGBAColor(
|
||||
(((lamp.color >>> 10) & 0x1F) << 3).toShort,
|
||||
(((lamp.color >>> 5) & 0x1F) << 3).toShort,
|
||||
(((lamp.color >>> 0) & 0x1F) << 3).toShort
|
||||
(((lamp.color >>> 10) & 0x1f) << 3).toShort,
|
||||
(((lamp.color >>> 5) & 0x1f) << 3).toShort,
|
||||
(((lamp.color >>> 0) & 0x1f) << 3).toShort,
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
class ComputerNode(val computerCase: Case)
|
||||
extends ComputerAwareNode(computerCase)
|
||||
with AudibleComputerAware
|
||||
with WindowedNode[ComputerWindow]
|
||||
{
|
||||
extends ComputerAwareNode(computerCase) with AudibleComputerAware with WindowedNode[ComputerWindow] {
|
||||
override val iconSource: IconSource = IconSource.Nodes.Computer.Default
|
||||
override def iconColor: Color = TierColor.get(computerCase.tier)
|
||||
|
||||
@ -80,31 +77,31 @@ class ComputerNode(val computerCase: Case)
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
|
||||
case _ =>
|
||||
cardSlots =
|
||||
cardSlots = {
|
||||
if (computerCase.tier == Tier.Three) {
|
||||
addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Two),
|
||||
new CardSlotWidget(_, Tier.Two)
|
||||
new CardSlotWidget(_, Tier.Two),
|
||||
)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
|
||||
|
||||
diskSlots =
|
||||
diskSlots = {
|
||||
if (computerCase.tier == Tier.Three) {
|
||||
addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Two))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Three))
|
||||
}
|
||||
}
|
||||
|
||||
floppySlot = Some(addSlotWidget(new FloppySlotWidget(_)))
|
||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
||||
@ -117,4 +114,4 @@ class ComputerNode(val computerCase: Case)
|
||||
override protected def onShiftClick(event: ClickEvent): Unit = toggleIsTurnedOn()
|
||||
|
||||
override protected def hoveredShiftStatusBarText: String = if (computer.machine.isRunning) "Turn off" else "Turn on"
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import ocelot.desktop.windows.DiskDriveWindow
|
||||
import totoro.ocelot.brain.entity.FloppyDiskDrive
|
||||
|
||||
class DiskDriveNode(entity: FloppyDiskDrive)
|
||||
extends EntityNode(entity)
|
||||
extends EntityNode(entity)
|
||||
with SyncedInventory
|
||||
with LabeledEntityNode
|
||||
with DiskDriveAware
|
||||
|
||||
@ -6,12 +6,9 @@ import ocelot.desktop.windows.HologramProjectorWindow
|
||||
import totoro.ocelot.brain.entity.HologramProjector
|
||||
|
||||
class HologramProjectorNode(val hologramProjector: HologramProjector)
|
||||
extends EntityNode(hologramProjector)
|
||||
with LabeledEntityNode
|
||||
with WindowedNode[HologramProjectorWindow] {
|
||||
extends EntityNode(hologramProjector) with LabeledEntityNode with WindowedNode[HologramProjectorWindow] {
|
||||
|
||||
override def iconSource: IconSource = IconSource.Nodes.HologramProjector(hologramProjector.tier)
|
||||
|
||||
override def createWindow(): HologramProjectorWindow = new HologramProjectorWindow(this)
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ class IronNoteBlockNode(val ironNoteBlock: IronNoteBlock) extends NoteBlockNodeB
|
||||
EventBus.send(NoteBlockTriggerEvent(
|
||||
ironNoteBlock.node.address,
|
||||
Instruments(Random.nextInt(Instruments.length))._1,
|
||||
Random.nextInt(NumberOfPitches)
|
||||
Random.nextInt(NumberOfPitches),
|
||||
))
|
||||
}
|
||||
case _ =>
|
||||
|
||||
@ -16,11 +16,10 @@ import totoro.ocelot.brain.network
|
||||
import totoro.ocelot.brain.util.{Direction, Tier}
|
||||
|
||||
class MicrocontrollerNode(val microcontroller: Microcontroller)
|
||||
extends ComputerAwareNode(microcontroller)
|
||||
extends ComputerAwareNode(microcontroller)
|
||||
with ComputerAware
|
||||
with DefaultSlotItemsFillable
|
||||
with WindowedNode[ComputerWindow]
|
||||
{
|
||||
with WindowedNode[ComputerWindow] {
|
||||
override val iconSource: IconSource = IconSource.Nodes.Microcontroller.Default
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
@ -49,7 +48,7 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
||||
NodePort(Some(Direction.Top)),
|
||||
NodePort(Some(Direction.Back)),
|
||||
NodePort(Some(Direction.Right)),
|
||||
NodePort(Some(Direction.Left))
|
||||
NodePort(Some(Direction.Left)),
|
||||
)
|
||||
|
||||
override def getNodeByPort(port: NodePort): network.Node =
|
||||
@ -71,14 +70,14 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
||||
case Tier.One =>
|
||||
cardSlots = addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.One),
|
||||
new CardSlotWidget(_, Tier.One)
|
||||
new CardSlotWidget(_, Tier.One),
|
||||
)
|
||||
|
||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.One))
|
||||
|
||||
memorySlots = addSlotWidgets(
|
||||
new MemorySlotWidget(_, Tier.One),
|
||||
new MemorySlotWidget(_, Tier.One)
|
||||
new MemorySlotWidget(_, Tier.One),
|
||||
)
|
||||
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
@ -86,14 +85,14 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
||||
case Tier.Two =>
|
||||
cardSlots = addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Two),
|
||||
new CardSlotWidget(_, Tier.One)
|
||||
new CardSlotWidget(_, Tier.One),
|
||||
)
|
||||
|
||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.One))
|
||||
|
||||
memorySlots = addSlotWidgets(
|
||||
new MemorySlotWidget(_, Tier.One),
|
||||
new MemorySlotWidget(_, Tier.One)
|
||||
new MemorySlotWidget(_, Tier.One),
|
||||
)
|
||||
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
@ -102,14 +101,14 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
||||
case _ =>
|
||||
cardSlots = addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three)
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
)
|
||||
|
||||
cpuSlot = addSlotWidget(new ComputerCpuSlotWidget(_, this, Tier.Three))
|
||||
|
||||
memorySlots = addSlotWidgets(
|
||||
new MemorySlotWidget(_, Tier.Three),
|
||||
new MemorySlotWidget(_, Tier.Three)
|
||||
new MemorySlotWidget(_, Tier.Three),
|
||||
)
|
||||
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
@ -120,17 +119,19 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
||||
// ---------------------------- DefaultSlotItemsFillable ----------------------------
|
||||
|
||||
override def fillSlotsWithDefaultItems(): Unit = {
|
||||
cardSlots(0).item =
|
||||
cardSlots(0).item = {
|
||||
if (microcontroller.tier == Tier.Two)
|
||||
WirelessNetworkCardItem.Tier2.Factory.build()
|
||||
else
|
||||
WirelessNetworkCardItem.Tier1.Factory.build()
|
||||
}
|
||||
|
||||
cardSlots(1).item =
|
||||
cardSlots(1).item = {
|
||||
if (microcontroller.tier == Tier.Two)
|
||||
RedstoneCardItem.Tier2.Factory.build()
|
||||
else
|
||||
RedstoneCardItem.Tier1.Factory.build()
|
||||
}
|
||||
|
||||
cpuSlot.item = new CpuItem.Factory(Tier.One).build()
|
||||
|
||||
@ -145,4 +146,4 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
||||
override protected def onShiftClick(event: ClickEvent): Unit = toggleIsTurnedOn()
|
||||
|
||||
override protected def hoveredShiftStatusBarText: String = if (computer.machine.isRunning) "Turn off" else "Turn on"
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,4 +60,4 @@ object NoteBlockNode {
|
||||
("pling", "Electric Piano (Glowstone)"),
|
||||
("harp", "Harp"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Entity
|
||||
SoundBuffers.NoteBlock(event.instrument),
|
||||
SoundCategory.Records,
|
||||
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()
|
||||
|
||||
addParticle(event.pitch)
|
||||
@ -36,7 +36,8 @@ abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Entity
|
||||
override def drawParticles(g: Graphics): Unit = synchronized {
|
||||
for ((time, pitch) <- particles.reverseIterator) {
|
||||
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.filterInPlace(_._1 <= 1f)
|
||||
@ -48,4 +49,4 @@ abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Entity
|
||||
if (isHovered || isMoving)
|
||||
root.get.statusBar.addMouseEntry("icons/LMB", "Play sample")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ import ocelot.desktop.util.OcelotInterfaceLogStorage
|
||||
import ocelot.desktop.windows.OcelotInterfaceWindow
|
||||
|
||||
class OcelotBlockNode(val ocelot: OcelotBlock)
|
||||
extends EntityNode(ocelot)
|
||||
extends EntityNode(ocelot)
|
||||
with LabeledEntityNode
|
||||
with OcelotLogParticleNode
|
||||
with OcelotInterfaceLogStorage
|
||||
|
||||
@ -7,7 +7,8 @@ import ocelot.desktop.graphics.{Graphics, IconSource}
|
||||
import ocelot.desktop.node.{EntityNode, LabeledEntityNode, WindowedNode}
|
||||
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
|
||||
|
||||
private var lastTick = OcelotDesktop.ticker.tick
|
||||
@ -32,8 +33,7 @@ class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends EntityNode(openFMRad
|
||||
text = rest + first
|
||||
|
||||
animationIndex += 1
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
animationIndex = 0
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends EntityNode(openFMRad
|
||||
g.text(
|
||||
(position.x + size.width / 2) / scale - textWidth / 2,
|
||||
(position.y + 18) / scale,
|
||||
text
|
||||
text,
|
||||
)
|
||||
|
||||
g.restore()
|
||||
@ -76,4 +76,4 @@ class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends EntityNode(openFMRad
|
||||
}
|
||||
|
||||
override def createWindow(): OpenFMRadioWindow = new OpenFMRadioWindow(this)
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,10 +17,7 @@ import totoro.ocelot.brain.event.NetworkActivityEvent
|
||||
import totoro.ocelot.brain.network
|
||||
import totoro.ocelot.brain.util.Direction
|
||||
|
||||
class RackNode(val rack: Rack)
|
||||
extends ComputerAwareNode(rack)
|
||||
with WindowedNode[RackWindow]
|
||||
{
|
||||
class RackNode(val rack: Rack) extends ComputerAwareNode(rack) with WindowedNode[RackWindow] {
|
||||
override val iconSource: IconSource = IconSource.Nodes.Rack.Empty
|
||||
override def exposeAddress = false
|
||||
|
||||
@ -29,20 +26,21 @@ class RackNode(val rack: Rack)
|
||||
NodePort(Some(Direction.Top)),
|
||||
NodePort(Some(Direction.Back)),
|
||||
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 shouldReceiveEventsFor(address: String): Boolean =
|
||||
override def shouldReceiveEventsFor(address: String): Boolean = {
|
||||
super.shouldReceiveEventsFor(address) ||
|
||||
rack.inventory.entities.exists {
|
||||
case mountable: RackMountable if mountable.node.address == address => true
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
RackNode.addContextMenuEntriesOfMountable(menu, getMountableByClick(event))
|
||||
@ -91,9 +89,9 @@ class RackNode(val rack: Rack)
|
||||
x,
|
||||
y,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize
|
||||
NoHighlightSize,
|
||||
),
|
||||
driveIconSource
|
||||
driveIconSource,
|
||||
)
|
||||
|
||||
case _ =>
|
||||
@ -131,12 +129,12 @@ class RackNode(val rack: Rack)
|
||||
|
||||
val localPosition = Vector2D(
|
||||
event.mousePos.x - position.x - horizontalMargin,
|
||||
event.mousePos.y - position.y - verticalMargin
|
||||
event.mousePos.y - position.y - verticalMargin,
|
||||
)
|
||||
|
||||
val m = Size2D(
|
||||
this.width - horizontalMargin * 2,
|
||||
this.height - verticalMargin * 2
|
||||
this.height - verticalMargin * 2,
|
||||
)
|
||||
|
||||
// Checking if click was inside mountables area
|
||||
@ -207,7 +205,7 @@ object RackNode {
|
||||
"Change floppy"
|
||||
else
|
||||
"Set floppy",
|
||||
IconSource.Save
|
||||
IconSource.Save,
|
||||
) {
|
||||
diskDriveMountableItem.window.open()
|
||||
})
|
||||
@ -217,4 +215,4 @@ object RackNode {
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,14 +18,13 @@ import totoro.ocelot.brain.util.Tier
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
class RaidNode(val raid: Raid) extends
|
||||
EntityNode(raid)
|
||||
with SyncedInventory
|
||||
with LabeledEntityNode
|
||||
with DiskActivityHandler
|
||||
with DefaultSlotItemsFillable
|
||||
with WindowedNode[RaidWindow]
|
||||
{
|
||||
class RaidNode(val raid: Raid)
|
||||
extends EntityNode(raid)
|
||||
with SyncedInventory
|
||||
with LabeledEntityNode
|
||||
with DiskActivityHandler
|
||||
with DefaultSlotItemsFillable
|
||||
with WindowedNode[RaidWindow] {
|
||||
var diskSlots: Array[HddSlotWidget] = Array.tabulate(3)(index => new HddSlotWidget(Slot(index), Tier.Three))
|
||||
|
||||
override val iconSource: IconSource = IconSource.Nodes.Raid.Default
|
||||
@ -45,7 +44,9 @@ class RaidNode(val raid: Raid) extends
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@ -56,9 +57,10 @@ class RaidNode(val raid: Raid) extends
|
||||
super.shouldReceiveEventsFor(address) || raid.filesystem.exists(_.node.address == address)
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
DiskItem.addRealPathContextMenuEntries(menu, raid, realPathSetter => {
|
||||
realPathSetter()
|
||||
})
|
||||
DiskItem.addRealPathContextMenuEntries(menu, raid,
|
||||
realPathSetter => {
|
||||
realPathSetter()
|
||||
})
|
||||
|
||||
// TODO: Implement DiskDriveWindow later, because at this moment every
|
||||
// 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
|
||||
|
||||
class RelayNode(val relay: Relay)
|
||||
extends EntityNode(relay)
|
||||
with SyncedInventory
|
||||
with LabeledNode
|
||||
with WindowedNode[RelayWindow] {
|
||||
extends EntityNode(relay) with SyncedInventory with LabeledNode with WindowedNode[RelayWindow] {
|
||||
|
||||
override val iconSource: IconSource = IconSource.Nodes.Relay
|
||||
|
||||
@ -22,7 +19,8 @@ class RelayNode(val relay: Relay)
|
||||
NodePort(Some(Direction.East)),
|
||||
NodePort(Some(Direction.West)),
|
||||
NodePort(Some(Direction.Up)),
|
||||
NodePort(Some(Direction.Down)))
|
||||
NodePort(Some(Direction.Down)),
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
class ScreenNode(val screen: Screen)
|
||||
extends EntityNode(screen)
|
||||
with LabeledEntityNode
|
||||
with WindowedNode[ScreenWindow]
|
||||
with TickUpdatable {
|
||||
extends EntityNode(screen) with LabeledEntityNode with WindowedNode[ScreenWindow] with TickUpdatable {
|
||||
|
||||
// NOTE: whenever you access screen's TextBuffer methods, make sure to synchronize on `screen`:
|
||||
// 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.
|
||||
|
||||
override def minimumSize: Size2D = Size2D(
|
||||
Size * screen.aspectRatio._1,
|
||||
Size * screen.aspectRatio._2
|
||||
Size * screen.aspectRatio._2,
|
||||
)
|
||||
|
||||
override def maximumSize: Size2D = minimumSize
|
||||
@ -170,7 +166,7 @@ class ScreenNode(val screen: Screen)
|
||||
startY: Float,
|
||||
scaleX: Float,
|
||||
scaleY: Float,
|
||||
filteringMode: MinFilteringMode
|
||||
filteringMode: MinFilteringMode,
|
||||
): Unit = {
|
||||
g.save()
|
||||
g.scale(scaleX, scaleY)
|
||||
@ -196,7 +192,7 @@ class ScreenNode(val screen: Screen)
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
iconColor
|
||||
iconColor,
|
||||
)
|
||||
}
|
||||
|
||||
@ -233,7 +229,7 @@ class ScreenNode(val screen: Screen)
|
||||
position.x + HighlightThickness,
|
||||
position.y + y * Size,
|
||||
NoHighlightSize,
|
||||
Size
|
||||
Size,
|
||||
)
|
||||
}
|
||||
|
||||
@ -261,14 +257,15 @@ class ScreenNode(val screen: Screen)
|
||||
)
|
||||
|
||||
// Middle
|
||||
for (x <- 1 until aspectRatioWidth - 1)
|
||||
for (x <- 1 until aspectRatioWidth - 1) {
|
||||
drawScreenPart(
|
||||
IconSource.Nodes.Screen.RowMiddle,
|
||||
position.x + x * Size,
|
||||
position.y + HighlightThickness,
|
||||
Size,
|
||||
NoHighlightSize
|
||||
NoHighlightSize,
|
||||
)
|
||||
}
|
||||
|
||||
// Right
|
||||
drawScreenPart(
|
||||
@ -276,7 +273,7 @@ class ScreenNode(val screen: Screen)
|
||||
position.x + (aspectRatioWidth - 1) * Size,
|
||||
position.y + HighlightThickness,
|
||||
Size - HighlightThickness,
|
||||
NoHighlightSize
|
||||
NoHighlightSize,
|
||||
)
|
||||
}
|
||||
// n x n
|
||||
@ -286,7 +283,7 @@ class ScreenNode(val screen: Screen)
|
||||
height: Float,
|
||||
leftIcon: IconSource,
|
||||
middleIcon: IconSource,
|
||||
rightIcon: IconSource
|
||||
rightIcon: IconSource,
|
||||
): Unit = {
|
||||
// Left
|
||||
drawScreenPart(
|
||||
@ -294,18 +291,19 @@ class ScreenNode(val screen: Screen)
|
||||
position.x + HighlightThickness,
|
||||
y,
|
||||
Size,
|
||||
height
|
||||
height,
|
||||
)
|
||||
|
||||
// Middle
|
||||
for (x <- 1 until aspectRatioWidth - 1)
|
||||
for (x <- 1 until aspectRatioWidth - 1) {
|
||||
drawScreenPart(
|
||||
middleIcon,
|
||||
position.x + x * Size,
|
||||
y,
|
||||
Size,
|
||||
height
|
||||
height,
|
||||
)
|
||||
}
|
||||
|
||||
// Right
|
||||
drawScreenPart(
|
||||
@ -313,7 +311,7 @@ class ScreenNode(val screen: Screen)
|
||||
position.x + (aspectRatioWidth - 1) * Size - HighlightThickness,
|
||||
y,
|
||||
Size,
|
||||
height
|
||||
height,
|
||||
)
|
||||
}
|
||||
|
||||
@ -323,18 +321,19 @@ class ScreenNode(val screen: Screen)
|
||||
Size - HighlightThickness,
|
||||
IconSource.Nodes.Screen.TopLeft,
|
||||
IconSource.Nodes.Screen.TopMiddle,
|
||||
IconSource.Nodes.Screen.TopRight
|
||||
IconSource.Nodes.Screen.TopRight,
|
||||
)
|
||||
|
||||
// Middle
|
||||
for (y <- 1 until aspectRatioHeight - 1)
|
||||
for (y <- 1 until aspectRatioHeight - 1) {
|
||||
drawLine(
|
||||
position.y + (y * Size),
|
||||
Size,
|
||||
IconSource.Nodes.Screen.MiddleLeft,
|
||||
IconSource.Nodes.Screen.Middle,
|
||||
IconSource.Nodes.Screen.MiddleRight
|
||||
IconSource.Nodes.Screen.MiddleRight,
|
||||
)
|
||||
}
|
||||
|
||||
// Bottom
|
||||
drawLine(
|
||||
@ -342,7 +341,7 @@ class ScreenNode(val screen: Screen)
|
||||
Size - HighlightThickness,
|
||||
IconSource.Nodes.Screen.BottomLeft,
|
||||
IconSource.Nodes.Screen.BottomMiddle,
|
||||
IconSource.Nodes.Screen.BottomRight
|
||||
IconSource.Nodes.Screen.BottomRight,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -365,18 +364,18 @@ class ScreenNode(val screen: Screen)
|
||||
virtualScreenBounds.y,
|
||||
virtualScreenBounds.w,
|
||||
virtualScreenBounds.h,
|
||||
Color.Black
|
||||
Color.Black,
|
||||
)
|
||||
|
||||
// Calculating pixel data bounds, so that they fit perfectly into the virtual screen
|
||||
val pixelDataSize = Size2D(
|
||||
screenWidth * FontWidth,
|
||||
screenHeight * FontHeight
|
||||
screenHeight * FontHeight,
|
||||
)
|
||||
|
||||
val scale = Math.min(
|
||||
virtualScreenBounds.w / pixelDataSize.width,
|
||||
virtualScreenBounds.h / pixelDataSize.height
|
||||
virtualScreenBounds.h / pixelDataSize.height,
|
||||
)
|
||||
|
||||
// Drawing pixel data
|
||||
@ -386,7 +385,7 @@ class ScreenNode(val screen: Screen)
|
||||
virtualScreenBounds.y + virtualScreenBounds.h / 2 - (pixelDataSize.height * scale) / 2,
|
||||
scale,
|
||||
scale,
|
||||
MinFilteringMode.NearestMipmapNearest
|
||||
MinFilteringMode.NearestMipmapNearest,
|
||||
)
|
||||
}
|
||||
// Drawing simple overlay otherwise
|
||||
@ -396,7 +395,7 @@ class ScreenNode(val screen: Screen)
|
||||
position.x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize
|
||||
NoHighlightSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,19 +12,18 @@ import totoro.ocelot.brain.entity.{TapeDrive, TapeDriveState}
|
||||
import totoro.ocelot.brain.event.{TapeDriveAudioEvent, TapeDriveStopEvent}
|
||||
|
||||
class TapeDriveNode(val tapeDrive: TapeDrive)
|
||||
extends EntityNode(tapeDrive)
|
||||
extends EntityNode(tapeDrive)
|
||||
with SyncedInventory
|
||||
with LabeledEntityNode
|
||||
with PositionalSoundSourcesNode
|
||||
with WindowedNode[TapeDriveWindow]
|
||||
{
|
||||
with WindowedNode[TapeDriveWindow] {
|
||||
|
||||
override def iconSource: IconSource = IconSource.Nodes.TapeDrive
|
||||
|
||||
private lazy val soundTapeRewind: SoundSource = SoundSource.fromBuffer(
|
||||
SoundBuffers.MachineTapeRewind,
|
||||
SoundCategory.Records,
|
||||
looping = true
|
||||
looping = true,
|
||||
)
|
||||
|
||||
private lazy val streams: (SoundStream, SoundSource) = Audio.newStream(SoundCategory.Records)
|
||||
@ -53,7 +52,8 @@ class TapeDriveNode(val tapeDrive: TapeDrive)
|
||||
override def update(): Unit = {
|
||||
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) {
|
||||
soundTapeRewind.stop()
|
||||
@ -78,7 +78,6 @@ class TapeDriveNode(val tapeDrive: TapeDrive)
|
||||
|
||||
override def brainInventory: TapeDrive = tapeDrive
|
||||
|
||||
|
||||
// -------------------------------- Windowed --------------------------------
|
||||
|
||||
override protected def createWindow(): TapeDriveWindow = new TapeDriveWindow(this)
|
||||
|
||||
@ -76,12 +76,14 @@ object UiHandler extends Logging {
|
||||
|
||||
private val _clipboard = Toolkit.getDefaultToolkit.getSystemClipboard
|
||||
|
||||
def clipboard: String = try {
|
||||
_clipboard.getData(DataFlavor.stringFlavor).toString
|
||||
} catch {
|
||||
case _: UnsupportedFlavorException =>
|
||||
logger.debug("Trying to paste non-Unicode content from clipboard! Replaced with empty string.")
|
||||
""
|
||||
def clipboard: String = {
|
||||
try {
|
||||
_clipboard.getData(DataFlavor.stringFlavor).toString
|
||||
} catch {
|
||||
case _: UnsupportedFlavorException =>
|
||||
logger.debug("Trying to paste non-Unicode content from clipboard! Replaced with empty string.")
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
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 sanitizeWindowSize(size: Size2D): Size2D =
|
||||
private def sanitizeWindowSize(size: Size2D): Size2D = {
|
||||
Size2D(
|
||||
if (size.width < 10) 800 else size.width,
|
||||
if (size.height < 10) 600 else size.height,
|
||||
)
|
||||
}
|
||||
|
||||
private def sanitizeWindowGeometry(currentGeometry: Rect2D): Rect2D = {
|
||||
val Rect2D(x, y, w, h) = currentGeometry
|
||||
@ -243,21 +246,22 @@ object UiHandler extends Logging {
|
||||
val arch = System.getProperty("os.arch")
|
||||
val is64bit = arch.startsWith("amd64")
|
||||
|
||||
val libs =
|
||||
if (SystemUtils.IS_OS_WINDOWS)
|
||||
val libs = {
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
if (is64bit)
|
||||
Array("jinput-dx8_64.dll", "jinput-raw_64.dll", "jinput-wintab.dll", "lwjgl64.dll", "OpenAL64.dll")
|
||||
else
|
||||
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")
|
||||
else if (SystemUtils.IS_OS_LINUX)
|
||||
else if (SystemUtils.IS_OS_LINUX) {
|
||||
if (is64bit)
|
||||
Array("libjinput-linux64.so", "liblwjgl64.so", "libopenal64.so")
|
||||
else
|
||||
Array("libjinput-linux.so", "liblwjgl.so", "libopenal.so")
|
||||
else
|
||||
} else
|
||||
throw new Exception("Unsupported OS")
|
||||
}
|
||||
|
||||
logger.debug(s"Unpacking native libraries to: $librariesPath")
|
||||
|
||||
@ -329,13 +333,14 @@ object UiHandler extends Logging {
|
||||
UiThreadTasks.add(updateWindowTitle)
|
||||
}
|
||||
|
||||
private def updateWindowTitle(): Unit =
|
||||
private def updateWindowTitle(): Unit = {
|
||||
Display.setTitle(
|
||||
if (_windowTitleSuffix.isEmpty)
|
||||
windowTitleBase
|
||||
else
|
||||
s"$windowTitleBase - ${_windowTitleSuffix.get}"
|
||||
)
|
||||
}
|
||||
|
||||
private var exitRequested = false
|
||||
|
||||
@ -441,7 +446,7 @@ object UiHandler extends Logging {
|
||||
|
||||
ScrollEvents.events.foreach(dispatchEvent(scrollTarget))
|
||||
|
||||
for (event <- MouseEvents.events)
|
||||
for (event <- MouseEvents.events) {
|
||||
if (event.state == MouseEvent.State.Press) {
|
||||
dispatchEvent(mouseTarget)(event)
|
||||
|
||||
@ -452,6 +457,7 @@ object UiHandler extends Logging {
|
||||
} else {
|
||||
dispatchEvent(hierarchy.reverseIterator)(event)
|
||||
}
|
||||
}
|
||||
|
||||
hierarchy.reverseIterator.foreach {
|
||||
case handler: HoverHandler if !mouseTarget.contains(handler) => handler.setHovered(false)
|
||||
|
||||
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