Add scalafmt

This commit is contained in:
LeshaInc 2025-01-30 20:29:55 +00:00
parent edd9916905
commit ee4f2dcc3b
194 changed files with 2153 additions and 1888 deletions

View File

@ -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
View 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
}

View File

@ -1 +0,0 @@
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")

View File

@ -1,2 +1,2 @@
# suppress inspection "UnusedProperty" for whole file
sbt.version = 1.8.3
sbt.version = 1.10.7

View File

@ -1 +0,0 @@
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")

3
project/plugins.sbt Normal file
View 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")

View File

@ -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))
}

View File

@ -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])) {

View File

@ -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()
}

View File

@ -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,
)
}

View File

@ -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) {

View File

@ -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

View File

@ -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(())
}

View File

@ -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
}

View File

@ -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

View File

@ -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)
}

View File

@ -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,
)
}

View File

@ -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

View File

@ -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

View File

@ -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 = {

View File

@ -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")

View File

@ -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)
}

View File

@ -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,
),
)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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 ]"
}
}

View File

@ -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 = {

View File

@ -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)
}
}

View File

@ -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 = {

View File

@ -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)

View File

@ -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,
)

View File

@ -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")

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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 = {

View File

@ -21,4 +21,4 @@ class VertexBuffer[V <: Vertex] extends Buffer[V] {
ty = head.vertexType
super.extractMeta(head)
}
}
}

View File

@ -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]

View File

@ -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))
}

View File

@ -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))
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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),
)
}

View File

@ -12,4 +12,4 @@ object PrimitiveTriangles extends PrimitiveType {
object PrimitiveLines extends PrimitiveType {
override def toGL: Int = GL11.GL_LINES
}
}

View File

@ -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()

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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[_, _]]
}

View File

@ -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.
*/

View File

@ -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)),
)
}

View File

@ -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]

View File

@ -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

View File

@ -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)

View File

@ -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")
}
}

View File

@ -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

View File

@ -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()
})

View File

@ -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

View File

@ -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)

View File

@ -30,4 +30,4 @@ object MagicalMemoryItem {
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new MagicalMemoryItem(_)))
}
}
}

View File

@ -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))

View File

@ -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 {

View File

@ -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

View File

@ -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(

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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.
*/

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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,

View File

@ -27,4 +27,4 @@ object NodePort {
def fromByte(byte: Byte): NodePort = {
if (byte == -1) NodePort(None) else NodePort(Some(Direction(byte)))
}
}
}

View File

@ -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")

View File

@ -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)

View File

@ -4,4 +4,4 @@ import scala.collection.mutable
class NodeTypeGroup(val name: String) {
val types: mutable.ArrayBuffer[NodeType] = mutable.ArrayBuffer[NodeType]()
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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"
}
}

View File

@ -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

View File

@ -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)
}

View File

@ -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 _ =>

View File

@ -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"
}
}

View File

@ -60,4 +60,4 @@ object NoteBlockNode {
("pling", "Electric Piano (Glowstone)"),
("harp", "Harp"),
)
}
}

View File

@ -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")
}
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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 _ =>
}
}
}
}

View File

@ -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

View File

@ -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)

View File

@ -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,
)
}
}

View File

@ -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)

View File

@ -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