Merge branch 'develop'
@ -16,8 +16,8 @@ variables:
|
|||||||
PACKAGE_NAME: "ocelot-desktop-${CI_COMMIT_TAG}.jar"
|
PACKAGE_NAME: "ocelot-desktop-${CI_COMMIT_TAG}.jar"
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- test
|
|
||||||
- build
|
- build
|
||||||
|
- test
|
||||||
- upload
|
- upload
|
||||||
- deploy
|
- deploy
|
||||||
- release
|
- release
|
||||||
@ -29,12 +29,14 @@ test:
|
|||||||
script:
|
script:
|
||||||
- sbt test
|
- sbt test
|
||||||
|
|
||||||
scalafmt:
|
# Rest in piece.
|
||||||
stage: test
|
#scalafmt:
|
||||||
before_script:
|
# stage: test
|
||||||
- sbt -v sbtVersion
|
# allow_failure: true
|
||||||
script:
|
# before_script:
|
||||||
- sbt scalafmtCheckAll
|
# - sbt -v sbtVersion
|
||||||
|
# script:
|
||||||
|
# - sbt scalafmtCheckAll
|
||||||
|
|
||||||
build:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
|
|||||||
@ -1,24 +1,15 @@
|
|||||||
version = 3.8.6
|
version = 3.9.9
|
||||||
runner.dialect = scala213
|
runner.dialect = scala213
|
||||||
preset = default
|
preset = default
|
||||||
maxColumn = 120
|
maxColumn = 120
|
||||||
indent.defnSite = 2
|
|
||||||
|
|
||||||
align = {
|
align = {
|
||||||
preset = none
|
preset = none
|
||||||
openParenDefnSite = true
|
openParenDefnSite = false
|
||||||
}
|
}
|
||||||
|
|
||||||
newlines = {
|
newlines = {
|
||||||
source = keep
|
source = keep
|
||||||
topLevelStatementBlankLines = [
|
|
||||||
{ blanks { before = 1, after = 1, beforeAll = -1, afterAll = -1 } }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
binPack = {
|
|
||||||
preset = Oneline
|
|
||||||
literalsExclude = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rewrite = {
|
rewrite = {
|
||||||
@ -30,7 +21,15 @@ rewrite = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
docstrings = {
|
docstrings = {
|
||||||
oneline = fold
|
style = SpaceAsterisk
|
||||||
|
blankFirstLine = unfold
|
||||||
|
oneline = unfold
|
||||||
wrap = keep
|
wrap = keep
|
||||||
forceBlankLineBefore = false
|
forceBlankLineBefore = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
indent {
|
||||||
|
defnSite = 2
|
||||||
|
extendSite = 2
|
||||||
|
withSiteRelativeToExtends = 2
|
||||||
|
}
|
||||||
|
|||||||
12
build.sbt
@ -1,5 +1,5 @@
|
|||||||
name := "ocelot-desktop"
|
name := "ocelot-desktop"
|
||||||
version := "1.13.1"
|
version := "1.14.0"
|
||||||
scalaVersion := "2.13.10"
|
scalaVersion := "2.13.10"
|
||||||
|
|
||||||
lazy val root = project.in(file("."))
|
lazy val root = project.in(file("."))
|
||||||
@ -21,9 +21,9 @@ libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
|
|||||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test"
|
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test"
|
||||||
libraryDependencies += "org.scalatest" %% "scalatest-funsuite" % "3.2.19" % "test"
|
libraryDependencies += "org.scalatest" %% "scalatest-funsuite" % "3.2.19" % "test"
|
||||||
|
|
||||||
libraryDependencies += "org.apache.logging.log4j" % "log4j-core" % "2.20.0"
|
libraryDependencies += "org.apache.logging.log4j" % "log4j-core" % "2.25.1"
|
||||||
libraryDependencies += "org.apache.logging.log4j" % "log4j-api" % "2.20.0"
|
libraryDependencies += "org.apache.logging.log4j" % "log4j-api" % "2.25.1"
|
||||||
libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.20.0"
|
libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.25.1"
|
||||||
|
|
||||||
val lwjglVersion = "2.9.3"
|
val lwjglVersion = "2.9.3"
|
||||||
|
|
||||||
@ -32,7 +32,9 @@ libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion class
|
|||||||
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-windows"
|
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-windows"
|
||||||
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-osx"
|
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-osx"
|
||||||
|
|
||||||
libraryDependencies += "com.github.stephengold" % "j-ogg-all" % "1.0.3"
|
Compile / unmanagedResourceDirectories += baseDirectory.value / "lib" / "native"
|
||||||
|
|
||||||
|
libraryDependencies += "com.github.stephengold" % "j-ogg-all" % "1.0.6"
|
||||||
libraryDependencies += "com.github.wendykierp" % "JTransforms" % "3.1"
|
libraryDependencies += "com.github.wendykierp" % "JTransforms" % "3.1"
|
||||||
libraryDependencies += "com.github.sarxos" % "webcam-capture" % "0.3.12"
|
libraryDependencies += "com.github.sarxos" % "webcam-capture" % "0.3.12"
|
||||||
|
|
||||||
|
|||||||
25
doc/lwjgl-apple-silicon-compilation.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# How to compile LWJGL2 for Apple Silicon
|
||||||
|
LWJGL2 does not provide official native ARM support, therefore Ocelot uses a specially modified version by **shadowfacts**
|
||||||
|
|
||||||
|
Article: https://shadowfacts.net/2022/lwjgl-arm64/
|
||||||
|
|
||||||
|
Repository: https://github.com/shadowfacts/lwjgl2-arm64
|
||||||
|
|
||||||
|
This procedure is completely optional, as the precompiled library is already checked into the repository at `lib/native/liblwjgl-arm64.dylib`
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
This assumes that you are running macOS on **Apple Silicon** *(cross-compiling LWJGL seems to be impossible)*
|
||||||
|
|
||||||
|
1. Acquire a JDK8 built for ARM - for example, [Zulu 8 JDK](https://www.azul.com/downloads/?version=java-8-lts&os=macos&architecture=arm-64-bit&package=jdk#zulu), and add it to your `JAVA_HOME`
|
||||||
|
2. Get [`maven`](https://maven.apache.org/) and [`ant`](https://ant.apache.org/) - you may install them via `brew`, but be careful, as they will try to install a JDK as a dependency
|
||||||
|
3. Clone the LWJGL repository:
|
||||||
|
```bash
|
||||||
|
% git clone https://github.com/shadowfacts/lwjgl-arm64.git
|
||||||
|
```
|
||||||
|
4. Run the following commands in the repo:
|
||||||
|
```bash
|
||||||
|
% ant generate-all
|
||||||
|
% ant jars
|
||||||
|
% ant compile-native
|
||||||
|
```
|
||||||
|
5. Copy the compiled library from `libs/macosx/liblwjgl.dylib` into the `ocelot-desktop` project as `lib/native/liblwjgl-arm64.dylib`
|
||||||
BIN
lib/native/liblwjgl-arm64.dylib
Normal file
@ -1 +1 @@
|
|||||||
Subproject commit 4b2d2fcec19f8e238dd1c4bdb09d42b11e9bb6e6
|
Subproject commit da5dd8877035f4994b77047658561d316319b54b
|
||||||
0
spritepack/spritepack.sh
Normal file → Executable file
BIN
sprites/icons/SettingsKeymap.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
sprites/icons/SideAny.png
Normal file
|
After Width: | Height: | Size: 161 B |
BIN
sprites/icons/SideDown.png
Normal file
|
After Width: | Height: | Size: 158 B |
BIN
sprites/icons/SideEast.png
Normal file
|
After Width: | Height: | Size: 161 B |
BIN
sprites/icons/SideNone.png
Normal file
|
After Width: | Height: | Size: 178 B |
BIN
sprites/icons/SideNorth.png
Normal file
|
After Width: | Height: | Size: 154 B |
BIN
sprites/icons/SideSouth.png
Normal file
|
After Width: | Height: | Size: 150 B |
BIN
sprites/icons/SideUndefined.png
Normal file
|
After Width: | Height: | Size: 160 B |
BIN
sprites/icons/SideUp.png
Normal file
|
After Width: | Height: | Size: 154 B |
BIN
sprites/icons/SideWest.png
Normal file
|
After Width: | Height: | Size: 160 B |
BIN
sprites/particles/Smoke.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 120 B |
|
Before Width: | Height: | Size: 123 B |
|
Before Width: | Height: | Size: 167 B |
|
Before Width: | Height: | Size: 546 B |
|
Before Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 171 B |
BIN
sprites/screen/InnerBorderB.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
sprites/screen/InnerBorderT.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
sprites/screen/InnerCornerBL.png
Normal file
|
After Width: | Height: | Size: 201 B |
BIN
sprites/screen/InnerCornerBR.png
Normal file
|
After Width: | Height: | Size: 188 B |
BIN
sprites/screen/InnerCornerTL.png
Normal file
|
After Width: | Height: | Size: 188 B |
BIN
sprites/screen/InnerCornerTR.png
Normal file
|
After Width: | Height: | Size: 203 B |
BIN
sprites/screen/OuterBorderT.png
Normal file
|
After Width: | Height: | Size: 190 B |
BIN
sprites/screen/OuterCornerBL.png
Normal file
|
After Width: | Height: | Size: 219 B |
BIN
sprites/screen/OuterCornerBR.png
Normal file
|
After Width: | Height: | Size: 229 B |
BIN
sprites/screen/OuterCornerTL.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
sprites/screen/OuterCornerTR.png
Normal file
|
After Width: | Height: | Size: 228 B |
@ -11,7 +11,7 @@ AboutButtonBackgroundActive = #888888
|
|||||||
PortDown = #8382d8
|
PortDown = #8382d8
|
||||||
PortUp = #75bdc1
|
PortUp = #75bdc1
|
||||||
PortNorth = #c8ca5f
|
PortNorth = #c8ca5f
|
||||||
PortSouth = #990da3
|
PortSouth = #ed8ef4
|
||||||
PortEast = #7ec95f
|
PortEast = #7ec95f
|
||||||
PortWest = #db7d75
|
PortWest = #db7d75
|
||||||
PortAny = #9b9b9b
|
PortAny = #9b9b9b
|
||||||
@ -23,6 +23,7 @@ Tier3 = #c354cd
|
|||||||
|
|
||||||
Label = #333333
|
Label = #333333
|
||||||
LabelError = #aa0000
|
LabelError = #aa0000
|
||||||
|
LabelDisabled = #888888
|
||||||
|
|
||||||
Scrollbar = #e5e5e526
|
Scrollbar = #e5e5e526
|
||||||
ScrollbarThumb = #cc3f72
|
ScrollbarThumb = #cc3f72
|
||||||
@ -46,6 +47,8 @@ ComputerAddress = #333333
|
|||||||
|
|
||||||
ScreenOff = #000000
|
ScreenOff = #000000
|
||||||
|
|
||||||
|
WindowBackground = #c6c6c6
|
||||||
|
|
||||||
StatusBar = #181818
|
StatusBar = #181818
|
||||||
StatusBarActive = #ffffff04
|
StatusBarActive = #ffffff04
|
||||||
StatusBarBorder = #222222
|
StatusBarBorder = #222222
|
||||||
@ -190,3 +193,6 @@ Flash = #ffffff
|
|||||||
RelayTextLow = #009900
|
RelayTextLow = #009900
|
||||||
RelayTextMid = #999900
|
RelayTextMid = #999900
|
||||||
RelayTextHigh = #990000
|
RelayTextHigh = #990000
|
||||||
|
|
||||||
|
BoomCardGlowStart = #ff4010
|
||||||
|
BoomCardGlowEnd = #ff1010
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 149 KiB |
@ -1,22 +1,22 @@
|
|||||||
BackgroundPattern 0 0 304 304
|
BackgroundPattern 0 0 304 304
|
||||||
BarSegment 385 434 16 4
|
BarSegment 385 434 16 4
|
||||||
Empty 134 618 16 16
|
Empty 134 632 16 16
|
||||||
EmptySlot 237 567 18 18
|
EmptySlot 246 567 18 18
|
||||||
Knob 203 434 50 50
|
Knob 203 434 50 50
|
||||||
KnobCenter 254 434 50 50
|
KnobCenter 254 434 50 50
|
||||||
KnobLimits 305 434 50 50
|
KnobLimits 305 434 50 50
|
||||||
Loading 0 305 48 448
|
Loading 0 305 48 448
|
||||||
Logo 305 0 168 200
|
Logo 305 0 168 200
|
||||||
ShadowBorder 201 540 1 24
|
ShadowBorder 279 305 1 24
|
||||||
ShadowCorner 233 674 24 24
|
ShadowCorner 233 674 24 24
|
||||||
TabArrow 225 600 8 14
|
TabArrow 569 567 8 14
|
||||||
blocks/Generic 151 618 16 16
|
blocks/Generic 151 632 16 16
|
||||||
blocks/HologramEffect 209 549 4 4
|
blocks/HologramEffect 291 305 4 4
|
||||||
blocks/HologramProjector1Top 168 618 16 16
|
blocks/HologramProjector1Top 168 632 16 16
|
||||||
blocks/HologramProjector2Top 185 618 16 16
|
blocks/HologramProjector2Top 185 632 16 16
|
||||||
blocks/HologramProjectorSide 202 618 16 16
|
blocks/HologramProjectorSide 202 632 16 16
|
||||||
buttons/BottomDrawerClose 256 567 18 18
|
buttons/BottomDrawerClose 265 567 18 18
|
||||||
buttons/BottomDrawerOpen 275 567 18 18
|
buttons/BottomDrawerOpen 284 567 18 18
|
||||||
buttons/OpenFMRadioCloseOff 404 445 7 8
|
buttons/OpenFMRadioCloseOff 404 445 7 8
|
||||||
buttons/OpenFMRadioCloseOn 412 445 7 8
|
buttons/OpenFMRadioCloseOn 412 445 7 8
|
||||||
buttons/OpenFMRadioRedstoneOff 359 445 8 8
|
buttons/OpenFMRadioRedstoneOff 359 445 8 8
|
||||||
@ -25,303 +25,319 @@ buttons/OpenFMRadioStartOff 258 674 24 24
|
|||||||
buttons/OpenFMRadioStartOn 283 674 24 24
|
buttons/OpenFMRadioStartOn 283 674 24 24
|
||||||
buttons/OpenFMRadioStopOff 308 674 24 24
|
buttons/OpenFMRadioStopOff 308 674 24 24
|
||||||
buttons/OpenFMRadioStopOn 333 674 24 24
|
buttons/OpenFMRadioStopOn 333 674 24 24
|
||||||
buttons/OpenFMRadioVolumeDownOff 234 600 10 10
|
buttons/OpenFMRadioVolumeDownOff 578 567 10 10
|
||||||
buttons/OpenFMRadioVolumeDownOn 245 600 10 10
|
buttons/OpenFMRadioVolumeDownOn 589 567 10 10
|
||||||
buttons/OpenFMRadioVolumeUpOff 256 600 10 10
|
buttons/OpenFMRadioVolumeUpOff 600 567 10 10
|
||||||
buttons/OpenFMRadioVolumeUpOn 267 600 10 10
|
buttons/OpenFMRadioVolumeUpOn 611 567 10 10
|
||||||
buttons/PowerOff 294 567 18 18
|
buttons/PowerOff 303 567 18 18
|
||||||
buttons/PowerOn 313 567 18 18
|
buttons/PowerOn 322 567 18 18
|
||||||
buttons/RackRelayOff 134 655 65 18
|
buttons/RackRelayOff 134 655 65 18
|
||||||
buttons/RackRelayOn 200 655 65 18
|
buttons/RackRelayOn 200 655 65 18
|
||||||
icons/Antenna 219 618 16 16
|
icons/Antenna 219 632 16 16
|
||||||
icons/ArrowRight 236 618 16 16
|
icons/ArrowRight 236 632 16 16
|
||||||
icons/AspectRatio 253 618 16 16
|
icons/AspectRatio 253 632 16 16
|
||||||
icons/Book 270 618 16 16
|
icons/Book 270 632 16 16
|
||||||
icons/ButtonCheck 134 600 17 17
|
icons/ButtonCheck 143 600 17 17
|
||||||
icons/ButtonClipboard 152 600 17 17
|
icons/ButtonClipboard 161 600 17 17
|
||||||
icons/ButtonRandomize 170 600 17 17
|
icons/ButtonRandomize 179 600 17 17
|
||||||
icons/CPU 287 618 16 16
|
icons/CPU 287 632 16 16
|
||||||
icons/Card 304 618 16 16
|
icons/Card 304 632 16 16
|
||||||
icons/Close 209 600 15 14
|
icons/Close 553 567 15 14
|
||||||
icons/Code 321 618 16 16
|
icons/Code 321 632 16 16
|
||||||
icons/ComponentBus 338 618 16 16
|
icons/ComponentBus 338 632 16 16
|
||||||
icons/Copy 355 618 16 16
|
icons/Copy 355 632 16 16
|
||||||
icons/Cross 372 618 16 16
|
icons/Cross 372 632 16 16
|
||||||
icons/Delete 389 618 16 16
|
icons/Delete 389 632 16 16
|
||||||
icons/DragLMB 500 567 21 14
|
icons/DragLMB 509 567 21 14
|
||||||
icons/DragRMB 522 567 21 14
|
icons/DragRMB 531 567 21 14
|
||||||
icons/EEPROM 406 618 16 16
|
icons/EEPROM 406 632 16 16
|
||||||
icons/Edit 423 618 16 16
|
icons/Edit 423 632 16 16
|
||||||
icons/Eject 440 618 16 16
|
icons/Eject 440 632 16 16
|
||||||
icons/File 457 618 16 16
|
icons/File 457 632 16 16
|
||||||
icons/Floppy 474 618 16 16
|
icons/Floppy 474 632 16 16
|
||||||
icons/Folder 491 618 16 16
|
icons/Folder 491 632 16 16
|
||||||
icons/FolderSlash 508 618 16 16
|
icons/FolderSlash 508 632 16 16
|
||||||
icons/Grid 168 567 22 22
|
icons/Grid 177 567 22 22
|
||||||
icons/GridOff 191 567 22 22
|
icons/GridOff 200 567 22 22
|
||||||
icons/Guitar 525 618 16 16
|
icons/Guitar 525 632 16 16
|
||||||
icons/HDD 542 618 16 16
|
icons/HDD 542 632 16 16
|
||||||
icons/Help 559 618 16 16
|
icons/Help 559 632 16 16
|
||||||
icons/Home 214 567 22 22
|
icons/Home 223 567 22 22
|
||||||
icons/Keyboard 576 618 16 16
|
icons/Keyboard 576 632 16 16
|
||||||
icons/KeyboardOff 593 618 16 16
|
icons/KeyboardOff 593 632 16 16
|
||||||
icons/LMB 350 655 11 14
|
icons/LMB 298 540 11 14
|
||||||
icons/Label 610 618 16 16
|
icons/Label 610 632 16 16
|
||||||
icons/LinesHorizontal 627 618 16 16
|
icons/LinesHorizontal 627 632 16 16
|
||||||
icons/Link 644 618 16 16
|
icons/Link 644 632 16 16
|
||||||
icons/LinkSlash 661 618 16 16
|
icons/LinkSlash 661 632 16 16
|
||||||
icons/Memory 678 618 16 16
|
icons/Memory 678 632 16 16
|
||||||
icons/Microchip 695 618 16 16
|
icons/Microchip 695 632 16 16
|
||||||
icons/NA 712 618 16 16
|
icons/NA 712 632 16 16
|
||||||
icons/NotificationError 374 655 11 11
|
icons/NotificationError 322 540 11 11
|
||||||
icons/NotificationInfo 386 655 11 11
|
icons/NotificationInfo 334 540 11 11
|
||||||
icons/NotificationWarning 398 655 11 11
|
icons/NotificationWarning 346 540 11 11
|
||||||
icons/Ocelot 729 618 16 16
|
icons/Ocelot 729 632 16 16
|
||||||
icons/Pin 305 655 14 14
|
icons/Pin 253 540 14 14
|
||||||
icons/Plus 746 618 16 16
|
icons/Plus 746 632 16 16
|
||||||
icons/Power 763 618 16 16
|
icons/Power 763 632 16 16
|
||||||
icons/RMB 362 655 11 14
|
icons/RMB 310 540 11 14
|
||||||
icons/Restart 780 618 16 16
|
icons/Restart 780 632 16 16
|
||||||
icons/Save 797 618 16 16
|
icons/Save 797 632 16 16
|
||||||
icons/SaveAs 814 618 16 16
|
icons/SaveAs 814 632 16 16
|
||||||
icons/Server 831 618 16 16
|
icons/Server 831 632 16 16
|
||||||
icons/SettingsSound 266 655 12 17
|
icons/SettingsKeymap 201 540 12 17
|
||||||
icons/SettingsSystem 279 655 12 17
|
icons/SettingsSound 214 540 12 17
|
||||||
icons/SettingsUI 292 655 12 17
|
icons/SettingsSystem 227 540 12 17
|
||||||
icons/Tier0 848 618 16 16
|
icons/SettingsUI 240 540 12 17
|
||||||
icons/Tier1 865 618 16 16
|
icons/SideAny 358 540 11 11
|
||||||
icons/Tier2 882 618 16 16
|
icons/SideDown 370 540 11 11
|
||||||
icons/Tiers 899 618 16 16
|
icons/SideEast 382 540 11 11
|
||||||
icons/Unpin 320 655 14 14
|
icons/SideNone 394 540 11 11
|
||||||
icons/WaveLFSR 901 707 24 10
|
icons/SideNorth 406 540 11 11
|
||||||
icons/WaveNoise 926 707 24 10
|
icons/SideSouth 418 540 11 11
|
||||||
icons/WaveSawtooth 951 707 24 10
|
icons/SideUndefined 430 540 11 11
|
||||||
icons/WaveSine 976 707 24 10
|
icons/SideUp 442 540 11 11
|
||||||
icons/WaveSquare 134 724 24 10
|
icons/SideWest 454 540 11 11
|
||||||
icons/WaveTriangle 159 724 24 10
|
icons/Tier0 848 632 16 16
|
||||||
icons/Window 916 618 16 16
|
icons/Tier1 865 632 16 16
|
||||||
icons/WireArrowLeft 203 540 4 8
|
icons/Tier2 882 632 16 16
|
||||||
icons/WireArrowRight 208 540 4 8
|
icons/Tiers 899 632 16 16
|
||||||
|
icons/Unpin 268 540 14 14
|
||||||
|
icons/WaveLFSR 237 600 24 10
|
||||||
|
icons/WaveNoise 262 600 24 10
|
||||||
|
icons/WaveSawtooth 287 600 24 10
|
||||||
|
icons/WaveSine 312 600 24 10
|
||||||
|
icons/WaveSquare 337 600 24 10
|
||||||
|
icons/WaveTriangle 362 600 24 10
|
||||||
|
icons/Window 916 632 16 16
|
||||||
|
icons/WireArrowLeft 281 305 4 8
|
||||||
|
icons/WireArrowRight 286 305 4 8
|
||||||
items/APU0 49 655 16 96
|
items/APU0 49 655 16 96
|
||||||
items/APU1 66 655 16 96
|
items/APU1 66 655 16 96
|
||||||
items/APU2 83 655 16 96
|
items/APU2 83 655 16 96
|
||||||
items/CPU0 933 618 16 16
|
items/CPU0 933 632 16 16
|
||||||
items/CPU1 950 618 16 16
|
items/CPU1 950 632 16 16
|
||||||
items/CPU2 967 618 16 16
|
items/CPU2 967 632 16 16
|
||||||
items/CardBase 984 618 16 16
|
items/CardBase 984 632 16 16
|
||||||
items/CircuitBoard 1001 618 16 16
|
items/CircuitBoard 1001 632 16 16
|
||||||
items/ComponentBus0 134 635 16 16
|
items/ComponentBus0 358 674 16 16
|
||||||
items/ComponentBus1 151 635 16 16
|
items/ComponentBus1 375 674 16 16
|
||||||
items/ComponentBus2 168 635 16 16
|
items/ComponentBus2 392 674 16 16
|
||||||
items/ComponentBus3 185 635 16 16
|
items/ComponentBus3 409 674 16 16
|
||||||
items/DataCard0 49 526 16 128
|
items/DataCard0 49 526 16 128
|
||||||
items/DataCard1 66 526 16 128
|
items/DataCard1 66 526 16 128
|
||||||
items/DataCard2 83 526 16 128
|
items/DataCard2 83 526 16 128
|
||||||
items/DebugCard 202 635 16 16
|
items/DebugCard 426 674 16 16
|
||||||
items/DiskDriveMountable 219 635 16 16
|
items/DiskDriveMountable 443 674 16 16
|
||||||
items/EEPROM 236 635 16 16
|
items/EEPROM 460 674 16 16
|
||||||
items/FloppyDisk_dyeBlack 253 635 16 16
|
items/FloppyDisk_dyeBlack 477 674 16 16
|
||||||
items/FloppyDisk_dyeBlue 270 635 16 16
|
items/FloppyDisk_dyeBlue 494 674 16 16
|
||||||
items/FloppyDisk_dyeBrown 287 635 16 16
|
items/FloppyDisk_dyeBrown 511 674 16 16
|
||||||
items/FloppyDisk_dyeCyan 304 635 16 16
|
items/FloppyDisk_dyeCyan 528 674 16 16
|
||||||
items/FloppyDisk_dyeGray 321 635 16 16
|
items/FloppyDisk_dyeGray 545 674 16 16
|
||||||
items/FloppyDisk_dyeGreen 338 635 16 16
|
items/FloppyDisk_dyeGreen 562 674 16 16
|
||||||
items/FloppyDisk_dyeLightBlue 355 635 16 16
|
items/FloppyDisk_dyeLightBlue 579 674 16 16
|
||||||
items/FloppyDisk_dyeLightGray 372 635 16 16
|
items/FloppyDisk_dyeLightGray 596 674 16 16
|
||||||
items/FloppyDisk_dyeLime 389 635 16 16
|
items/FloppyDisk_dyeLime 613 674 16 16
|
||||||
items/FloppyDisk_dyeMagenta 406 635 16 16
|
items/FloppyDisk_dyeMagenta 630 674 16 16
|
||||||
items/FloppyDisk_dyeOrange 423 635 16 16
|
items/FloppyDisk_dyeOrange 647 674 16 16
|
||||||
items/FloppyDisk_dyePink 440 635 16 16
|
items/FloppyDisk_dyePink 664 674 16 16
|
||||||
items/FloppyDisk_dyePurple 457 635 16 16
|
items/FloppyDisk_dyePurple 681 674 16 16
|
||||||
items/FloppyDisk_dyeRed 474 635 16 16
|
items/FloppyDisk_dyeRed 698 674 16 16
|
||||||
items/FloppyDisk_dyeWhite 491 635 16 16
|
items/FloppyDisk_dyeWhite 715 674 16 16
|
||||||
items/FloppyDisk_dyeYellow 508 635 16 16
|
items/FloppyDisk_dyeYellow 732 674 16 16
|
||||||
items/GraphicsCard0 525 635 16 16
|
items/GraphicsCard0 749 674 16 16
|
||||||
items/GraphicsCard1 542 635 16 16
|
items/GraphicsCard1 766 674 16 16
|
||||||
items/GraphicsCard2 559 635 16 16
|
items/GraphicsCard2 783 674 16 16
|
||||||
items/HardDiskDrive0 576 635 16 16
|
items/HardDiskDrive0 800 674 16 16
|
||||||
items/HardDiskDrive1 593 635 16 16
|
items/HardDiskDrive1 817 674 16 16
|
||||||
items/HardDiskDrive2 610 635 16 16
|
items/HardDiskDrive2 834 674 16 16
|
||||||
items/InternetCard 134 567 16 32
|
items/InternetCard 143 567 16 32
|
||||||
items/LinkedCard 100 655 16 96
|
items/LinkedCard 100 655 16 96
|
||||||
items/Memory0 627 635 16 16
|
items/Memory0 851 674 16 16
|
||||||
items/Memory1 644 635 16 16
|
items/Memory1 868 674 16 16
|
||||||
items/Memory2 661 635 16 16
|
items/Memory2 885 674 16 16
|
||||||
items/Memory3 678 635 16 16
|
items/Memory3 902 674 16 16
|
||||||
items/Memory4 695 635 16 16
|
items/Memory4 919 674 16 16
|
||||||
items/Memory5 712 635 16 16
|
items/Memory5 936 674 16 16
|
||||||
items/Memory6 729 635 16 16
|
items/Memory6 953 674 16 16
|
||||||
items/NetworkCard 746 635 16 16
|
items/NetworkCard 970 674 16 16
|
||||||
items/OcelotCard 100 526 16 128
|
items/OcelotCard 100 526 16 128
|
||||||
items/RedstoneCard0 763 635 16 16
|
items/RedstoneCard0 987 674 16 16
|
||||||
items/RedstoneCard1 780 635 16 16
|
items/RedstoneCard1 1004 674 16 16
|
||||||
items/SelfDestructingCard 151 567 16 32
|
items/SelfDestructingCard 160 567 16 32
|
||||||
items/Server0 797 635 16 16
|
items/Server0 134 707 16 16
|
||||||
items/Server1 814 635 16 16
|
items/Server1 151 707 16 16
|
||||||
items/Server2 831 635 16 16
|
items/Server2 168 707 16 16
|
||||||
items/Server3 848 635 16 16
|
items/Server3 185 707 16 16
|
||||||
items/SoundCard 117 526 16 128
|
items/SoundCard 117 526 16 128
|
||||||
items/TapeCopper 865 635 16 16
|
items/TapeCopper 202 707 16 16
|
||||||
items/TapeDiamond 882 635 16 16
|
items/TapeDiamond 219 707 16 16
|
||||||
items/TapeGold 899 635 16 16
|
items/TapeGold 236 707 16 16
|
||||||
items/TapeGreg 916 635 16 16
|
items/TapeGreg 253 707 16 16
|
||||||
items/TapeIg 933 635 16 16
|
items/TapeIg 270 707 16 16
|
||||||
items/TapeIron 950 635 16 16
|
items/TapeIron 287 707 16 16
|
||||||
items/TapeNetherStar 967 635 16 16
|
items/TapeNetherStar 304 707 16 16
|
||||||
items/TapeSteel 984 635 16 16
|
items/TapeSteel 321 707 16 16
|
||||||
items/WirelessNetworkCard0 1001 635 16 16
|
items/WirelessNetworkCard0 338 707 16 16
|
||||||
items/WirelessNetworkCard1 358 674 16 16
|
items/WirelessNetworkCard1 355 707 16 16
|
||||||
light-panel/BookmarkLeft 882 707 18 14
|
light-panel/BookmarkLeft 218 600 18 14
|
||||||
light-panel/BookmarkRight 188 600 20 14
|
light-panel/BookmarkRight 197 600 20 14
|
||||||
light-panel/BorderB 214 549 4 4
|
light-panel/BorderB 296 305 4 4
|
||||||
light-panel/BorderL 304 549 4 2
|
light-panel/BorderL 284 314 4 2
|
||||||
light-panel/BorderR 219 549 4 4
|
light-panel/BorderR 301 305 4 4
|
||||||
light-panel/BorderT 224 549 4 4
|
light-panel/BorderT 306 305 4 4
|
||||||
light-panel/CornerBL 229 549 4 4
|
light-panel/CornerBL 311 305 4 4
|
||||||
light-panel/CornerBR 234 549 4 4
|
light-panel/CornerBR 316 305 4 4
|
||||||
light-panel/CornerTL 239 549 4 4
|
light-panel/CornerTL 321 305 4 4
|
||||||
light-panel/CornerTR 244 549 4 4
|
light-panel/CornerTR 326 305 4 4
|
||||||
light-panel/Fill 207 560 2 2
|
light-panel/Fill 410 305 2 2
|
||||||
light-panel/Vent 356 434 2 38
|
light-panel/Vent 356 434 2 38
|
||||||
nodes/Cable 377 445 8 8
|
nodes/Cable 377 445 8 8
|
||||||
nodes/Camera 375 674 16 16
|
nodes/Camera 372 707 16 16
|
||||||
nodes/Chest 335 655 14 14
|
nodes/Chest 283 540 14 14
|
||||||
nodes/HologramProjector0 392 674 16 16
|
nodes/HologramProjector0 389 707 16 16
|
||||||
nodes/HologramProjector1 409 674 16 16
|
nodes/HologramProjector1 406 707 16 16
|
||||||
nodes/IronNoteBlock 426 674 16 16
|
nodes/IronNoteBlock 423 707 16 16
|
||||||
nodes/Lamp 443 674 16 16
|
nodes/Lamp 440 707 16 16
|
||||||
nodes/LampFrame 460 674 16 16
|
nodes/LampFrame 457 707 16 16
|
||||||
nodes/LampGlow 49 305 128 128
|
nodes/LampGlow 49 305 128 128
|
||||||
nodes/NewNode 477 674 16 16
|
nodes/NewNode 474 707 16 16
|
||||||
nodes/NoteBlock 494 674 16 16
|
nodes/NoteBlock 491 707 16 16
|
||||||
nodes/OpenFMRadio 511 674 16 16
|
nodes/OpenFMRadio 508 707 16 16
|
||||||
nodes/Relay 528 674 16 16
|
nodes/Relay 525 707 16 16
|
||||||
nodes/TapeDrive 545 674 16 16
|
nodes/TapeDrive 542 707 16 16
|
||||||
nodes/computer/Default 562 674 16 16
|
nodes/computer/Default 559 707 16 16
|
||||||
nodes/computer/DiskActivity 579 674 16 16
|
nodes/computer/DiskActivity 576 707 16 16
|
||||||
nodes/computer/Error 596 674 16 16
|
nodes/computer/Error 593 707 16 16
|
||||||
nodes/computer/On 613 674 16 16
|
nodes/computer/On 610 707 16 16
|
||||||
nodes/disk-drive/Default 630 674 16 16
|
nodes/disk-drive/Default 627 707 16 16
|
||||||
nodes/disk-drive/DiskActivity 647 674 16 16
|
nodes/disk-drive/DiskActivity 644 707 16 16
|
||||||
nodes/disk-drive/Floppy 664 674 16 16
|
nodes/disk-drive/Floppy 661 707 16 16
|
||||||
nodes/holidays/Christmas 134 674 32 32
|
nodes/holidays/Christmas 134 674 32 32
|
||||||
nodes/holidays/Halloween 167 674 32 32
|
nodes/holidays/Halloween 167 674 32 32
|
||||||
nodes/holidays/Valentines 200 674 32 32
|
nodes/holidays/Valentines 200 674 32 32
|
||||||
nodes/microcontroller/Default 681 674 16 16
|
nodes/microcontroller/Default 678 707 16 16
|
||||||
nodes/microcontroller/Error 698 674 16 16
|
nodes/microcontroller/Error 695 707 16 16
|
||||||
nodes/microcontroller/On 715 674 16 16
|
nodes/microcontroller/On 712 707 16 16
|
||||||
nodes/ocelot-block/Default 117 655 16 80
|
nodes/ocelot-block/Default 117 655 16 80
|
||||||
nodes/ocelot-block/Rx 732 674 16 16
|
nodes/ocelot-block/Rx 729 707 16 16
|
||||||
nodes/ocelot-block/Tx 749 674 16 16
|
nodes/ocelot-block/Tx 746 707 16 16
|
||||||
nodes/rack/Default 766 674 16 16
|
nodes/rack/Default 763 707 16 16
|
||||||
nodes/rack/Empty 783 674 16 16
|
nodes/rack/Empty 780 707 16 16
|
||||||
nodes/rack/drive/0/Default 800 674 16 16
|
nodes/rack/drive/0/Default 797 707 16 16
|
||||||
nodes/rack/drive/0/DiskActivity 817 674 16 16
|
nodes/rack/drive/0/DiskActivity 814 707 16 16
|
||||||
nodes/rack/drive/0/Floppy 834 674 16 16
|
nodes/rack/drive/0/Floppy 831 707 16 16
|
||||||
nodes/rack/drive/1/Default 851 674 16 16
|
nodes/rack/drive/1/Default 848 707 16 16
|
||||||
nodes/rack/drive/1/DiskActivity 868 674 16 16
|
nodes/rack/drive/1/DiskActivity 865 707 16 16
|
||||||
nodes/rack/drive/1/Floppy 885 674 16 16
|
nodes/rack/drive/1/Floppy 882 707 16 16
|
||||||
nodes/rack/drive/2/Default 902 674 16 16
|
nodes/rack/drive/2/Default 899 707 16 16
|
||||||
nodes/rack/drive/2/DiskActivity 919 674 16 16
|
nodes/rack/drive/2/DiskActivity 916 707 16 16
|
||||||
nodes/rack/drive/2/Floppy 936 674 16 16
|
nodes/rack/drive/2/Floppy 933 707 16 16
|
||||||
nodes/rack/drive/3/Default 953 674 16 16
|
nodes/rack/drive/3/Default 950 707 16 16
|
||||||
nodes/rack/drive/3/DiskActivity 970 674 16 16
|
nodes/rack/drive/3/DiskActivity 967 707 16 16
|
||||||
nodes/rack/drive/3/Floppy 987 674 16 16
|
nodes/rack/drive/3/Floppy 984 707 16 16
|
||||||
nodes/rack/drive/Floppy 1004 674 16 16
|
nodes/rack/drive/Floppy 1001 707 16 16
|
||||||
nodes/rack/server/0/Default 134 707 16 16
|
nodes/rack/server/0/Default 266 655 16 16
|
||||||
nodes/rack/server/0/DiskActivity 151 707 16 16
|
nodes/rack/server/0/DiskActivity 283 655 16 16
|
||||||
nodes/rack/server/0/Error 168 707 16 16
|
nodes/rack/server/0/Error 300 655 16 16
|
||||||
nodes/rack/server/0/NetworkActivity 185 707 16 16
|
nodes/rack/server/0/NetworkActivity 317 655 16 16
|
||||||
nodes/rack/server/0/On 202 707 16 16
|
nodes/rack/server/0/On 334 655 16 16
|
||||||
nodes/rack/server/1/Default 219 707 16 16
|
nodes/rack/server/1/Default 351 655 16 16
|
||||||
nodes/rack/server/1/DiskActivity 236 707 16 16
|
nodes/rack/server/1/DiskActivity 368 655 16 16
|
||||||
nodes/rack/server/1/Error 253 707 16 16
|
nodes/rack/server/1/Error 385 655 16 16
|
||||||
nodes/rack/server/1/NetworkActivity 270 707 16 16
|
nodes/rack/server/1/NetworkActivity 402 655 16 16
|
||||||
nodes/rack/server/1/On 287 707 16 16
|
nodes/rack/server/1/On 419 655 16 16
|
||||||
nodes/rack/server/2/Default 304 707 16 16
|
nodes/rack/server/2/Default 436 655 16 16
|
||||||
nodes/rack/server/2/DiskActivity 321 707 16 16
|
nodes/rack/server/2/DiskActivity 453 655 16 16
|
||||||
nodes/rack/server/2/Error 338 707 16 16
|
nodes/rack/server/2/Error 470 655 16 16
|
||||||
nodes/rack/server/2/NetworkActivity 355 707 16 16
|
nodes/rack/server/2/NetworkActivity 487 655 16 16
|
||||||
nodes/rack/server/2/On 372 707 16 16
|
nodes/rack/server/2/On 504 655 16 16
|
||||||
nodes/rack/server/3/Default 389 707 16 16
|
nodes/rack/server/3/Default 521 655 16 16
|
||||||
nodes/rack/server/3/DiskActivity 406 707 16 16
|
nodes/rack/server/3/DiskActivity 538 655 16 16
|
||||||
nodes/rack/server/3/Error 423 707 16 16
|
nodes/rack/server/3/Error 555 655 16 16
|
||||||
nodes/rack/server/3/NetworkActivity 440 707 16 16
|
nodes/rack/server/3/NetworkActivity 572 655 16 16
|
||||||
nodes/rack/server/3/On 457 707 16 16
|
nodes/rack/server/3/On 589 655 16 16
|
||||||
nodes/raid/0/DiskActivity 474 707 16 16
|
nodes/raid/0/DiskActivity 606 655 16 16
|
||||||
nodes/raid/0/Error 491 707 16 16
|
nodes/raid/0/Error 623 655 16 16
|
||||||
nodes/raid/1/DiskActivity 508 707 16 16
|
nodes/raid/1/DiskActivity 640 655 16 16
|
||||||
nodes/raid/1/Error 525 707 16 16
|
nodes/raid/1/Error 657 655 16 16
|
||||||
nodes/raid/2/DiskActivity 542 707 16 16
|
nodes/raid/2/DiskActivity 674 655 16 16
|
||||||
nodes/raid/2/Error 559 707 16 16
|
nodes/raid/2/Error 691 655 16 16
|
||||||
nodes/raid/Default 576 707 16 16
|
nodes/raid/Default 708 655 16 16
|
||||||
nodes/screen/BottomLeft 593 707 16 16
|
nodes/screen/BottomLeft 725 655 16 16
|
||||||
nodes/screen/BottomMiddle 610 707 16 16
|
nodes/screen/BottomMiddle 742 655 16 16
|
||||||
nodes/screen/BottomRight 627 707 16 16
|
nodes/screen/BottomRight 759 655 16 16
|
||||||
nodes/screen/ColumnBottom 644 707 16 16
|
nodes/screen/ColumnBottom 776 655 16 16
|
||||||
nodes/screen/ColumnMiddle 661 707 16 16
|
nodes/screen/ColumnMiddle 793 655 16 16
|
||||||
nodes/screen/ColumnTop 678 707 16 16
|
nodes/screen/ColumnTop 810 655 16 16
|
||||||
nodes/screen/Middle 695 707 16 16
|
nodes/screen/Middle 827 655 16 16
|
||||||
nodes/screen/MiddleLeft 712 707 16 16
|
nodes/screen/MiddleLeft 844 655 16 16
|
||||||
nodes/screen/MiddleRight 729 707 16 16
|
nodes/screen/MiddleRight 861 655 16 16
|
||||||
nodes/screen/PowerOnOverlay 746 707 16 16
|
nodes/screen/PowerOnOverlay 878 655 16 16
|
||||||
nodes/screen/RowLeft 763 707 16 16
|
nodes/screen/RowLeft 895 655 16 16
|
||||||
nodes/screen/RowMiddle 780 707 16 16
|
nodes/screen/RowMiddle 912 655 16 16
|
||||||
nodes/screen/RowRight 797 707 16 16
|
nodes/screen/RowRight 929 655 16 16
|
||||||
nodes/screen/Standalone 814 707 16 16
|
nodes/screen/Standalone 946 655 16 16
|
||||||
nodes/screen/TopLeft 831 707 16 16
|
nodes/screen/TopLeft 963 655 16 16
|
||||||
nodes/screen/TopMiddle 848 707 16 16
|
nodes/screen/TopMiddle 980 655 16 16
|
||||||
nodes/screen/TopRight 865 707 16 16
|
nodes/screen/TopRight 997 655 16 16
|
||||||
panel/BorderB 249 549 4 4
|
panel/BorderB 331 305 4 4
|
||||||
panel/BorderL 309 549 4 2
|
panel/BorderL 289 314 4 2
|
||||||
panel/BorderR 254 549 4 4
|
panel/BorderR 336 305 4 4
|
||||||
panel/BorderT 259 549 4 4
|
panel/BorderT 341 305 4 4
|
||||||
panel/CornerBL 264 549 4 4
|
panel/CornerBL 346 305 4 4
|
||||||
panel/CornerBR 269 549 4 4
|
panel/CornerBR 351 305 4 4
|
||||||
panel/CornerTL 274 549 4 4
|
panel/CornerTL 356 305 4 4
|
||||||
panel/CornerTR 279 549 4 4
|
panel/CornerTR 361 305 4 4
|
||||||
panel/Fill 210 560 2 2
|
panel/Fill 413 305 2 2
|
||||||
particles/Note 377 434 7 10
|
particles/Note 377 434 7 10
|
||||||
screen/BorderB 206 549 2 8
|
particles/Smoke 134 567 8 64
|
||||||
screen/BorderT 203 549 2 10
|
screen/InnerBorderB 281 321 2 4
|
||||||
screen/CornerBL 386 445 8 8
|
screen/InnerBorderT 284 321 2 4
|
||||||
screen/CornerBR 395 445 8 8
|
screen/InnerCornerBL 366 305 4 4
|
||||||
screen/CornerTL 359 434 8 10
|
screen/InnerCornerBR 371 305 4 4
|
||||||
screen/CornerTR 368 434 8 10
|
screen/InnerCornerTL 376 305 4 4
|
||||||
window/BorderDark 203 560 1 4
|
screen/InnerCornerTR 381 305 4 4
|
||||||
window/BorderLight 205 560 1 4
|
screen/OuterBorderT 281 314 2 6
|
||||||
window/CornerBL 284 549 4 4
|
screen/OuterCornerBL 386 445 8 8
|
||||||
window/CornerBR 289 549 4 4
|
screen/OuterCornerBR 395 445 8 8
|
||||||
window/CornerTL 294 549 4 4
|
screen/OuterCornerTL 359 434 8 10
|
||||||
window/CornerTR 299 549 4 4
|
screen/OuterCornerTR 368 434 8 10
|
||||||
|
window/BorderDark 406 305 1 4
|
||||||
|
window/BorderLight 408 305 1 4
|
||||||
|
window/CornerBL 386 305 4 4
|
||||||
|
window/CornerBR 391 305 4 4
|
||||||
|
window/CornerTL 396 305 4 4
|
||||||
|
window/CornerTR 401 305 4 4
|
||||||
window/OpenFMRadio 474 0 232 105
|
window/OpenFMRadio 474 0 232 105
|
||||||
window/case/Motherboard 123 434 79 70
|
window/case/Motherboard 123 434 79 70
|
||||||
window/rack/Lines 49 434 73 91
|
window/rack/Lines 49 434 73 91
|
||||||
window/rack/Motherboard 178 305 100 78
|
window/rack/Motherboard 178 305 100 78
|
||||||
window/rack/NetworkBack 221 554 1 2
|
window/rack/NetworkBack 293 326 1 2
|
||||||
window/rack/NetworkBottom 223 554 1 2
|
window/rack/NetworkBottom 295 326 1 2
|
||||||
window/rack/NetworkConnector 225 554 1 2
|
window/rack/NetworkConnector 297 326 1 2
|
||||||
window/rack/NetworkLeft 227 554 1 2
|
window/rack/NetworkLeft 299 326 1 2
|
||||||
window/rack/NetworkRight 229 554 1 2
|
window/rack/NetworkRight 301 326 1 2
|
||||||
window/rack/NetworkTop 231 554 1 2
|
window/rack/NetworkTop 303 326 1 2
|
||||||
window/rack/NodeBack 314 549 5 1
|
window/rack/NodeBack 287 321 5 1
|
||||||
window/rack/NodeBottom 320 549 5 1
|
window/rack/NodeBottom 293 321 5 1
|
||||||
window/rack/NodeLeft 326 549 5 1
|
window/rack/NodeLeft 299 321 5 1
|
||||||
window/rack/NodeRight 332 549 5 1
|
window/rack/NodeRight 305 321 5 1
|
||||||
window/rack/NodeTop 338 549 5 1
|
window/rack/NodeTop 311 321 5 1
|
||||||
window/rack/SideBack 209 554 1 3
|
window/rack/SideBack 281 326 1 3
|
||||||
window/rack/SideBottom 211 554 1 3
|
window/rack/SideBottom 283 326 1 3
|
||||||
window/rack/SideConnector 213 554 1 3
|
window/rack/SideConnector 285 326 1 3
|
||||||
window/rack/SideLeft 215 554 1 3
|
window/rack/SideLeft 287 326 1 3
|
||||||
window/rack/SideRight 217 554 1 3
|
window/rack/SideRight 289 326 1 3
|
||||||
window/rack/SideTop 219 554 1 3
|
window/rack/SideTop 291 326 1 3
|
||||||
window/raid/Slots 134 540 66 26
|
window/raid/Slots 134 540 66 26
|
||||||
window/tape/Back 332 567 20 15
|
window/tape/Back 341 567 20 15
|
||||||
window/tape/BackPressed 353 567 20 15
|
window/tape/BackPressed 362 567 20 15
|
||||||
window/tape/Forward 374 567 20 15
|
window/tape/Forward 383 567 20 15
|
||||||
window/tape/ForwardPressed 395 567 20 15
|
window/tape/ForwardPressed 404 567 20 15
|
||||||
window/tape/Play 416 567 20 15
|
window/tape/Play 425 567 20 15
|
||||||
window/tape/PlayPressed 437 567 20 15
|
window/tape/PlayPressed 446 567 20 15
|
||||||
window/tape/Screen 134 526 146 13
|
window/tape/Screen 134 526 146 13
|
||||||
window/tape/Stop 458 567 20 15
|
window/tape/Stop 467 567 20 15
|
||||||
window/tape/StopPressed 479 567 20 15
|
window/tape/StopPressed 488 567 20 15
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#version 140
|
#version 150
|
||||||
|
|
||||||
in vec3 inPos;
|
in vec3 inPos;
|
||||||
in vec3 inNormal;
|
in vec3 inNormal;
|
||||||
|
|||||||
@ -383,6 +383,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
|
|||||||
loadWorld(nbt)
|
loadWorld(nbt)
|
||||||
|
|
||||||
resetAutosave()
|
resetAutosave()
|
||||||
|
logger.info(s"Workspace successfully loaded from: $path")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else Failure(new FileNotFoundException("Specified directory does not contain 'workspace.nbt'"))
|
} else Failure(new FileNotFoundException("Specified directory does not contain 'workspace.nbt'"))
|
||||||
|
|||||||
@ -5,11 +5,9 @@ import ocelot.desktop.Settings.ExtendedConfig
|
|||||||
import ocelot.desktop.util.{Logging, SettingsData}
|
import ocelot.desktop.util.{Logging, SettingsData}
|
||||||
import org.apache.commons.lang3.SystemUtils
|
import org.apache.commons.lang3.SystemUtils
|
||||||
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
import java.util
|
import java.util
|
||||||
import scala.io.{Codec, Source}
|
|
||||||
|
|
||||||
class Settings(val config: Config) extends SettingsData {
|
class Settings(val config: Config) extends SettingsData {
|
||||||
// TODO: refactor this mess (having to declare every field 3 times is extremely error-prone)
|
// TODO: refactor this mess (having to declare every field 3 times is extremely error-prone)
|
||||||
@ -44,9 +42,12 @@ class Settings(val config: Config) extends SettingsData {
|
|||||||
windowSize.y -= 16
|
windowSize.y -= 16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keymap.load(config.getConfig("ocelot.keymap"))
|
||||||
|
|
||||||
recentWorkspace = config.getOptionalString("ocelot.workspace.recent")
|
recentWorkspace = config.getOptionalString("ocelot.workspace.recent")
|
||||||
pinNewWindows = config.getBooleanOrElse("ocelot.workspace.pinNewWindows", default = true)
|
pinNewWindows = config.getBooleanOrElse("ocelot.workspace.pinNewWindows", default = true)
|
||||||
unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5)
|
unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5).toFloat
|
||||||
|
unfocusedWindowHide = config.getBooleanOrElse("ocelot.workspace.unfocusedWindowHide", default = false)
|
||||||
saveOnExit = config.getBooleanOrElse("ocelot.workspace.saveOnExit", default = true)
|
saveOnExit = config.getBooleanOrElse("ocelot.workspace.saveOnExit", default = true)
|
||||||
autosave = config.getBooleanOrElse("ocelot.workspace.autosave", default = true)
|
autosave = config.getBooleanOrElse("ocelot.workspace.autosave", default = true)
|
||||||
autosavePeriod = config.getIntOrElse("ocelot.workspace.autosavePeriod", default = 300)
|
autosavePeriod = config.getIntOrElse("ocelot.workspace.autosavePeriod", default = 300)
|
||||||
@ -105,6 +106,9 @@ object Settings extends Logging {
|
|||||||
|
|
||||||
def withValue(path: String, value: Option[Any]): Config =
|
def withValue(path: String, value: Option[Any]): Config =
|
||||||
config.withValue(path, ConfigValueFactory.fromAnyRef(value.orNull))
|
config.withValue(path, ConfigValueFactory.fromAnyRef(value.orNull))
|
||||||
|
|
||||||
|
def withValue(path: String, value: Int): Config =
|
||||||
|
config.withValue(path, ConfigValueFactory.fromAnyRef(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
class Int2D(var x: Int, var y: Int) {
|
class Int2D(var x: Int, var y: Int) {
|
||||||
@ -132,39 +136,18 @@ object Settings extends Logging {
|
|||||||
def get: Settings = settings
|
def get: Settings = settings
|
||||||
|
|
||||||
def load(path: Path): Unit = {
|
def load(path: Path): Unit = {
|
||||||
import java.lang.System.{lineSeparator => EOL}
|
|
||||||
|
|
||||||
if (Files.exists(path)) {
|
if (Files.exists(path)) {
|
||||||
var stream: InputStream = null
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stream = Files.newInputStream(path)
|
settings = new Settings(ConfigFactory.parseFile(path.toFile))
|
||||||
val source = Source.fromInputStream(stream)(Codec.UTF8)
|
|
||||||
val plain = source.getLines().mkString("", EOL, EOL)
|
|
||||||
val config = ConfigFactory.parseString(plain)
|
|
||||||
settings = new Settings(config)
|
|
||||||
source.close()
|
|
||||||
|
|
||||||
logger.info(s"Loaded Ocelot Desktop configuration from: $path")
|
logger.info(s"Loaded Ocelot Desktop configuration from: $path")
|
||||||
|
|
||||||
return
|
return
|
||||||
} catch {
|
} catch {
|
||||||
case _: Throwable =>
|
case t: Throwable => logger.error(s"Failed to parse $path!", t)
|
||||||
logger.info(s"Failed to parse $path, using default Ocelot Desktop configuration.")
|
|
||||||
} finally {
|
|
||||||
if (stream != null)
|
|
||||||
stream.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val defaults = {
|
logger.info(s"Using default Ocelot Desktop configuration...")
|
||||||
val in = getClass.getResourceAsStream("/ocelot/desktop/ocelot.conf")
|
settings = new Settings(ConfigFactory.parseResources("/ocelot/desktop/ocelot.conf"))
|
||||||
val config = Source.fromInputStream(in)(Codec.UTF8).getLines().mkString("", EOL, EOL)
|
|
||||||
in.close()
|
|
||||||
ConfigFactory.parseString(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
settings = new Settings(defaults)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def save(path: Path): Unit = {
|
def save(path: Path): Unit = {
|
||||||
@ -186,9 +169,11 @@ object Settings extends Logging {
|
|||||||
.withValuePreserveOrigin("ocelot.window.fullscreen", settings.windowFullscreen)
|
.withValuePreserveOrigin("ocelot.window.fullscreen", settings.windowFullscreen)
|
||||||
.withValuePreserveOrigin("ocelot.window.disableVsync", settings.disableVsync)
|
.withValuePreserveOrigin("ocelot.window.disableVsync", settings.disableVsync)
|
||||||
.withValuePreserveOrigin("ocelot.window.debugLwjgl", settings.debugLwjgl)
|
.withValuePreserveOrigin("ocelot.window.debugLwjgl", settings.debugLwjgl)
|
||||||
|
.withValue("ocelot.keymap", settings.keymap.save())
|
||||||
.withValue("ocelot.workspace.recent", settings.recentWorkspace)
|
.withValue("ocelot.workspace.recent", settings.recentWorkspace)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.pinNewWindows", settings.pinNewWindows)
|
.withValuePreserveOrigin("ocelot.workspace.pinNewWindows", settings.pinNewWindows)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowTransparency", settings.unfocusedWindowTransparency)
|
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowTransparency", settings.unfocusedWindowTransparency)
|
||||||
|
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowHide", settings.unfocusedWindowHide)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.saveOnExit", settings.saveOnExit)
|
.withValuePreserveOrigin("ocelot.workspace.saveOnExit", settings.saveOnExit)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.autosave", settings.autosave)
|
.withValuePreserveOrigin("ocelot.workspace.autosave", settings.autosave)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.autosavePeriod", settings.autosavePeriod)
|
.withValuePreserveOrigin("ocelot.workspace.autosavePeriod", settings.autosavePeriod)
|
||||||
|
|||||||
@ -21,7 +21,7 @@ object AL10W extends Logging {
|
|||||||
val exc = OpenAlException(func, errName, err)
|
val exc = OpenAlException(func, errName, err)
|
||||||
|
|
||||||
if (Settings.get.logAudioErrorStacktrace) {
|
if (Settings.get.logAudioErrorStacktrace) {
|
||||||
logger.error(exc)
|
logger.error(exc.getMessage, exc)
|
||||||
} else {
|
} else {
|
||||||
logger.error(exc.getMessage)
|
logger.error(exc.getMessage)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -246,4 +246,12 @@ object Audio extends Logging {
|
|||||||
AL10W.alDeleteBuffers(buf.get(i))
|
AL10W.alDeleteBuffers(buf.get(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def removeAllSources(): Unit = synchronized {
|
||||||
|
for (sourceId <- sources.values) {
|
||||||
|
deleteSource(sourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
sources.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,8 @@ object SoundBuffers extends Resource {
|
|||||||
lazy val MinecraftClickRelease: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click_release.ogg")
|
lazy val MinecraftClickRelease: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click_release.ogg")
|
||||||
lazy val MinecraftExplosion: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/explosion.ogg")
|
lazy val MinecraftExplosion: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/explosion.ogg")
|
||||||
|
|
||||||
|
lazy val SelfDestructingCardCountdownBeep: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/countdown_beep.ogg")
|
||||||
|
|
||||||
lazy val NoteBlock: Map[String, SoundBuffer] = List(
|
lazy val NoteBlock: Map[String, SoundBuffer] = List(
|
||||||
"banjo", "basedrum", "bass", "bell", "bit", "chime", "cow_bell", "didgeridoo", "flute", "guitar",
|
"banjo", "basedrum", "bass", "bell", "bit", "chime", "cow_bell", "didgeridoo", "flute", "guitar",
|
||||||
"harp", "hat", "iron_xylophone", "pling", "snare", "xylophone",
|
"harp", "hat", "iron_xylophone", "pling", "snare", "xylophone",
|
||||||
|
|||||||
@ -34,15 +34,15 @@ class SoundSource(
|
|||||||
Audio.getSourceStatus(this)
|
Audio.getSourceStatus(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
def isPlaying: Boolean = {
|
def playing: Boolean = {
|
||||||
status == SoundSource.Status.Playing
|
status == SoundSource.Status.Playing
|
||||||
}
|
}
|
||||||
|
|
||||||
def isPaused: Boolean = {
|
def paused: Boolean = {
|
||||||
status == SoundSource.Status.Paused
|
status == SoundSource.Status.Paused
|
||||||
}
|
}
|
||||||
|
|
||||||
def isStopped: Boolean = {
|
def stopped: Boolean = {
|
||||||
status == SoundSource.Status.Stopped
|
status == SoundSource.Status.Stopped
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +124,6 @@ object SoundSource {
|
|||||||
SoundSource.fromBuffer(SoundBuffers.MinecraftClickRelease, SoundCategory.Interface)
|
SoundSource.fromBuffer(SoundBuffers.MinecraftClickRelease, SoundCategory.Interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val MinecraftExplosion: SoundSource =
|
|
||||||
SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)
|
|
||||||
|
|
||||||
lazy val MachineFloppyInsert: SoundSource =
|
lazy val MachineFloppyInsert: SoundSource =
|
||||||
SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
|
SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package ocelot.desktop.color
|
package ocelot.desktop.color
|
||||||
|
|
||||||
|
import ocelot.desktop.geometry.FloatUtils.ExtendedFloat
|
||||||
import ocelot.desktop.geometry.Vector3D
|
import ocelot.desktop.geometry.Vector3D
|
||||||
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
@ -60,6 +61,15 @@ case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Co
|
|||||||
HSVAColor(hue, saturation, value, a)
|
HSVAColor(hue, saturation, value, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def lerp(dst: RGBAColorNorm, t: Float): RGBAColorNorm = {
|
||||||
|
RGBAColorNorm(
|
||||||
|
r.lerp(dst.r, t),
|
||||||
|
g.lerp(dst.g, t),
|
||||||
|
b.lerp(dst.b, t),
|
||||||
|
a.lerp(dst.a, t),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def withAlpha(alpha: Float): RGBAColorNorm = RGBAColorNorm(r, g, b, alpha)
|
def withAlpha(alpha: Float): RGBAColorNorm = RGBAColorNorm(r, g, b, alpha)
|
||||||
|
|
||||||
// ʕ•ᴥ•ʔ
|
// ʕ•ᴥ•ʔ
|
||||||
|
|||||||
@ -152,7 +152,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def isPlaying: Boolean =
|
def isPlaying: Boolean =
|
||||||
playbackSoundSource.isDefined && playbackSoundSource.get.isPlaying || playbackThread.isDefined
|
playbackSoundSource.isDefined && playbackSoundSource.get.playing || playbackThread.isDefined
|
||||||
|
|
||||||
@Callback()
|
@Callback()
|
||||||
def start(context: Context, args: Arguments): Array[AnyRef] =
|
def start(context: Context, args: Arguments): Array[AnyRef] =
|
||||||
|
|||||||
@ -4,6 +4,6 @@ object FloatUtils {
|
|||||||
implicit class ExtendedFloat(val v: Float) extends AnyVal {
|
implicit class ExtendedFloat(val v: Float) extends AnyVal {
|
||||||
def lerp(that: Float, alpha: Float): Float = v * (1 - alpha) + that * alpha
|
def lerp(that: Float, alpha: Float): Float = v * (1 - alpha) + that * alpha
|
||||||
|
|
||||||
def clamp(min: Float = 0f, max: Float = 1f): Float = v.min(max).max(min)
|
def clamped(min: Float = 0f, max: Float = 1f): Float = v.min(max).max(min)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,9 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
def inflate(addition: Float): Rect2D = Rect2D(x - addition, y - addition, w + addition * 2, h + addition * 2)
|
def inflated(dx: Float, dy: Float): Rect2D = Rect2D(x - dx, y - dy, w + dx * 2, h + dy * 2)
|
||||||
|
|
||||||
|
def inflated(delta: Float): Rect2D = inflated(delta, delta)
|
||||||
|
|
||||||
def center: Vector2D = {
|
def center: Vector2D = {
|
||||||
min + (size * 0.5f).toVector
|
min + (size * 0.5f).toVector
|
||||||
|
|||||||
@ -24,7 +24,7 @@ object Transform2D {
|
|||||||
Transform2D.translate(-1f, 1f) >> Transform2D.scale(2f / width, -2f / height)
|
Transform2D.translate(-1f, 1f) >> Transform2D.scale(2f / width, -2f / height)
|
||||||
|
|
||||||
def rotate(angle: Float): Transform2D = {
|
def rotate(angle: Float): Transform2D = {
|
||||||
val (s, c) = (math.sin(angle).asInstanceOf[Float], math.cos(angle).asInstanceOf[Float])
|
val (s, c) = (math.sin(angle).toFloat, math.cos(angle).toFloat)
|
||||||
|
|
||||||
// format: off
|
// format: off
|
||||||
Transform2D(
|
Transform2D(
|
||||||
|
|||||||
@ -36,9 +36,6 @@ case class Vector2D(x: Float, y: Float) extends Persistable {
|
|||||||
|
|
||||||
def *(scalar: Float): Vector2D = Vector2D(x * scalar, y * scalar)
|
def *(scalar: Float): Vector2D = Vector2D(x * scalar, y * scalar)
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
def *(scalar: Double): Vector2D = Vector2D(x * scalar, y * scalar)
|
|
||||||
|
|
||||||
def /(scalar: Float): Vector2D = Vector2D(x / scalar, y / scalar)
|
def /(scalar: Float): Vector2D = Vector2D(x / scalar, y / scalar)
|
||||||
|
|
||||||
def snap(v: Float): Vector2D = Vector2D((x / v).floor * v, (y / v).floor * v)
|
def snap(v: Float): Vector2D = Vector2D((x / v).floor * v, (y / v).floor * v)
|
||||||
|
|||||||
@ -45,7 +45,7 @@ case class Vector3D(x: Float, y: Float, z: Float) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
def angle(that: Vector3D): Float = {
|
def angle(that: Vector3D): Float = {
|
||||||
math.acos((dot(that) / length / that.length).clamp(-1, 1)).toFloat
|
math.acos((dot(that) / length / that.length).clamped(-1, 1)).toFloat
|
||||||
}
|
}
|
||||||
|
|
||||||
def lerp(that: Vector3D, alpha: Float): Vector3D = {
|
def lerp(that: Vector3D, alpha: Float): Vector3D = {
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package ocelot.desktop.graphics
|
|||||||
|
|
||||||
import ocelot.desktop.color.{Color, RGBAColorNorm}
|
import ocelot.desktop.color.{Color, RGBAColorNorm}
|
||||||
import ocelot.desktop.geometry.{Rect2D, Size2D, Transform2D, Vector2D}
|
import ocelot.desktop.geometry.{Rect2D, Size2D, Transform2D, Vector2D}
|
||||||
import ocelot.desktop.graphics.IconSource.Animation
|
|
||||||
import ocelot.desktop.graphics.Texture.MinFilteringMode
|
import ocelot.desktop.graphics.Texture.MinFilteringMode
|
||||||
import ocelot.desktop.graphics.mesh.{Mesh2D, MeshInstance2D, MeshVertex2D}
|
import ocelot.desktop.graphics.mesh.{Mesh2D, MeshInstance2D, MeshVertex2D}
|
||||||
import ocelot.desktop.graphics.render.InstanceRenderer
|
import ocelot.desktop.graphics.render.InstanceRenderer
|
||||||
@ -280,68 +279,87 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
|
|
||||||
// I hate scala. Overloaded methods with default arguments are not allowed
|
// I hate scala. Overloaded methods with default arguments are not allowed
|
||||||
def sprite(icon: IconSource, bounds: Rect2D): Unit = {
|
def sprite(icon: IconSource, bounds: Rect2D): Unit = {
|
||||||
sprite(icon.path, bounds.x, bounds.y, bounds.w, bounds.h, Color.White, icon.animation)
|
sprite(icon, bounds.x, bounds.y, bounds.w, bounds.h, Color.White)
|
||||||
|
}
|
||||||
|
|
||||||
|
def sprite(icon: IconSource, bounds: Rect2D, color: Color): Unit = {
|
||||||
|
sprite(icon, bounds.x, bounds.y, bounds.w, bounds.h, color)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sprite(icon: IconSource, pos: Vector2D, size: Size2D): Unit = {
|
def sprite(icon: IconSource, pos: Vector2D, size: Size2D): Unit = {
|
||||||
sprite(icon.path, pos.x, pos.y, size.width, size.height, Color.White, icon.animation)
|
sprite(icon, pos.x, pos.y, size.width, size.height, Color.White)
|
||||||
|
}
|
||||||
|
|
||||||
|
def sprite(icon: IconSource, pos: Vector2D, color: Color): Unit = {
|
||||||
|
sprite(icon, pos.x, pos.y, color)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sprite(icon: IconSource, pos: Vector2D, size: Size2D, color: Color): Unit = {
|
def sprite(icon: IconSource, pos: Vector2D, size: Size2D, color: Color): Unit = {
|
||||||
sprite(icon.path, pos.x, pos.y, size.width, size.height, color, icon.animation)
|
sprite(icon, pos.x, pos.y, size.width, size.height, color)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sprite(icon: IconSource, x: Float, y: Float): Unit = {
|
def sprite(icon: IconSource, x: Float, y: Float): Unit = {
|
||||||
sprite(icon.path, x, y, icon.animation)
|
sprite(icon, x, y, Color.White)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sprite(icon: IconSource, x: Float, y: Float, width: Float, height: Float): Unit = {
|
def sprite(icon: IconSource, x: Float, y: Float, width: Float, height: Float): Unit = {
|
||||||
sprite(icon.path, x, y, width, height, animation = icon.animation)
|
sprite(icon, x, y, width, height, Color.White)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sprite(icon: IconSource, x: Float, y: Float, width: Float, height: Float, color: Color): Unit = {
|
def sprite(icon: IconSource, x: Float, y: Float, color: Color): Unit = {
|
||||||
sprite(icon.path, x, y, width, height, color, icon.animation)
|
val size = Spritesheet.spriteSize(icon.path)
|
||||||
|
sprite(icon, x, y, size.width, size.height, color)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sprite(name: String, bounds: Rect2D): Unit = {
|
def sprite(
|
||||||
sprite(name, bounds.origin, bounds.size, Color.White)
|
icon: IconSource,
|
||||||
|
x: Float,
|
||||||
|
y: Float,
|
||||||
|
width: Float,
|
||||||
|
height: Float,
|
||||||
|
color: Color,
|
||||||
|
): Unit = {
|
||||||
|
sprite = icon.path
|
||||||
|
foreground = color
|
||||||
|
|
||||||
|
val spriteRect = icon.animation.map { animation =>
|
||||||
|
val duration = animation.frames.map(_._2).sum
|
||||||
|
var timeOffset = 0f
|
||||||
|
var curFrame = 0
|
||||||
|
|
||||||
|
breakable {
|
||||||
|
for ((idx, dur) <- animation.frames) {
|
||||||
|
timeOffset += dur
|
||||||
|
curFrame = idx
|
||||||
|
if (timeOffset >= time % duration) break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val size = animation.frameSize match {
|
||||||
|
case Some(size) => Size2D(this.spriteRect.w, this.spriteRect.w * size.height / size.width)
|
||||||
|
case None => Size2D(this.spriteRect.w, this.spriteRect.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.spriteRect.copy(y = this.spriteRect.y + curFrame * size.height, h = size.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
_rect(x, y, width, height, fixUV = true, spriteRect)
|
||||||
}
|
}
|
||||||
|
|
||||||
def sprite(name: String, x: Float, y: Float, color: Color): Unit = {
|
def sprite(
|
||||||
sprite(name, Vector2D(x, y), Spritesheet.spriteSize(name), color)
|
name: String,
|
||||||
}
|
x: Float,
|
||||||
|
y: Float,
|
||||||
def sprite(name: String, pos: Vector2D, color: Color): Unit = {
|
width: Float,
|
||||||
sprite(name, pos, Spritesheet.spriteSize(name), color)
|
height: Float,
|
||||||
}
|
color: Color,
|
||||||
|
spriteRect: Option[Rect2D],
|
||||||
def sprite(name: String, pos: Vector2D, size: Size2D, color: Color): Unit = {
|
fixUV: Boolean = true,
|
||||||
sprite(name, pos.x, pos.y, size.width, size.height, color)
|
): Unit = {
|
||||||
}
|
|
||||||
|
|
||||||
def sprite(name: String, pos: Vector2D, size: Size2D): Unit = {
|
|
||||||
sprite(name, pos.x, pos.y, size.width, size.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
def sprite(name: String, x: Float, y: Float): Unit = {
|
|
||||||
sprite(name, x, y, Color.White, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
def sprite(name: String, x: Float, y: Float, animation: Option[Animation]): Unit = {
|
|
||||||
sprite(name, x, y, Color.White, animation)
|
|
||||||
}
|
|
||||||
|
|
||||||
def sprite(name: String, x: Float, y: Float, color: Color, animation: Option[Animation]): Unit = {
|
|
||||||
val size = Spritesheet.spriteSize(name)
|
|
||||||
sprite(name, x, y, size.width, size.height, color, animation)
|
|
||||||
}
|
|
||||||
|
|
||||||
def sprite(name: String, x: Float, y: Float, width: Float, height: Float,
|
|
||||||
color: Color = Color.White,
|
|
||||||
animation: Option[Animation] = None): Unit = {
|
|
||||||
sprite = name
|
sprite = name
|
||||||
foreground = color
|
foreground = color
|
||||||
_rect(x, y, width, height, fixUV = true, animation)
|
|
||||||
|
_rect(x, y, width, height, fixUV, spriteRect)
|
||||||
}
|
}
|
||||||
|
|
||||||
def rect(r: Rect2D, color: Color): Unit = {
|
def rect(r: Rect2D, color: Color): Unit = {
|
||||||
@ -349,7 +367,7 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
}
|
}
|
||||||
|
|
||||||
def rect(x: Float, y: Float, width: Float, height: Float, color: Color = RGBAColorNorm(1f, 1f, 1f)): Unit = {
|
def rect(x: Float, y: Float, width: Float, height: Float, color: Color = RGBAColorNorm(1f, 1f, 1f)): Unit = {
|
||||||
sprite("Empty", x, y, width, height, color)
|
sprite(IconSource.Empty, x, y, width, height, color)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def checkFont(): Unit = {
|
private def checkFont(): Unit = {
|
||||||
@ -364,28 +382,8 @@ class Graphics(private var width: Int, private var height: Int, private var scal
|
|||||||
|
|
||||||
private def _rect(x: Float, y: Float, width: Float, height: Float,
|
private def _rect(x: Float, y: Float, width: Float, height: Float,
|
||||||
fixUV: Boolean = true,
|
fixUV: Boolean = true,
|
||||||
animation: Option[Animation] = None): Unit = {
|
spriteRectOptional: Option[Rect2D] = None): Unit = {
|
||||||
val spriteRect = animation match {
|
val spriteRect = spriteRectOptional.getOrElse(this.spriteRect)
|
||||||
case None => this.spriteRect
|
|
||||||
case Some(animation) =>
|
|
||||||
val duration = animation.frames.map(_._2).sum
|
|
||||||
var timeOffset = 0f
|
|
||||||
var curFrame = 0
|
|
||||||
|
|
||||||
breakable {
|
|
||||||
for ((idx, dur) <- animation.frames) {
|
|
||||||
timeOffset += dur
|
|
||||||
curFrame = idx
|
|
||||||
if (timeOffset >= time % duration) break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val size = animation.frameSize match {
|
|
||||||
case Some(size) => Size2D(this.spriteRect.w, this.spriteRect.w * size.height / size.width)
|
|
||||||
case None => Size2D(this.spriteRect.w, this.spriteRect.w)
|
|
||||||
}
|
|
||||||
this.spriteRect.copy(y = this.spriteRect.y + curFrame * size.height, h = size.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
val uvTransform = Transform2D.translate(spriteRect.x, spriteRect.y) >>
|
val uvTransform = Transform2D.translate(spriteRect.x, spriteRect.y) >>
|
||||||
(if (fixUV)
|
(if (fixUV)
|
||||||
|
|||||||
@ -3,96 +3,148 @@ package ocelot.desktop.graphics
|
|||||||
import ocelot.desktop.geometry.Size2D
|
import ocelot.desktop.geometry.Size2D
|
||||||
import ocelot.desktop.ui.widget.modal.notification.NotificationType.NotificationType
|
import ocelot.desktop.ui.widget.modal.notification.NotificationType.NotificationType
|
||||||
import totoro.ocelot.brain.entity.tape.Tape.{Kind => TapeKind}
|
import totoro.ocelot.brain.entity.tape.Tape.{Kind => TapeKind}
|
||||||
|
import totoro.ocelot.brain.util.Direction.{Direction, Down, East, North, South, Up, West}
|
||||||
import totoro.ocelot.brain.util.DyeColor
|
import totoro.ocelot.brain.util.DyeColor
|
||||||
import totoro.ocelot.brain.util.ExtendedTier.ExtendedTier
|
import totoro.ocelot.brain.util.ExtendedTier.ExtendedTier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
case class IconSource(
|
case class IconSource(path: String, animation: Option[IconSource.Animation] = None)
|
||||||
path: String,
|
|
||||||
animation: Option[IconSource.Animation] = None,
|
|
||||||
)
|
|
||||||
|
|
||||||
object IconSource {
|
object IconSource {
|
||||||
val CardIcon: IconSource = IconSource("icons/Card")
|
case class Animation(frames: Array[(Int, Float)], frameSize: Option[Size2D])
|
||||||
val CpuIcon: IconSource = IconSource("icons/CPU")
|
|
||||||
val HddIcon: IconSource = IconSource("icons/HDD")
|
|
||||||
val EepromIcon: IconSource = IconSource("icons/EEPROM")
|
|
||||||
val FloppyIcon: IconSource = IconSource("icons/Floppy")
|
|
||||||
val MemoryIcon: IconSource = IconSource("icons/Memory")
|
|
||||||
val ServerIcon: IconSource = IconSource("icons/Server")
|
|
||||||
val ComponentBusIcon: IconSource = IconSource("icons/ComponentBus")
|
|
||||||
|
|
||||||
val TierIcon: Tier => IconSource = { tier =>
|
object Animation {
|
||||||
IconSource(s"icons/Tier${tier.id}")
|
def apply(frames: (Int, Float)*) = new Animation(frames.toArray, None)
|
||||||
|
def apply(size: Size2D)(frames: (Int, Float)*) = new Animation(frames.toArray, Some(size))
|
||||||
|
def apply(size: Option[Size2D] = None)(frames: Array[(Int, Float)]) = new Animation(frames, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Items {
|
class IconScope(directory: String)(implicit parent: Option[IconScope]) {
|
||||||
|
// this makes nested `IconScope` declarations use `this` as their parent scope.
|
||||||
|
implicit def scope: Option[IconScope] = Some(this)
|
||||||
|
|
||||||
|
private def prefix: String = parent match {
|
||||||
|
case Some(parent) => s"${parent.prefix}/$directory"
|
||||||
|
case None => directory
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def get(name: String): IconSource = {
|
||||||
|
get(name, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def get(name: String, animation: Animation): IconSource = {
|
||||||
|
get(name, Some(animation))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def get(name: String, animation: Option[Animation]): IconSource = {
|
||||||
|
IconSource(s"$prefix/$name", animation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private implicit val scope: Option[IconScope] = None
|
||||||
|
|
||||||
|
val Empty: IconSource = IconSource("Empty")
|
||||||
|
val EmptySlot: IconSource = IconSource("EmptySlot")
|
||||||
|
val ShadowCorner: IconSource = IconSource("ShadowCorner")
|
||||||
|
val ShadowBorder: IconSource = IconSource("ShadowBorder")
|
||||||
|
val TabArrow: IconSource = IconSource("TabArrow")
|
||||||
|
val BarSegment: IconSource = IconSource("BarSegment")
|
||||||
|
val BackgroundPattern: IconSource = IconSource("BackgroundPattern")
|
||||||
|
val Logo: IconSource = IconSource("Logo")
|
||||||
|
|
||||||
|
val Knob: IconSource = IconSource("Knob")
|
||||||
|
val KnobLimits: IconSource = IconSource("KnobLimits")
|
||||||
|
val KnobCenter: IconSource = IconSource("KnobCenter")
|
||||||
|
|
||||||
|
val Loading: IconSource = IconSource(
|
||||||
|
"Loading",
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
object Items extends IconScope("items") {
|
||||||
val Cpu: Tier => IconSource = { tier =>
|
val Cpu: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/CPU${tier.id}")
|
get(s"CPU${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val Apu: Tier => IconSource = { tier =>
|
val Apu: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/APU${tier.id}", animation = Some(Animations.Apu))
|
get(s"APU${tier.id}", Animations.Apu)
|
||||||
}
|
}
|
||||||
|
|
||||||
val GraphicsCard: Tier => IconSource = { tier =>
|
val GraphicsCard: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/GraphicsCard${tier.id}")
|
get(s"GraphicsCard${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val NetworkCard: IconSource = IconSource("items/NetworkCard")
|
val NetworkCard: IconSource = get("NetworkCard")
|
||||||
|
|
||||||
val WirelessNetworkCard: Tier => IconSource = { tier =>
|
val WirelessNetworkCard: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/WirelessNetworkCard${tier.id}")
|
get(s"WirelessNetworkCard${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val LinkedCard: IconSource = IconSource("items/LinkedCard", animation = Some(Animations.LinkedCard))
|
val LinkedCard: IconSource = get("LinkedCard", Animations.LinkedCard)
|
||||||
|
|
||||||
val InternetCard: IconSource = IconSource("items/InternetCard", animation = Some(Animations.InternetCard))
|
val InternetCard: IconSource = get("InternetCard", Animations.InternetCard)
|
||||||
|
|
||||||
val RedstoneCard: Tier => IconSource = { tier =>
|
val RedstoneCard: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/RedstoneCard${tier.id}")
|
get(s"RedstoneCard${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val DataCard: Tier => IconSource = { tier =>
|
val DataCard: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/DataCard${tier.id}", animation = Some(Animations.DataCard))
|
get(s"DataCard${tier.id}", Animations.DataCard)
|
||||||
}
|
}
|
||||||
|
|
||||||
val SoundCard: IconSource = IconSource("items/SoundCard", animation = Some(Animations.DataCard))
|
val SoundCard: IconSource = get("SoundCard", Animations.DataCard)
|
||||||
|
|
||||||
val SelfDestructingCard: IconSource =
|
val SelfDestructingCard: IconSource =
|
||||||
IconSource("items/SelfDestructingCard", animation = Some(Animations.SelfDestructingCard))
|
get("SelfDestructingCard", Animations.SelfDestructingCard)
|
||||||
|
|
||||||
val OcelotCard: IconSource = IconSource("items/OcelotCard", animation = Some(Animations.OcelotCard))
|
val OcelotCard: IconSource = get("OcelotCard", Animations.OcelotCard)
|
||||||
|
|
||||||
val HardDiskDrive: Tier => IconSource = { tier =>
|
val HardDiskDrive: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/HardDiskDrive${tier.id}")
|
get(s"HardDiskDrive${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val Eeprom: IconSource = IconSource("items/EEPROM")
|
val Eeprom: IconSource = get("EEPROM")
|
||||||
|
|
||||||
val FloppyDisk: DyeColor => IconSource = { color =>
|
val FloppyDisk: DyeColor => IconSource = { color =>
|
||||||
IconSource(s"items/FloppyDisk_${color.name}")
|
get(s"FloppyDisk_${color.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val Memory: ExtendedTier => IconSource = { tier =>
|
val Memory: ExtendedTier => IconSource = { tier =>
|
||||||
IconSource(s"items/Memory${tier.id}")
|
get(s"Memory${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val Server: Tier => IconSource = { tier =>
|
val Server: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/Server${tier.id}")
|
get(s"Server${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val ComponentBus: Tier => IconSource = { tier =>
|
val ComponentBus: Tier => IconSource = { tier =>
|
||||||
IconSource(s"items/ComponentBus${tier.id}")
|
get(s"ComponentBus${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val Tape: TapeKind => IconSource = {
|
val Tape: TapeKind => IconSource = {
|
||||||
case TapeKind.Golder => Tape(TapeKind.Gold)
|
case TapeKind.Golder => Tape(TapeKind.Gold)
|
||||||
case TapeKind.NetherStarrer => Tape(TapeKind.NetherStar)
|
case TapeKind.NetherStarrer => Tape(TapeKind.NetherStar)
|
||||||
case kind => IconSource(s"items/Tape$kind")
|
case kind => get(s"Tape$kind")
|
||||||
}
|
}
|
||||||
|
|
||||||
val DiskDriveMountable: IconSource = IconSource("items/DiskDriveMountable")
|
val DiskDriveMountable: IconSource = get("DiskDriveMountable")
|
||||||
|
|
||||||
// noinspection ScalaWeakerAccess
|
// noinspection ScalaWeakerAccess
|
||||||
object Animations {
|
object Animations {
|
||||||
@ -114,223 +166,368 @@ object IconSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Animation(frames: Array[(Int, Float)], frameSize: Option[Size2D])
|
object Icons extends IconScope("icons") {
|
||||||
|
val Card: IconSource = get("Card")
|
||||||
|
val Cpu: IconSource = get("CPU")
|
||||||
|
val Hdd: IconSource = get("HDD")
|
||||||
|
val Eeprom: IconSource = get("EEPROM")
|
||||||
|
val Floppy: IconSource = get("Floppy")
|
||||||
|
val Memory: IconSource = get("Memory")
|
||||||
|
val Server: IconSource = get("Server")
|
||||||
|
val ComponentBus: IconSource = get("ComponentBus")
|
||||||
|
|
||||||
object Animation {
|
val Tier: Tier => IconSource = { tier =>
|
||||||
def apply(frames: (Int, Float)*) = new Animation(frames.toArray, None)
|
get(s"Tier${tier.id}")
|
||||||
def apply(size: Size2D, frames: (Int, Float)*) = new Animation(frames.toArray, Some(size))
|
}
|
||||||
def apply(size: Option[Size2D] = None, frames: Array[(Int, Float)]) = new Animation(frames, size)
|
|
||||||
|
val SideNone: IconSource = get("SideNone")
|
||||||
|
val SideAny: IconSource = get("SideAny")
|
||||||
|
val SideUndefined: IconSource = get("SideUndefined")
|
||||||
|
val Side: Direction => IconSource = {
|
||||||
|
case Down => get("SideDown")
|
||||||
|
case Up => get("SideUp")
|
||||||
|
case North => get("SideNorth")
|
||||||
|
case South => get("SideSouth")
|
||||||
|
case West => get("SideWest")
|
||||||
|
case East => get("SideEast")
|
||||||
|
case _ => SideUndefined
|
||||||
|
}
|
||||||
|
|
||||||
|
val Notification: NotificationType => IconSource = { notificationType =>
|
||||||
|
get(s"Notification$notificationType")
|
||||||
|
}
|
||||||
|
|
||||||
|
val NA: IconSource = get("NA")
|
||||||
|
val SettingsKeymap: IconSource = get("SettingsKeymap")
|
||||||
|
val SettingsSystem: IconSource = get("SettingsSystem")
|
||||||
|
val SettingsSound: IconSource = get("SettingsSound")
|
||||||
|
val SettingsUI: IconSource = get("SettingsUI")
|
||||||
|
val Delete: IconSource = get("Delete")
|
||||||
|
val Label: IconSource = get("Label")
|
||||||
|
val Copy: IconSource = get("Copy")
|
||||||
|
val AspectRatio: IconSource = get("AspectRatio")
|
||||||
|
val Eject: IconSource = get("Eject")
|
||||||
|
val Restart: IconSource = get("Restart")
|
||||||
|
val Edit: IconSource = get("Edit")
|
||||||
|
val Folder: IconSource = get("Folder")
|
||||||
|
val FolderSlash: IconSource = get("FolderSlash")
|
||||||
|
val Code: IconSource = get("Code")
|
||||||
|
val File: IconSource = get("File")
|
||||||
|
val Link: IconSource = get("Link")
|
||||||
|
val LinkSlash: IconSource = get("LinkSlash")
|
||||||
|
val Power: IconSource = get("Power")
|
||||||
|
val Save: IconSource = get("Save")
|
||||||
|
val SaveAs: IconSource = get("SaveAs")
|
||||||
|
val Plus: IconSource = get("Plus")
|
||||||
|
val Cross: IconSource = get("Cross")
|
||||||
|
val Microchip: IconSource = get("Microchip")
|
||||||
|
val Antenna: IconSource = get("Antenna")
|
||||||
|
val Window: IconSource = get("Window")
|
||||||
|
val Tiers: IconSource = get("Tiers")
|
||||||
|
val LinesHorizontal: IconSource = get("LinesHorizontal")
|
||||||
|
val ArrowRight: IconSource = get("ArrowRight")
|
||||||
|
val Book: IconSource = get("Book")
|
||||||
|
val Help: IconSource = get("Help")
|
||||||
|
val Ocelot: IconSource = get("Ocelot")
|
||||||
|
val Guitar: IconSource = get("Guitar")
|
||||||
|
val Keyboard: IconSource = get("Keyboard")
|
||||||
|
val KeyboardOff: IconSource = get("KeyboardOff")
|
||||||
|
val ButtonRandomize: IconSource = get("ButtonRandomize")
|
||||||
|
val ButtonClipboard: IconSource = get("ButtonClipboard")
|
||||||
|
val ButtonCheck: IconSource = get("ButtonCheck")
|
||||||
|
val Home: IconSource = get("Home")
|
||||||
|
val Pin: IconSource = get("Pin")
|
||||||
|
val Unpin: IconSource = get("Unpin")
|
||||||
|
val Close: IconSource = get("Close")
|
||||||
|
val Grid: IconSource = get("Grid")
|
||||||
|
val GridOff: IconSource = get("GridOff")
|
||||||
|
val LMB: IconSource = get("LMB")
|
||||||
|
val RMB: IconSource = get("RMB")
|
||||||
|
val DragLMB: IconSource = get("DragLMB")
|
||||||
|
val DragRMB: IconSource = get("DragRMB")
|
||||||
|
|
||||||
|
val WireArrowLeft: IconSource = get("WireArrowLeft")
|
||||||
|
val WireArrowRight: IconSource = get("WireArrowRight")
|
||||||
|
|
||||||
|
val WaveSine: IconSource = get("WaveSine")
|
||||||
|
val WaveTriangle: IconSource = get("WaveTriangle")
|
||||||
|
val WaveSawtooth: IconSource = get("WaveSawtooth")
|
||||||
|
val WaveSquare: IconSource = get("WaveSquare")
|
||||||
|
val WaveNoise: IconSource = get("WaveNoise")
|
||||||
|
val WaveLFSR: IconSource = get("WaveLFSR")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------- Ocelot interface icons -----------------------
|
object Nodes extends IconScope("nodes") {
|
||||||
|
val NewNode: IconSource = get("NewNode")
|
||||||
|
|
||||||
val Notification: NotificationType => IconSource = { notificationType =>
|
val Cable: IconSource = get("Cable")
|
||||||
IconSource(s"icons/Notification$notificationType")
|
val Camera: IconSource = get("Camera")
|
||||||
}
|
val Chest: IconSource = get("Chest")
|
||||||
|
|
||||||
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")
|
|
||||||
val SettingsUI: IconSource = IconSource("icons/SettingsUI")
|
|
||||||
val Delete: IconSource = IconSource("icons/Delete")
|
|
||||||
val Label: IconSource = IconSource("icons/Label")
|
|
||||||
val Copy: IconSource = IconSource("icons/Copy")
|
|
||||||
val AspectRatio: IconSource = IconSource("icons/AspectRatio")
|
|
||||||
val Eject: IconSource = IconSource("icons/Eject")
|
|
||||||
val Restart: IconSource = IconSource("icons/Restart")
|
|
||||||
val Edit: IconSource = IconSource("icons/Edit")
|
|
||||||
val Folder: IconSource = IconSource("icons/Folder")
|
|
||||||
val FolderSlash: IconSource = IconSource("icons/FolderSlash")
|
|
||||||
val Code: IconSource = IconSource("icons/Code")
|
|
||||||
val File: IconSource = IconSource("icons/File")
|
|
||||||
val Link: IconSource = IconSource("icons/Link")
|
|
||||||
val LinkSlash: IconSource = IconSource("icons/LinkSlash")
|
|
||||||
val Power: IconSource = IconSource("icons/Power")
|
|
||||||
val Save: IconSource = IconSource("icons/Save")
|
|
||||||
val SaveAs: IconSource = IconSource("icons/SaveAs")
|
|
||||||
val Plus: IconSource = IconSource("icons/Plus")
|
|
||||||
val Cross: IconSource = IconSource("icons/Cross")
|
|
||||||
val Microchip: IconSource = IconSource("icons/Microchip")
|
|
||||||
val Antenna: IconSource = IconSource("icons/Antenna")
|
|
||||||
val Window: IconSource = IconSource("icons/Window")
|
|
||||||
val Tiers: IconSource = IconSource("icons/Tiers")
|
|
||||||
val LinesHorizontal: IconSource = IconSource("icons/LinesHorizontal")
|
|
||||||
val ArrowRight: IconSource = IconSource("icons/ArrowRight")
|
|
||||||
val Book: IconSource = IconSource("icons/Book")
|
|
||||||
val Help: IconSource = IconSource("icons/Help")
|
|
||||||
val Ocelot: IconSource = IconSource("icons/Ocelot")
|
|
||||||
val Guitar: IconSource = IconSource("icons/Guitar")
|
|
||||||
val Keyboard: IconSource = IconSource("icons/Keyboard")
|
|
||||||
val KeyboardOff: IconSource = IconSource("icons/KeyboardOff")
|
|
||||||
|
|
||||||
// ----------------------- Node icons -----------------------
|
|
||||||
|
|
||||||
val NA: IconSource = IconSource("icons/NA")
|
|
||||||
|
|
||||||
object Nodes {
|
|
||||||
val NewNode: IconSource = IconSource("nodes/NewNode")
|
|
||||||
|
|
||||||
val Cable: IconSource = IconSource("nodes/Cable")
|
|
||||||
val Camera: IconSource = IconSource("nodes/Camera")
|
|
||||||
val Chest: IconSource = IconSource("nodes/Chest")
|
|
||||||
|
|
||||||
val HologramProjector: Tier => IconSource = { tier =>
|
val HologramProjector: Tier => IconSource = { tier =>
|
||||||
IconSource(s"nodes/HologramProjector${tier.id}")
|
get(s"HologramProjector${tier.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val IronNoteBlock: IconSource = IconSource("nodes/IronNoteBlock")
|
val IronNoteBlock: IconSource = get("IronNoteBlock")
|
||||||
val NoteBlock: IconSource = IconSource("nodes/NoteBlock")
|
val NoteBlock: IconSource = get("NoteBlock")
|
||||||
val OpenFMRadio: IconSource = IconSource("nodes/OpenFMRadio")
|
val OpenFMRadio: IconSource = get("OpenFMRadio")
|
||||||
val Relay: IconSource = IconSource("nodes/Relay")
|
val Relay: IconSource = get("Relay")
|
||||||
val TapeDrive: IconSource = IconSource("nodes/TapeDrive")
|
val TapeDrive: IconSource = get("TapeDrive")
|
||||||
|
|
||||||
object Computer extends PowerIconSource with DiskActivityIconSource {
|
val Lamp: IconSource = get("Lamp")
|
||||||
override protected def prefix: String = "nodes/computer"
|
val LampFrame: IconSource = get("LampFrame")
|
||||||
|
val LampGlow: IconSource = get("LampGlow")
|
||||||
|
|
||||||
val Default: IconSource = IconSource(s"$prefix/Default")
|
object Computer extends IconScope("computer") with PowerIconSource with DiskActivityIconSource {
|
||||||
|
val Default: IconSource = get("Default")
|
||||||
}
|
}
|
||||||
|
|
||||||
object DiskDrive extends DiskActivityIconSource with FloppyDriveIconSource {
|
object DiskDrive extends IconScope("disk-drive") with DiskActivityIconSource with FloppyDriveIconSource {
|
||||||
override protected def prefix: String = "nodes/disk-drive"
|
val Default: IconSource = get("Default")
|
||||||
|
|
||||||
val Default: IconSource = IconSource(s"$prefix/Default")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Lamp extends IconSource("nodes/Lamp") {
|
object Microcontroller extends IconScope("microcontroller") with PowerIconSource {
|
||||||
val Frame: IconSource = IconSource("nodes/LampFrame")
|
val Default: IconSource = get("Default")
|
||||||
val Glow: IconSource = IconSource("nodes/LampGlow")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Microcontroller extends PowerIconSource {
|
object OcelotBlock extends IconScope("ocelot-block") {
|
||||||
override protected def prefix: String = "nodes/microcontroller"
|
val Default: IconSource = get(
|
||||||
|
"Default",
|
||||||
val Default: IconSource = IconSource(s"$prefix/Default")
|
Some(Animation(Size2D(16, 16))((0, 30f), (1, 5f), (2, 2f), (0, 20f), (3, 3f), (4, 2f))),
|
||||||
}
|
|
||||||
|
|
||||||
object OcelotBlock {
|
|
||||||
val Default: IconSource = IconSource(
|
|
||||||
"nodes/ocelot-block/Default",
|
|
||||||
animation = Some(Animation(Size2D(16, 16), (0, 30f), (1, 5f), (2, 2f), (0, 20f), (3, 3f), (4, 2f))),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val Rx: IconSource = IconSource("nodes/ocelot-block/Rx")
|
val Rx: IconSource = get("Rx")
|
||||||
val Tx: IconSource = IconSource("nodes/ocelot-block/Tx")
|
val Tx: IconSource = get("Tx")
|
||||||
}
|
}
|
||||||
|
|
||||||
object Rack {
|
object Rack extends IconScope("rack") {
|
||||||
protected val prefix: String = "nodes/rack"
|
val Empty: IconSource = get("Empty")
|
||||||
|
val Default: IconSource = get("Default")
|
||||||
val Empty: IconSource = IconSource(s"$prefix/Empty")
|
|
||||||
val Default: IconSource = IconSource(s"$prefix/Default")
|
|
||||||
|
|
||||||
val Server: Array[Server] = Array.tabulate(4)(new Server(_))
|
val Server: Array[Server] = Array.tabulate(4)(new Server(_))
|
||||||
val Drive: Array[Drive] = Array.tabulate(4)(new Drive(_))
|
val Drive: Array[Drive] = Array.tabulate(4)(new Drive(_))
|
||||||
|
|
||||||
class Server(val slot: Int) extends PowerIconSource with DiskActivityIconSource with NetworkActivityIconSource {
|
class Server(val slot: Int)
|
||||||
override protected def prefix: String = s"${Rack.prefix}/server/$slot"
|
extends IconScope(s"server/$slot")
|
||||||
|
with PowerIconSource
|
||||||
|
with DiskActivityIconSource
|
||||||
|
with NetworkActivityIconSource {
|
||||||
|
|
||||||
val Default: IconSource = IconSource(s"$prefix/Default")
|
val Default: IconSource = get("Default")
|
||||||
}
|
}
|
||||||
|
|
||||||
class Drive(val slot: Int) extends DiskActivityIconSource with FloppyDriveIconSource {
|
class Drive(val slot: Int)
|
||||||
override protected def prefix: String = s"${Rack.prefix}/drive/$slot"
|
extends IconScope(s"drive/$slot")
|
||||||
|
with DiskActivityIconSource
|
||||||
|
with FloppyDriveIconSource {
|
||||||
|
|
||||||
val Default: IconSource = IconSource(s"$prefix/Default")
|
val Default: IconSource = get("Default")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Raid {
|
object Raid extends IconScope("raid") {
|
||||||
protected val prefix: String = "nodes/raid"
|
val Default: IconSource = get("Default")
|
||||||
|
|
||||||
val Default: IconSource = IconSource(s"$prefix/Default")
|
|
||||||
|
|
||||||
val Drive: Array[Drive] = Array.tabulate(3)(new Drive(_))
|
val Drive: Array[Drive] = Array.tabulate(3)(new Drive(_))
|
||||||
|
|
||||||
class Drive(val slot: Int) extends DiskActivityIconSource {
|
class Drive(val slot: Int) extends IconScope(slot.toString) with DiskActivityIconSource {
|
||||||
override protected def prefix: String = s"${Raid.prefix}/$slot"
|
val Error: IconSource = get("Error")
|
||||||
|
|
||||||
val Error: IconSource = IconSource(s"$prefix/Error")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Screen {
|
object Screen extends IconScope("screen") {
|
||||||
protected val prefix: String = "nodes/screen"
|
val Standalone: IconSource = get("Standalone")
|
||||||
|
val PowerOnOverlay: IconSource = get("PowerOnOverlay")
|
||||||
|
|
||||||
val Standalone: IconSource = IconSource(s"$prefix/Standalone")
|
val ColumnTop: IconSource = get("ColumnTop")
|
||||||
val PowerOnOverlay: IconSource = IconSource(s"$prefix/PowerOnOverlay")
|
val ColumnMiddle: IconSource = get("ColumnMiddle")
|
||||||
|
val ColumnBottom: IconSource = get("ColumnBottom")
|
||||||
|
|
||||||
val ColumnTop: IconSource = IconSource(s"$prefix/ColumnTop")
|
val RowLeft: IconSource = get("RowLeft")
|
||||||
val ColumnMiddle: IconSource = IconSource(s"$prefix/ColumnMiddle")
|
val RowMiddle: IconSource = get("RowMiddle")
|
||||||
val ColumnBottom: IconSource = IconSource(s"$prefix/ColumnBottom")
|
val RowRight: IconSource = get("RowRight")
|
||||||
|
|
||||||
val RowLeft: IconSource = IconSource(s"$prefix/RowLeft")
|
val TopLeft: IconSource = get("TopLeft")
|
||||||
val RowMiddle: IconSource = IconSource(s"$prefix/RowMiddle")
|
val TopMiddle: IconSource = get("TopMiddle")
|
||||||
val RowRight: IconSource = IconSource(s"$prefix/RowRight")
|
val TopRight: IconSource = get("TopRight")
|
||||||
|
|
||||||
val TopLeft: IconSource = IconSource(s"$prefix/TopLeft")
|
val MiddleLeft: IconSource = get("MiddleLeft")
|
||||||
val TopMiddle: IconSource = IconSource(s"$prefix/TopMiddle")
|
val Middle: IconSource = get("Middle")
|
||||||
val TopRight: IconSource = IconSource(s"$prefix/TopRight")
|
val MiddleRight: IconSource = get("MiddleRight")
|
||||||
|
|
||||||
val MiddleLeft: IconSource = IconSource(s"$prefix/MiddleLeft")
|
val BottomLeft: IconSource = get("BottomLeft")
|
||||||
val Middle: IconSource = IconSource(s"$prefix/Middle")
|
val BottomMiddle: IconSource = get("BottomMiddle")
|
||||||
val MiddleRight: IconSource = IconSource(s"$prefix/MiddleRight")
|
val BottomRight: IconSource = get("BottomRight")
|
||||||
|
|
||||||
val BottomLeft: IconSource = IconSource(s"$prefix/BottomLeft")
|
|
||||||
val BottomMiddle: IconSource = IconSource(s"$prefix/BottomMiddle")
|
|
||||||
val BottomRight: IconSource = IconSource(s"$prefix/BottomRight")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Holidays {
|
object Holidays extends IconScope("holidays") {
|
||||||
protected val prefix: String = "nodes/holidays"
|
val Christmas: IconSource = get("Christmas")
|
||||||
|
val Valentines: IconSource = get("Valentines")
|
||||||
val Christmas: IconSource = IconSource(s"$prefix/Christmas")
|
val Halloween: IconSource = get("Halloween")
|
||||||
val Valentines: IconSource = IconSource(s"$prefix/Valentines")
|
|
||||||
val Halloween: IconSource = IconSource(s"$prefix/Halloween")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PowerIconSource {
|
trait PowerIconSource extends IconScope {
|
||||||
protected def prefix: String
|
val On: IconSource = get("On")
|
||||||
|
val Error: IconSource = get("Error")
|
||||||
val On: IconSource = IconSource(s"$prefix/On")
|
|
||||||
val Error: IconSource = IconSource(s"$prefix/Error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait DiskActivityIconSource {
|
trait DiskActivityIconSource extends IconScope {
|
||||||
protected def prefix: String
|
val DiskActivity: IconSource = get("DiskActivity")
|
||||||
|
|
||||||
val DiskActivity: IconSource = IconSource(s"$prefix/DiskActivity")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait NetworkActivityIconSource {
|
trait NetworkActivityIconSource extends IconScope {
|
||||||
protected def prefix: String
|
val NetworkActivity: IconSource = get("NetworkActivity")
|
||||||
|
|
||||||
val NetworkActivity: IconSource = IconSource(s"$prefix/NetworkActivity")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait FloppyDriveIconSource {
|
trait FloppyDriveIconSource extends IconScope {
|
||||||
protected def prefix: String
|
val Floppy: IconSource = get("Floppy")
|
||||||
|
}
|
||||||
|
|
||||||
val Floppy: IconSource = IconSource(s"$prefix/Floppy")
|
object Screen extends IconScope("screen") {
|
||||||
|
val InnerCornerTL: IconSource = get("InnerCornerTL")
|
||||||
|
val InnerCornerTR: IconSource = get("InnerCornerTR")
|
||||||
|
val InnerCornerBL: IconSource = get("InnerCornerBL")
|
||||||
|
val InnerCornerBR: IconSource = get("InnerCornerBR")
|
||||||
|
|
||||||
|
val OuterCornerTL: IconSource = get("OuterCornerTL")
|
||||||
|
val OuterCornerTR: IconSource = get("OuterCornerTR")
|
||||||
|
val OuterCornerBL: IconSource = get("OuterCornerBL")
|
||||||
|
val OuterCornerBR: IconSource = get("OuterCornerBR")
|
||||||
|
|
||||||
|
val InnerBorderT: IconSource = get("InnerBorderT")
|
||||||
|
val OuterBorderT: IconSource = get("OuterBorderT")
|
||||||
|
|
||||||
|
val InnerBorderB: IconSource = get("InnerBorderB")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Particles extends IconScope("particles") {
|
||||||
|
val Note: IconSource = get("Note")
|
||||||
|
val Smoke: IconSource = get("Smoke")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Buttons extends IconScope("buttons") {
|
||||||
|
val BottomDrawerOpen: IconSource = get("BottomDrawerOpen")
|
||||||
|
val BottomDrawerClose: IconSource = get("BottomDrawerClose")
|
||||||
|
|
||||||
|
val PowerOff: IconSource = get("PowerOff")
|
||||||
|
val PowerOn: IconSource = get("PowerOn")
|
||||||
|
|
||||||
|
val OpenFMRadioVolumeOff: Boolean => IconSource = { isUp =>
|
||||||
|
get(s"OpenFMRadioVolume${if (isUp) "Up" else "Down"}Off")
|
||||||
|
}
|
||||||
|
|
||||||
|
val OpenFMRadioVolumeOn: Boolean => IconSource = { isUp =>
|
||||||
|
get(s"OpenFMRadioVolume${if (isUp) "Up" else "Down"}On")
|
||||||
|
}
|
||||||
|
|
||||||
|
val OpenFMRadioRedstoneOff: IconSource = get("OpenFMRadioRedstoneOff")
|
||||||
|
val OpenFMRadioRedstoneOn: IconSource = get("OpenFMRadioRedstoneOn")
|
||||||
|
|
||||||
|
val OpenFMRadioCloseOff: IconSource = get("OpenFMRadioCloseOff")
|
||||||
|
val OpenFMRadioCloseOn: IconSource = get("OpenFMRadioCloseOn")
|
||||||
|
|
||||||
|
val OpenFMRadioStartOff: IconSource = get("OpenFMRadioStartOff")
|
||||||
|
val OpenFMRadioStopOn: IconSource = get("OpenFMRadioStopOn")
|
||||||
|
|
||||||
|
val RackRelayOff: IconSource = get("RackRelayOff")
|
||||||
|
val RackRelayOn: IconSource = get("RackRelayOn")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Window extends IconScope("window") {
|
||||||
|
val CornerTL: IconSource = get("CornerTL")
|
||||||
|
val CornerTR: IconSource = get("CornerTR")
|
||||||
|
val CornerBL: IconSource = get("CornerBL")
|
||||||
|
val CornerBR: IconSource = get("CornerBR")
|
||||||
|
|
||||||
|
val BorderLight: IconSource = get("BorderLight")
|
||||||
|
val BorderDark: IconSource = get("BorderDark")
|
||||||
|
|
||||||
|
object Tape extends IconScope("tape") {
|
||||||
|
abstract class TapeButtonIconSource private[Tape] (sprite: String) {
|
||||||
|
val Released: IconSource = get(sprite)
|
||||||
|
val Pressed: IconSource = get(s"${sprite}Pressed")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Back extends TapeButtonIconSource("Back")
|
||||||
|
object Play extends TapeButtonIconSource("Play")
|
||||||
|
object Stop extends TapeButtonIconSource("Stop")
|
||||||
|
object Forward extends TapeButtonIconSource("Forward")
|
||||||
|
|
||||||
|
val Screen: IconSource = get("Screen")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Case extends IconScope("case") {
|
||||||
|
val Motherboard: IconSource = get("Motherboard")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Rack extends IconScope("rack") {
|
||||||
|
val Motherboard: IconSource = get("Motherboard")
|
||||||
|
val Lines: IconSource = get("Lines")
|
||||||
|
|
||||||
|
trait DirectionIconSource {
|
||||||
|
protected def iconPrefix: String
|
||||||
|
|
||||||
|
val DirectionIcon: Direction => IconSource = { direction =>
|
||||||
|
get(s"$iconPrefix${direction.side.capitalize}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ConnectorIconSource {
|
||||||
|
protected def iconPrefix: String
|
||||||
|
|
||||||
|
val Connector: IconSource = get(s"${iconPrefix}Connector")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Side extends DirectionIconSource with ConnectorIconSource {
|
||||||
|
override protected def iconPrefix: String = "Side"
|
||||||
|
}
|
||||||
|
|
||||||
|
object Network extends DirectionIconSource with ConnectorIconSource {
|
||||||
|
override protected def iconPrefix: String = "Network"
|
||||||
|
}
|
||||||
|
|
||||||
|
object Node extends DirectionIconSource {
|
||||||
|
override protected def iconPrefix: String = "Node"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Raid extends IconScope("raid") {
|
||||||
|
val Slots: IconSource = get("Slots")
|
||||||
|
}
|
||||||
|
|
||||||
|
val OpenFMRadio: IconSource = get("OpenFMRadio")
|
||||||
|
}
|
||||||
|
|
||||||
|
object Panel extends IconScope("panel") {
|
||||||
|
val CornerTL: IconSource = get("CornerTL")
|
||||||
|
val CornerTR: IconSource = get("CornerTR")
|
||||||
|
val CornerBL: IconSource = get("CornerBL")
|
||||||
|
val CornerBR: IconSource = get("CornerBR")
|
||||||
|
|
||||||
|
val BorderT: IconSource = get("BorderT")
|
||||||
|
val BorderB: IconSource = get("BorderB")
|
||||||
|
val BorderL: IconSource = get("BorderL")
|
||||||
|
val BorderR: IconSource = get("BorderR")
|
||||||
|
|
||||||
|
val Fill: IconSource = get("Fill")
|
||||||
|
}
|
||||||
|
|
||||||
|
object LightPanel extends IconScope("light-panel") {
|
||||||
|
val CornerTL: IconSource = get("CornerTL")
|
||||||
|
val CornerTR: IconSource = get("CornerTR")
|
||||||
|
val CornerBL: IconSource = get("CornerBL")
|
||||||
|
val CornerBR: IconSource = get("CornerBR")
|
||||||
|
|
||||||
|
val BorderT: IconSource = get("BorderT")
|
||||||
|
val BorderB: IconSource = get("BorderB")
|
||||||
|
val BorderL: IconSource = get("BorderL")
|
||||||
|
val BorderR: IconSource = get("BorderR")
|
||||||
|
|
||||||
|
val Fill: IconSource = get("Fill")
|
||||||
|
val Vent: IconSource = get("Vent")
|
||||||
|
|
||||||
|
val BookmarkLeft: IconSource = get("BookmarkLeft")
|
||||||
|
val BookmarkRight: IconSource = get("BookmarkRight")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,22 @@
|
|||||||
package ocelot.desktop.inventory
|
package ocelot.desktop.inventory
|
||||||
|
|
||||||
import ocelot.desktop.inventory.Inventory.SlotObserver
|
import ocelot.desktop.inventory.Inventory.SlotObserver
|
||||||
import ocelot.desktop.ui.event.{Event, EventAware}
|
import ocelot.desktop.ui.event.{BrainEvent, Dispatchable, EventAware}
|
||||||
|
import ocelot.desktop.util.Disposable
|
||||||
import totoro.ocelot.brain.event.NodeEvent
|
import totoro.ocelot.brain.event.NodeEvent
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
/** Provides an inventory — a collection of [[Item]]s indexed by slots. */
|
/**
|
||||||
trait Inventory extends EventAware {
|
* Provides an inventory — a collection of [[Item]]s indexed by slots.
|
||||||
|
*/
|
||||||
|
trait Inventory extends EventAware with Disposable {
|
||||||
// parallels totoro.ocelot.brain.entity.traits.Inventory
|
// parallels totoro.ocelot.brain.entity.traits.Inventory
|
||||||
// this is intentional
|
// this is intentional
|
||||||
|
|
||||||
/** The type of items stored in this inventory. */
|
/**
|
||||||
|
* The type of items stored in this inventory.
|
||||||
|
*/
|
||||||
type I <: Item
|
type I <: Item
|
||||||
|
|
||||||
private type WeakHashSet[A] = mutable.WeakHashMap[A, Unit]
|
private type WeakHashSet[A] = mutable.WeakHashMap[A, Unit]
|
||||||
@ -20,13 +25,20 @@ trait Inventory extends EventAware {
|
|||||||
private val itemSlots = mutable.HashMap.empty[I, Int]
|
private val itemSlots = mutable.HashMap.empty[I, Int]
|
||||||
private val observers = mutable.HashMap.empty[Int, WeakHashSet[SlotObserver]]
|
private val observers = mutable.HashMap.empty[Int, WeakHashSet[SlotObserver]]
|
||||||
|
|
||||||
/** Called after a new item is added to the inventory.
|
override def dispose(): Unit = {
|
||||||
|
super.dispose()
|
||||||
|
inventoryIterator.foreach(_.removeAndDispose())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after a new item is added to the inventory.
|
||||||
*
|
*
|
||||||
* @param slot the slot the item was added to
|
* @param slot the slot the item was added to
|
||||||
*/
|
*/
|
||||||
def onItemAdded(slot: Slot): Unit
|
def onItemAdded(slot: Slot): Unit
|
||||||
|
|
||||||
/** Called after an item is removed from the inventory.
|
/**
|
||||||
|
* Called after an item is removed from the inventory.
|
||||||
*
|
*
|
||||||
* When the item is replaced by another one, the event are sequenced in the following order:
|
* When the item is replaced by another one, the event are sequenced in the following order:
|
||||||
*
|
*
|
||||||
@ -41,7 +53,9 @@ trait Inventory extends EventAware {
|
|||||||
*/
|
*/
|
||||||
def onItemRemoved(slot: Slot, removedItem: I, replacedBy: Option[I]): Unit
|
def onItemRemoved(slot: Slot, removedItem: I, replacedBy: Option[I]): Unit
|
||||||
|
|
||||||
/** An iterator over all slots occupied in this inventory. */
|
/**
|
||||||
|
* An iterator over all slots occupied in this inventory.
|
||||||
|
*/
|
||||||
def inventoryIterator: Iterator[Slot] = slotItems.keysIterator.map(Slot(_))
|
def inventoryIterator: Iterator[Slot] = slotItems.keysIterator.map(Slot(_))
|
||||||
|
|
||||||
def clearInventory(): Unit = {
|
def clearInventory(): Unit = {
|
||||||
@ -67,18 +81,20 @@ trait Inventory extends EventAware {
|
|||||||
super.shouldReceiveEventsFor(address) ||
|
super.shouldReceiveEventsFor(address) ||
|
||||||
inventoryIterator.flatMap(_.get).exists(_.shouldReceiveEventsFor(address))
|
inventoryIterator.flatMap(_.get).exists(_.shouldReceiveEventsFor(address))
|
||||||
|
|
||||||
override def handleEvent(event: Event): Unit = {
|
override def handleEvent(event: Dispatchable): Unit = {
|
||||||
super.handleEvent(event)
|
super.handleEvent(event)
|
||||||
|
|
||||||
for (slot <- inventoryIterator; item <- slot.get) {
|
for (slot <- inventoryIterator; item <- slot.get) {
|
||||||
event match {
|
event match {
|
||||||
case n: NodeEvent if !item.shouldReceiveEventsFor(n.address) => // ignore
|
case BrainEvent(e: NodeEvent) if !item.shouldReceiveEventsFor(e.address) => // ignore
|
||||||
case _ => item.handleEvent(event)
|
case _ => item.handleEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A proxy to access a slot of the inventory. */
|
/**
|
||||||
|
* A proxy to access a slot of the inventory.
|
||||||
|
*/
|
||||||
final class Slot private[Inventory] (val index: Int) {
|
final class Slot private[Inventory] (val index: Int) {
|
||||||
require(index >= 0)
|
require(index >= 0)
|
||||||
|
|
||||||
@ -86,27 +102,46 @@ trait Inventory extends EventAware {
|
|||||||
|
|
||||||
def nonEmpty: Boolean = !isEmpty
|
def nonEmpty: Boolean = !isEmpty
|
||||||
|
|
||||||
/** Inserts the `item` into this slot (replacing the previous item if any). */
|
/**
|
||||||
|
* Inserts the `item` into this slot (replacing the previous item if any).
|
||||||
|
*/
|
||||||
def put(item: inventory.I): Unit = {
|
def put(item: inventory.I): Unit = {
|
||||||
setSlot(index, Some(item))
|
setSlot(index, Some(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allows inserting/removing the item in this slot. */
|
/**
|
||||||
|
* Allows inserting/removing the item in this slot.
|
||||||
|
*/
|
||||||
def set(item: Option[inventory.I]): Unit = {
|
def set(item: Option[inventory.I]): Unit = {
|
||||||
setSlot(index, item)
|
setSlot(index, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes the item contained in this slot if there is one. */
|
/**
|
||||||
|
* Removes the item contained in this slot if there is one.
|
||||||
|
*/
|
||||||
def remove(): Unit = {
|
def remove(): Unit = {
|
||||||
setSlot(index, None)
|
setSlot(index, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The [[Item]] contained in this slot. */
|
/**
|
||||||
|
* Removes the item contained in this slot if there is one, calling the item's [[dispose]] method.
|
||||||
|
*/
|
||||||
|
def removeAndDispose(): Unit = {
|
||||||
|
for (item <- get) {
|
||||||
|
remove()
|
||||||
|
item.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [[Item]] contained in this slot.
|
||||||
|
*/
|
||||||
def get: Option[inventory.I] = slotItems.get(index)
|
def get: Option[inventory.I] = slotItems.get(index)
|
||||||
|
|
||||||
val inventory: Inventory.this.type = Inventory.this
|
val inventory: Inventory.this.type = Inventory.this
|
||||||
|
|
||||||
/** Registers an observer to receive item added/removed events.
|
/**
|
||||||
|
* Registers an observer to receive item added/removed events.
|
||||||
*
|
*
|
||||||
* @note The inventory keeps a '''weak''' reference to the `observer`.
|
* @note The inventory keeps a '''weak''' reference to the `observer`.
|
||||||
*/
|
*/
|
||||||
@ -148,8 +183,9 @@ trait Inventory extends EventAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final object Slot {
|
final object Slot {
|
||||||
|
/**
|
||||||
/** Creates a proxy to an inventory slot. */
|
* Creates a proxy to an inventory slot.
|
||||||
|
*/
|
||||||
def apply(index: Int) = new Slot(index)
|
def apply(index: Int) = new Slot(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,13 +242,15 @@ trait Inventory extends EventAware {
|
|||||||
object Inventory {
|
object Inventory {
|
||||||
trait SlotObserver {
|
trait SlotObserver {
|
||||||
|
|
||||||
/** Called after an item was inserted into this slot.
|
/**
|
||||||
|
* Called after an item was inserted into this slot.
|
||||||
*
|
*
|
||||||
* @note [[Inventory.onItemAdded]] is called before this method.
|
* @note [[Inventory.onItemAdded]] is called before this method.
|
||||||
*/
|
*/
|
||||||
def onItemAdded(): Unit
|
def onItemAdded(): Unit
|
||||||
|
|
||||||
/** Called after an item was removed from this slot.
|
/**
|
||||||
|
* Called after an item was removed from this slot.
|
||||||
*
|
*
|
||||||
* In particular, the slot no longer contains the removed item.
|
* In particular, the slot no longer contains the removed item.
|
||||||
*
|
*
|
||||||
@ -220,7 +258,9 @@ object Inventory {
|
|||||||
*/
|
*/
|
||||||
def onItemRemoved(removedItem: Item, replacedBy: Option[Item]): Unit
|
def onItemRemoved(removedItem: Item, replacedBy: Option[Item]): Unit
|
||||||
|
|
||||||
/** Called when an item contained in this slot sends a notification via [[Item.notifySlot]]. */
|
/**
|
||||||
|
* Called when an item contained in this slot sends a notification via [[Item.notifySlot]].
|
||||||
|
*/
|
||||||
def onItemNotification(notification: Item.Notification): Unit
|
def onItemNotification(notification: Item.Notification): Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,80 +20,7 @@ object Items extends Logging {
|
|||||||
// this is just to force load the class during initialization
|
// this is just to force load the class during initialization
|
||||||
def init(): Unit = {}
|
def init(): Unit = {}
|
||||||
|
|
||||||
/** Registers a recoverer for [[ItemRecoverer.sourceClass]]. */
|
logger.debug("Initialize item serialization...")
|
||||||
def registerRecoverer(recoverer: ItemRecoverer[_, _]): Unit = {
|
|
||||||
if (!_recoverers.contains(recoverer.sourceClass)) {
|
|
||||||
_recoverers(recoverer.sourceClass) = recoverer
|
|
||||||
logger.info(s"Registered a recoverer for ${recoverer.sourceClass.getName}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def registerItemFactoryRecoverers(factory: ItemFactory): Unit = {
|
|
||||||
for (recoverer <- factory.recoverers) {
|
|
||||||
registerRecoverer(recoverer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def registerSingleton(factory: ItemFactory): Unit = {
|
|
||||||
_groups += SingletonItemGroup(factory.name, factory)
|
|
||||||
registerItemFactoryRecoverers(factory)
|
|
||||||
}
|
|
||||||
|
|
||||||
def registerTiered(name: String, tiers: IterableOnce[Tier])(factory: Tier => ItemFactory): Unit = {
|
|
||||||
val group = TieredItemGroup(name, tiers.iterator.map(tier => (tier, factory(tier))).toSeq)
|
|
||||||
_groups += group
|
|
||||||
|
|
||||||
for ((_, factory) <- group.factories) {
|
|
||||||
registerItemFactoryRecoverers(factory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def registerExtendedTiered(name: String, tiers: IterableOnce[ExtendedTier])(
|
|
||||||
factory: ExtendedTier => ItemFactory
|
|
||||||
): Unit = {
|
|
||||||
val group = ExtendedTieredItemGroup(name, tiers.iterator.map(tier => (tier, factory(tier))).toSeq)
|
|
||||||
_groups += group
|
|
||||||
|
|
||||||
for ((_, factory) <- group.factories) {
|
|
||||||
registerItemFactoryRecoverers(factory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def registerArbitrary(name: String, icon: IconSource, factories: IterableOnce[(String, ItemFactory)]): Unit = {
|
|
||||||
val group = ArbitraryItemGroup(name, icon, factories.iterator.toSeq)
|
|
||||||
_groups += group
|
|
||||||
|
|
||||||
for ((_, factory) <- group.factories) {
|
|
||||||
registerItemFactoryRecoverers(factory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def groups: Iterable[ItemGroup] = _groups
|
|
||||||
|
|
||||||
/** Attempts to recover an [[Item]] from `source`.
|
|
||||||
*
|
|
||||||
* Checks superclasses and traits while looking for a recoverer.
|
|
||||||
*/
|
|
||||||
def recover[A](source: A): Option[Item] = {
|
|
||||||
linearizationOrder(source.getClass.asInstanceOf[Class[_]])
|
|
||||||
.flatMap(_recoverers.get)
|
|
||||||
.map(_.asInstanceOf[ItemRecoverer[_ >: A, _ <: Item]].recover(source))
|
|
||||||
.nextOption()
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed trait ItemGroup {
|
|
||||||
def name: String
|
|
||||||
}
|
|
||||||
|
|
||||||
case class SingletonItemGroup(name: String, factory: ItemFactory) extends ItemGroup
|
|
||||||
|
|
||||||
case class TieredItemGroup(name: String, factories: Seq[(Tier, ItemFactory)]) extends ItemGroup
|
|
||||||
|
|
||||||
case class ExtendedTieredItemGroup(name: String, factories: Seq[(ExtendedTier, ItemFactory)]) extends ItemGroup
|
|
||||||
|
|
||||||
case class ArbitraryItemGroup(name: String, icon: IconSource, factories: Seq[(String, ItemFactory)]) extends ItemGroup
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
registerTiered("CPU", Tier.One to Tier.Three)(new CpuItem.Factory(_))
|
registerTiered("CPU", Tier.One to Tier.Three)(new CpuItem.Factory(_))
|
||||||
registerTiered("APU", Tier.Two to Tier.Creative)(tier => new ApuItem.Factory(tier.saturatingSub(1)))
|
registerTiered("APU", Tier.Two to Tier.Creative)(tier => new ApuItem.Factory(tier.saturatingSub(1)))
|
||||||
@ -166,4 +93,81 @@ object Items extends Logging {
|
|||||||
.map(new TapeItem.Factory(_))
|
.map(new TapeItem.Factory(_))
|
||||||
.map(factory => (f"${factory.name}%s (${Tape.lengthMinutes(factory.kind)}%.0f min)", factory)),
|
.map(factory => (f"${factory.name}%s (${Tape.lengthMinutes(factory.kind)}%.0f min)", factory)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.debug("Item serialization initialization finished.")
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/** Registers a recoverer for [[ItemRecoverer.sourceClass]]. */
|
||||||
|
private def registerRecoverer(recoverer: ItemRecoverer[_, _]): Unit = {
|
||||||
|
if (!_recoverers.contains(recoverer.sourceClass)) {
|
||||||
|
_recoverers(recoverer.sourceClass) = recoverer
|
||||||
|
logger.debug(s"Registered a recoverer for ${recoverer.sourceClass.getName}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def registerItemFactoryRecoverers(factory: ItemFactory): Unit = {
|
||||||
|
for (recoverer <- factory.recoverers) {
|
||||||
|
registerRecoverer(recoverer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerSingleton(factory: ItemFactory): Unit = {
|
||||||
|
_groups += SingletonItemGroup(factory.name, factory)
|
||||||
|
registerItemFactoryRecoverers(factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerTiered(name: String, tiers: IterableOnce[Tier])(factory: Tier => ItemFactory): Unit = {
|
||||||
|
val group = TieredItemGroup(name, tiers.iterator.map(tier => (tier, factory(tier))).toSeq)
|
||||||
|
_groups += group
|
||||||
|
|
||||||
|
for ((_, factory) <- group.factories) {
|
||||||
|
registerItemFactoryRecoverers(factory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerExtendedTiered(name: String, tiers: IterableOnce[ExtendedTier])(
|
||||||
|
factory: ExtendedTier => ItemFactory
|
||||||
|
): Unit = {
|
||||||
|
val group = ExtendedTieredItemGroup(name, tiers.iterator.map(tier => (tier, factory(tier))).toSeq)
|
||||||
|
_groups += group
|
||||||
|
|
||||||
|
for ((_, factory) <- group.factories) {
|
||||||
|
registerItemFactoryRecoverers(factory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerArbitrary(name: String, icon: IconSource, factories: IterableOnce[(String, ItemFactory)]): Unit = {
|
||||||
|
val group = ArbitraryItemGroup(name, icon, factories.iterator.toSeq)
|
||||||
|
_groups += group
|
||||||
|
|
||||||
|
for ((_, factory) <- group.factories) {
|
||||||
|
registerItemFactoryRecoverers(factory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def groups: Iterable[ItemGroup] = _groups
|
||||||
|
|
||||||
|
/** Attempts to recover an [[Item]] from `source`.
|
||||||
|
*
|
||||||
|
* Checks superclasses and traits while looking for a recoverer.
|
||||||
|
*/
|
||||||
|
def recover[A](source: A): Option[Item] = {
|
||||||
|
linearizationOrder(source.getClass.asInstanceOf[Class[_]])
|
||||||
|
.flatMap(_recoverers.get)
|
||||||
|
.map(_.asInstanceOf[ItemRecoverer[_ >: A, _ <: Item]].recover(source))
|
||||||
|
.nextOption()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait ItemGroup {
|
||||||
|
def name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
case class SingletonItemGroup(name: String, factory: ItemFactory) extends ItemGroup
|
||||||
|
|
||||||
|
case class TieredItemGroup(name: String, factories: Seq[(Tier, ItemFactory)]) extends ItemGroup
|
||||||
|
|
||||||
|
case class ExtendedTieredItemGroup(name: String, factories: Seq[(ExtendedTier, ItemFactory)]) extends ItemGroup
|
||||||
|
|
||||||
|
case class ArbitraryItemGroup(name: String, icon: IconSource, factories: Seq[(String, ItemFactory)]) extends ItemGroup
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ trait PersistedInventory extends Inventory with Logging with Persistable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected def onSlotLoadFailed(slotIndex: Int): Unit = {
|
protected def onSlotLoadFailed(slotIndex: Int): Unit = {
|
||||||
Slot(slotIndex).remove()
|
Slot(slotIndex).removeAndDispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def saveEntityItem(slotNbt: NBTTagCompound, item: EntityItem): Unit = {
|
protected def saveEntityItem(slotNbt: NBTTagCompound, item: EntityItem): Unit = {
|
||||||
|
|||||||
@ -178,7 +178,7 @@ trait SyncedInventory extends PersistedInventory with EventAware with Logging {
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger.error("Breaking the loop forcefully by removing the items.")
|
logger.error("Breaking the loop forcefully by removing the items.")
|
||||||
Slot(slotIndex).remove()
|
Slot(slotIndex).removeAndDispose()
|
||||||
brainInventory.inventory(slotIndex).remove()
|
brainInventory.inventory(slotIndex).remove()
|
||||||
} else {
|
} else {
|
||||||
direction match {
|
direction match {
|
||||||
|
|||||||
@ -45,8 +45,8 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
menu.addEntry(new ContextMenuSubmenu("External data source", Some(ContextMenuIcon(IconSource.Code))) {
|
menu.addEntry(new ContextMenuSubmenu("External data source", Some(ContextMenuIcon(IconSource.Icons.Code))) {
|
||||||
addEntry(ContextMenuEntry("Local file", IconSource.File) {
|
addEntry(ContextMenuEntry("Local file", IconSource.Icons.File) {
|
||||||
OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.FILES_ONLY) { file =>
|
OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.FILES_ONLY) { file =>
|
||||||
Try {
|
Try {
|
||||||
for (file <- file) {
|
for (file <- file) {
|
||||||
@ -56,7 +56,7 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
addEntry(ContextMenuEntry("File via URL", IconSource.Link) {
|
addEntry(ContextMenuEntry("File via URL", IconSource.Icons.Link) {
|
||||||
new InputDialog(
|
new InputDialog(
|
||||||
title = "File via URL",
|
title = "File via URL",
|
||||||
onConfirmed = { text =>
|
onConfirmed = { text =>
|
||||||
@ -74,7 +74,7 @@ class EepromItem(val eeprom: EEPROM) extends Item with ComponentItem with Persis
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (eeprom.codePath.nonEmpty || eeprom.codeURL.nonEmpty) {
|
if (eeprom.codePath.nonEmpty || eeprom.codeURL.nonEmpty) {
|
||||||
addEntry(ContextMenuEntry("Detach", IconSource.LinkSlash) {
|
addEntry(ContextMenuEntry("Detach", IconSource.Icons.LinkSlash) {
|
||||||
eeprom.codeBytes = Some(Array.empty)
|
eeprom.codeBytes = Some(Array.empty)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,37 +3,44 @@ package ocelot.desktop.inventory.item
|
|||||||
import ocelot.desktop.graphics.IconSource
|
import ocelot.desktop.graphics.IconSource
|
||||||
import ocelot.desktop.inventory.traits.{ComponentItem, DiskItem, PersistableItem}
|
import ocelot.desktop.inventory.traits.{ComponentItem, DiskItem, PersistableItem}
|
||||||
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
||||||
|
import ocelot.desktop.ui.widget.DiskEditWindow
|
||||||
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
||||||
import totoro.ocelot.brain.entity.fs.ReadWriteLabel
|
import totoro.ocelot.brain.entity.fs.ReadWriteLabel
|
||||||
import totoro.ocelot.brain.entity.traits.{Disk, Entity, Floppy}
|
import totoro.ocelot.brain.entity.traits.{Disk, Entity, Floppy}
|
||||||
import totoro.ocelot.brain.entity.{FloppyManaged, FloppyUnmanaged}
|
import totoro.ocelot.brain.entity.{FloppyManaged, FloppyUnmanaged}
|
||||||
import totoro.ocelot.brain.loot.Loot.{FloppyFactory => LootFloppyFactory, LootFloppy}
|
import totoro.ocelot.brain.loot.Loot.{LootFloppy, FloppyFactory => LootFloppyFactory}
|
||||||
import totoro.ocelot.brain.util.DyeColor
|
import totoro.ocelot.brain.util.DyeColor
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class FloppyItem(var floppy: Floppy) extends Item with ComponentItem with PersistableItem with DiskItem {
|
class FloppyItem(var floppy: Floppy)
|
||||||
|
extends Item
|
||||||
|
with ComponentItem
|
||||||
|
with PersistableItem
|
||||||
|
with DiskItem
|
||||||
|
with DiskEditWindow.HasColor {
|
||||||
|
|
||||||
override def entity: Entity with Disk = floppy
|
override def entity: Entity with Disk = floppy
|
||||||
|
|
||||||
override def diskKind: String = "Floppy"
|
override def diskKind: String = "Floppy"
|
||||||
|
|
||||||
override def name: String = floppy.name.getOrElse(super.name)
|
override def name: String = floppy.name.getOrElse(super.name)
|
||||||
|
|
||||||
override def label: Option[String] = floppy.label.labelOption
|
override def diskLabel: Option[String] = floppy.label.labelOption
|
||||||
|
|
||||||
override def isLabelWriteable: Boolean = floppy.label.isInstanceOf[ReadWriteLabel]
|
override def diskLabel_=(label: Option[String]): Unit = {
|
||||||
|
|
||||||
override def setLabel(label: Option[String]): Unit = {
|
|
||||||
floppy.label.setLabel(label.orNull)
|
floppy.label.setLabel(label.orNull)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def color: Some[DyeColor] = Some(floppy.color)
|
override def isLabelWriteable: Boolean = floppy.label.isInstanceOf[ReadWriteLabel]
|
||||||
|
|
||||||
override def setColor(color: DyeColor): Unit = {
|
override def color: DyeColor = floppy.color
|
||||||
|
|
||||||
|
override def color_=(color: DyeColor): Unit = {
|
||||||
floppy.color = color
|
floppy.color = color
|
||||||
}
|
}
|
||||||
|
|
||||||
override def setManaged(managed: Boolean): Unit = {
|
override def setManaged(managed: Boolean): Unit = {
|
||||||
val label = this.label
|
val label = this.diskLabel
|
||||||
|
|
||||||
reinserting {
|
reinserting {
|
||||||
floppy =
|
floppy =
|
||||||
@ -41,13 +48,13 @@ class FloppyItem(var floppy: Floppy) extends Item with ComponentItem with Persis
|
|||||||
else new FloppyUnmanaged(floppy.name, floppy.color)
|
else new FloppyUnmanaged(floppy.name, floppy.color)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLabel(label)
|
diskLabel = label
|
||||||
}
|
}
|
||||||
|
|
||||||
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
||||||
super.fillTooltip(tooltip)
|
super.fillTooltip(tooltip)
|
||||||
|
|
||||||
for (label <- label) {
|
for (label <- diskLabel) {
|
||||||
if (label != name) {
|
if (label != name) {
|
||||||
// this is true for loot floppies
|
// this is true for loot floppies
|
||||||
addDiskLabelTooltip(tooltip, label)
|
addDiskLabelTooltip(tooltip, label)
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import ocelot.desktop.inventory.traits.{ComponentItem, DiskItem, PersistableItem
|
|||||||
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
||||||
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
||||||
import totoro.ocelot.brain.entity.fs.{Label, ReadWriteLabel}
|
import totoro.ocelot.brain.entity.fs.{Label, ReadWriteLabel}
|
||||||
import totoro.ocelot.brain.entity.traits.{Disk, Entity}
|
import totoro.ocelot.brain.entity.traits.{Disk, Entity, Environment}
|
||||||
import totoro.ocelot.brain.entity.{HDDManaged, HDDUnmanaged}
|
import totoro.ocelot.brain.entity.{HDDManaged, HDDUnmanaged}
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
@ -29,14 +29,14 @@ class HddItem(var hdd: Hdd) extends Item with ComponentItem with PersistableItem
|
|||||||
case Hdd.Unmanaged(hdd) => hdd.label
|
case Hdd.Unmanaged(hdd) => hdd.label
|
||||||
}
|
}
|
||||||
|
|
||||||
def label: Option[String] = fsLabel.labelOption
|
def diskLabel: Option[String] = fsLabel.labelOption
|
||||||
|
|
||||||
|
override def diskLabel_=(label: Option[String]): Unit = fsLabel.setLabel(label.orNull)
|
||||||
|
|
||||||
override def isLabelWriteable: Boolean = fsLabel.isInstanceOf[ReadWriteLabel]
|
override def isLabelWriteable: Boolean = fsLabel.isInstanceOf[ReadWriteLabel]
|
||||||
|
|
||||||
override def setLabel(label: Option[String]): Unit = fsLabel.setLabel(label.orNull)
|
|
||||||
|
|
||||||
override def setManaged(managed: Boolean): Unit = {
|
override def setManaged(managed: Boolean): Unit = {
|
||||||
val label = this.label
|
val label = this.diskLabel
|
||||||
|
|
||||||
reinserting {
|
reinserting {
|
||||||
hdd =
|
hdd =
|
||||||
@ -44,13 +44,13 @@ class HddItem(var hdd: Hdd) extends Item with ComponentItem with PersistableItem
|
|||||||
else Hdd(new HDDUnmanaged(tier.get))
|
else Hdd(new HDDUnmanaged(tier.get))
|
||||||
}
|
}
|
||||||
|
|
||||||
setLabel(label)
|
diskLabel = label
|
||||||
}
|
}
|
||||||
|
|
||||||
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
||||||
super.fillTooltip(tooltip)
|
super.fillTooltip(tooltip)
|
||||||
|
|
||||||
label.foreach(addDiskLabelTooltip(tooltip, _))
|
diskLabel.foreach(addDiskLabelTooltip(tooltip, _))
|
||||||
|
|
||||||
hdd match {
|
hdd match {
|
||||||
case Hdd.Managed(hdd) => addSourcePathTooltip(tooltip, hdd)
|
case Hdd.Managed(hdd) => addSourcePathTooltip(tooltip, hdd)
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class LinkedCardItem(val linkedCard: LinkedCard) extends Item with ComponentItem
|
|||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
menu.addEntry(
|
menu.addEntry(
|
||||||
ContextMenuEntry("Set channel", IconSource.Antenna) {
|
ContextMenuEntry("Set channel", IconSource.Icons.Antenna) {
|
||||||
new TunnelDialog(
|
new TunnelDialog(
|
||||||
tunnel => linkedCard.tunnel = tunnel,
|
tunnel => linkedCard.tunnel = tunnel,
|
||||||
linkedCard.tunnel,
|
linkedCard.tunnel,
|
||||||
|
|||||||
@ -23,7 +23,7 @@ class OcelotCardItem(val ocelotCard: OcelotCard)
|
|||||||
override def tooltipNameColor: Color = ColorScheme("OcelotCardTooltip")
|
override def tooltipNameColor: Color = ColorScheme("OcelotCardTooltip")
|
||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry("Open console", IconSource.Window) {
|
menu.addEntry(ContextMenuEntry("Open console", IconSource.Icons.Window) {
|
||||||
window.open()
|
window.open()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import totoro.ocelot.brain.nbt.NBTTagCompound
|
|||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
abstract class RedstoneCardItem extends Item with ComponentItem with PersistableItem with CardItem {}
|
abstract class RedstoneCardItem extends Item with ComponentItem with PersistableItem with CardItem
|
||||||
|
|
||||||
object RedstoneCardItem {
|
object RedstoneCardItem {
|
||||||
abstract class Factory extends ItemFactory {
|
abstract class Factory extends ItemFactory {
|
||||||
@ -43,7 +43,7 @@ object RedstoneCardItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry("Redstone I/O", IconSource.ArrowRight) {
|
menu.addEntry(ContextMenuEntry("Redstone I/O", IconSource.Icons.ArrowRight) {
|
||||||
windowed.window.open()
|
windowed.window.open()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -63,6 +63,12 @@ object RedstoneCardItem {
|
|||||||
|
|
||||||
windowed.save(nbt)
|
windowed.save(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def dispose(): Unit = {
|
||||||
|
windowed.closeAndDisposeWindow()
|
||||||
|
|
||||||
|
super.dispose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Tier1 {
|
object Tier1 {
|
||||||
@ -93,7 +99,7 @@ object RedstoneCardItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry("Bundled I/O", IconSource.LinesHorizontal) {
|
menu.addEntry(ContextMenuEntry("Bundled I/O", IconSource.Icons.LinesHorizontal) {
|
||||||
windowed.window.open()
|
windowed.window.open()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -111,6 +117,12 @@ object RedstoneCardItem {
|
|||||||
|
|
||||||
windowed.save(nbt)
|
windowed.save(nbt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def dispose(): Unit = {
|
||||||
|
windowed.closeAndDisposeWindow()
|
||||||
|
|
||||||
|
super.dispose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Tier2 {
|
object Tier2 {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class SelfDestructingCardItem(val card: SelfDestructingCard)
|
|||||||
tooltip.addLine(
|
tooltip.addLine(
|
||||||
if (card.remainingTime < 0) "Fuse has not been set"
|
if (card.remainingTime < 0) "Fuse has not been set"
|
||||||
else if (card.remainingTime == 0) "BOOM!"
|
else if (card.remainingTime == 0) "BOOM!"
|
||||||
else card.remainingTime.toString
|
else f"Time to explosion: ${card.remainingTime / 20f}%.2fs"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,52 @@
|
|||||||
package ocelot.desktop.inventory.item
|
package ocelot.desktop.inventory.item
|
||||||
|
|
||||||
|
import ocelot.desktop.audio.{Audio, SoundCategory, SoundSamples, SoundSource, SoundStream}
|
||||||
import ocelot.desktop.graphics.IconSource
|
import ocelot.desktop.graphics.IconSource
|
||||||
import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem}
|
import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem}
|
||||||
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
||||||
|
import ocelot.desktop.ui.event.BrainEvent
|
||||||
import ocelot.desktop.ui.widget.card.SoundCardWindow
|
import ocelot.desktop.ui.widget.card.SoundCardWindow
|
||||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||||
import ocelot.desktop.ui.widget.window.Windowed
|
import ocelot.desktop.ui.widget.window.Windowed
|
||||||
|
import ocelot.desktop.util.Lazy
|
||||||
|
import totoro.ocelot.brain.Settings
|
||||||
import totoro.ocelot.brain.entity.sound_card.SoundCard
|
import totoro.ocelot.brain.entity.sound_card.SoundCard
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
import totoro.ocelot.brain.event.SoundCardAudioEvent
|
||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class SoundCardItem(val soundCard: SoundCard)
|
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)
|
override def createWindow(): SoundCardWindow = new SoundCardWindow(soundCard)
|
||||||
|
|
||||||
override def entity: Entity with Environment = soundCard
|
override def entity: SoundCard = soundCard
|
||||||
|
|
||||||
|
private val streamPair = Lazy(Audio.newStream(SoundCategory.Records))
|
||||||
|
private def stream: SoundStream = streamPair.getSync._1
|
||||||
|
private def source: SoundSource = streamPair.getSync._2
|
||||||
|
|
||||||
|
eventHandlers += {
|
||||||
|
case BrainEvent(event: SoundCardAudioEvent) if !Audio.isDisabled =>
|
||||||
|
val samples = SoundSamples(event.data, Settings.get.soundCardSampleRate, SoundSamples.Format.Mono8)
|
||||||
|
stream.enqueue(samples)
|
||||||
|
source.volume = event.volume
|
||||||
|
}
|
||||||
|
|
||||||
|
override def dispose(): Unit = {
|
||||||
|
super.dispose()
|
||||||
|
|
||||||
|
for (stream <- streamPair.getOption) {
|
||||||
|
stream._2.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry("Open card interface", IconSource.Window) {
|
menu.addEntry(ContextMenuEntry("Open card interface", IconSource.Icons.Window) {
|
||||||
window.open()
|
window.open()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ trait ComponentItem extends EntityItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val copyAddressEntry = ContextMenuEntry("Copy address", IconSource.Copy) {
|
private val copyAddressEntry = ContextMenuEntry("Copy address", IconSource.Icons.Copy) {
|
||||||
UiHandler.clipboard = entity.node.address
|
UiHandler.clipboard = entity.node.address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ trait CpuLikeItem extends ComponentItem {
|
|||||||
override def entity: Entity with GenericCPU
|
override def entity: Entity with GenericCPU
|
||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
menu.addEntry(new ContextMenuSubmenu("Set architecture", Some(ContextMenuIcon(IconSource.Microchip))) {
|
menu.addEntry(new ContextMenuSubmenu("Set architecture", Some(ContextMenuIcon(IconSource.Icons.Microchip))) {
|
||||||
for (arch <- entity.allArchitectures) {
|
for (arch <- entity.allArchitectures) {
|
||||||
val name = MachineAPI.getArchitectureName(arch) +
|
val name = MachineAPI.getArchitectureName(arch) +
|
||||||
(if (arch == entity.architecture) " (current)" else "")
|
(if (arch == entity.architecture) " (current)" else "")
|
||||||
|
|||||||
@ -4,40 +4,38 @@ import ocelot.desktop.OcelotDesktop
|
|||||||
import ocelot.desktop.graphics.IconSource
|
import ocelot.desktop.graphics.IconSource
|
||||||
import ocelot.desktop.node.nodes.RaidNode
|
import ocelot.desktop.node.nodes.RaidNode
|
||||||
import ocelot.desktop.ui.widget.DiskEditWindow
|
import ocelot.desktop.ui.widget.DiskEditWindow
|
||||||
|
import ocelot.desktop.ui.widget.DiskEditWindow.{CanChangeManaged, EditableDisk, Lockable}
|
||||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||||
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
||||||
import ocelot.desktop.ui.widget.window.{Window, Windowed}
|
import ocelot.desktop.ui.widget.window.{Window, Windowed}
|
||||||
import totoro.ocelot.brain.entity.traits.{Disk, DiskManaged, DiskRealPathAware, DiskUnmanaged, Entity}
|
import totoro.ocelot.brain.entity.traits.{Disk, DiskManaged, DiskRealPathAware, DiskUnmanaged, Entity}
|
||||||
import totoro.ocelot.brain.util.DyeColor
|
|
||||||
|
|
||||||
import javax.swing.JFileChooser
|
import javax.swing.JFileChooser
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
/** A utility mixin for HDDs and floppies. */
|
/**
|
||||||
trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
* A utility mixin for HDDs and floppies.
|
||||||
|
*/
|
||||||
|
trait DiskItem
|
||||||
|
extends ComponentItem
|
||||||
|
with EditableDisk
|
||||||
|
with Lockable
|
||||||
|
with CanChangeManaged
|
||||||
|
with Windowed[DiskEditWindow] {
|
||||||
|
|
||||||
override def entity: Entity with Disk
|
override def entity: Entity with Disk
|
||||||
|
|
||||||
def diskKind: String
|
override def disk: Option[Entity with Disk] = Option(entity)
|
||||||
|
|
||||||
def color: Option[DyeColor] = None
|
override def capacity: Long = disk.fold(0L)(_.capacity)
|
||||||
|
|
||||||
def setColor(color: DyeColor): Unit = throw new UnsupportedOperationException()
|
override def lock(): Unit = reinserting {
|
||||||
|
|
||||||
def label: Option[String]
|
|
||||||
|
|
||||||
def isLabelWriteable: Boolean
|
|
||||||
|
|
||||||
def setLabel(label: Option[String]): Unit
|
|
||||||
|
|
||||||
def setManaged(managed: Boolean): Unit
|
|
||||||
|
|
||||||
def lock(): Unit = reinserting {
|
|
||||||
entity.setLocked(OcelotDesktop.player.nickname)
|
entity.setLocked(OcelotDesktop.player.nickname)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def createWindow(): DiskEditWindow = new DiskEditWindow(DiskItem.this)
|
override def createWindow(): DiskEditWindow = new DiskEditWindow(this)
|
||||||
|
|
||||||
def isEditingAllowed: Boolean = {
|
def editingAllowed: Boolean = {
|
||||||
slot.fold(false)(_.inventory match {
|
slot.fold(false)(_.inventory match {
|
||||||
case _: RaidNode => false
|
case _: RaidNode => false
|
||||||
case _ => true
|
case _ => true
|
||||||
@ -45,16 +43,19 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
if (isEditingAllowed) {
|
if (editingAllowed) {
|
||||||
// Real path
|
// Real path
|
||||||
entity match {
|
entity match {
|
||||||
case diskManaged: DiskManaged =>
|
case diskManaged: DiskManaged =>
|
||||||
DiskItem.addRealPathContextMenuEntries(menu, diskManaged,
|
DiskItem.addRealPathContextMenuEntries(
|
||||||
|
menu,
|
||||||
|
diskManaged,
|
||||||
realPathSetter => {
|
realPathSetter => {
|
||||||
reinserting {
|
reinserting {
|
||||||
realPathSetter()
|
realPathSetter()
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,18 +87,22 @@ trait DiskItem extends ComponentItem with Windowed[DiskEditWindow] {
|
|||||||
|
|
||||||
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
override def fillTooltip(tooltip: ItemTooltip): Unit = {
|
||||||
super.fillTooltip(tooltip)
|
super.fillTooltip(tooltip)
|
||||||
|
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
tooltip.addLine(s"Capacity: ${entity.capacity / 1024} kB")
|
tooltip.addLine(s"Capacity: ${capacity / 1024} kB")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object DiskItem {
|
object DiskItem {
|
||||||
def addRealPathContextMenuEntries(menu: ContextMenu, diskRealPathAware: DiskRealPathAware,
|
def addRealPathContextMenuEntries(
|
||||||
realPathSetter: (() => Unit) => Unit): Unit = {
|
menu: ContextMenu,
|
||||||
|
diskRealPathAware: DiskRealPathAware,
|
||||||
|
realPathSetter: (() => Unit) => Unit,
|
||||||
|
): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry(
|
menu.addEntry(ContextMenuEntry(
|
||||||
if (diskRealPathAware.customRealPath.isDefined) "Change directory" else "Set directory",
|
if (diskRealPathAware.customRealPath.isDefined) "Change directory" else "Set directory",
|
||||||
IconSource.Folder,
|
IconSource.Icons.Folder,
|
||||||
) {
|
) {
|
||||||
OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
OcelotDesktop.showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
||||||
Try {
|
Try {
|
||||||
@ -112,7 +117,7 @@ object DiskItem {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (diskRealPathAware.customRealPath.isDefined) {
|
if (diskRealPathAware.customRealPath.isDefined) {
|
||||||
menu.addEntry(ContextMenuEntry("Reset directory", IconSource.FolderSlash) {
|
menu.addEntry(ContextMenuEntry("Reset directory", IconSource.Icons.FolderSlash) {
|
||||||
realPathSetter(() => {
|
realPathSetter(() => {
|
||||||
// trigger component_removed / component_added signals
|
// trigger component_removed / component_added signals
|
||||||
diskRealPathAware.customRealPath = None
|
diskRealPathAware.customRealPath = None
|
||||||
@ -122,7 +127,7 @@ object DiskItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def addEditDiskContextMenuEntries[T <: Window](menu: ContextMenu, windowed: Windowed[T]): Unit = {
|
def addEditDiskContextMenuEntries[T <: Window](menu: ContextMenu, windowed: Windowed[T]): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry("Edit disk", IconSource.Edit) {
|
menu.addEntry(ContextMenuEntry("Edit disk", IconSource.Icons.Edit) {
|
||||||
windowed.window.open()
|
windowed.window.open()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
102
src/main/scala/ocelot/desktop/node/BoomCardFxHandler.scala
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package ocelot.desktop.node
|
||||||
|
|
||||||
|
import ocelot.desktop.audio.{SoundBuffers, SoundCategory, SoundSource}
|
||||||
|
import ocelot.desktop.color.Color
|
||||||
|
import ocelot.desktop.geometry.FloatUtils.ExtendedFloat
|
||||||
|
import ocelot.desktop.graphics.{Graphics, IconSource}
|
||||||
|
import ocelot.desktop.inventory.item.SelfDestructingCardItem
|
||||||
|
import ocelot.desktop.node.BoomCardFxHandler.{ExpandIntensity, ExpandPeriod, FlickerAlpha, FlickerDuty, GlowAlpha, MaxSize, MinSize}
|
||||||
|
import ocelot.desktop.ui.UiHandler
|
||||||
|
import ocelot.desktop.ui.event.BrainEvent
|
||||||
|
import ocelot.desktop.{ColorScheme, OcelotDesktop}
|
||||||
|
import totoro.ocelot.brain.event.SelfDestructingCardBoomEvent
|
||||||
|
|
||||||
|
trait BoomCardFxHandler extends Node with PositionalSoundSourcesNode with SmokeParticleNode {
|
||||||
|
private var boomPhase: Float = -1
|
||||||
|
|
||||||
|
private lazy val explosionSound = {
|
||||||
|
SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy val countdownBeepSound = {
|
||||||
|
SoundSource.fromBuffer(SoundBuffers.SelfDestructingCardCountdownBeep, SoundCategory.Environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def soundSources: Seq[SoundSource] = super.soundSources ++ Seq(
|
||||||
|
explosionSound,
|
||||||
|
countdownBeepSound,
|
||||||
|
)
|
||||||
|
|
||||||
|
eventHandlers += {
|
||||||
|
case BrainEvent(_: SelfDestructingCardBoomEvent) =>
|
||||||
|
OcelotDesktop.updateThreadTasks.add(() => {
|
||||||
|
explosionSound.play()
|
||||||
|
emitSmoke()
|
||||||
|
destroy()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def selfDestructingCards: IterableOnce[SelfDestructingCardItem]
|
||||||
|
|
||||||
|
private var phase = 0f
|
||||||
|
private var flickerPhase = 0f
|
||||||
|
|
||||||
|
private def updateBoomCardState(): Unit = {
|
||||||
|
phase = (phase + UiHandler.dt / ExpandPeriod) % 1f
|
||||||
|
flickerPhase = 0f.max(flickerPhase - UiHandler.dt)
|
||||||
|
boomPhase = -1
|
||||||
|
|
||||||
|
for (item <- selfDestructingCards) {
|
||||||
|
if (item.card.time > 0) {
|
||||||
|
// If multiple SDCs are ticking, let the most soon exploding one to define the glow
|
||||||
|
boomPhase = boomPhase.max(1 - item.card.time.toFloat / item.card.initialTime)
|
||||||
|
|
||||||
|
if (item.card.lastBeepTime < 0 || item.card.lastBeepTime - item.card.time >= 20) {
|
||||||
|
countdownBeepSound.play()
|
||||||
|
item.card.lastBeepTime = item.card.time
|
||||||
|
flickerPhase = FlickerDuty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def update(): Unit = {
|
||||||
|
super.update()
|
||||||
|
updateBoomCardState()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def expandFactor(phase: Float): Float = {
|
||||||
|
math.sin(2 * math.Pi * phase).toFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
override def drawLight(g: Graphics): Unit = {
|
||||||
|
super.drawLight(g)
|
||||||
|
|
||||||
|
if (boomPhase > 0) {
|
||||||
|
val expand = expandFactor(phase)
|
||||||
|
val glowSize = MinSize.lerp(MaxSize, boomPhase + ExpandIntensity * expand)
|
||||||
|
val alpha = boomPhase * GlowAlpha
|
||||||
|
|
||||||
|
if (flickerPhase >= 0.01) {
|
||||||
|
g.rect(bounds, Color.White.withAlpha(FlickerAlpha * (1 - boomPhase)))
|
||||||
|
}
|
||||||
|
|
||||||
|
g.sprite(
|
||||||
|
IconSource.Nodes.LampGlow,
|
||||||
|
position - size * glowSize,
|
||||||
|
size * (1 + 2 * glowSize),
|
||||||
|
ColorScheme("BoomCardGlowStart").lerp(ColorScheme("BoomCardGlowEnd"), boomPhase).withAlpha(alpha),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object BoomCardFxHandler {
|
||||||
|
private val ExpandPeriod = 1f
|
||||||
|
private val ExpandIntensity = 0.05f
|
||||||
|
private val FlickerDuty = 0.33f
|
||||||
|
private val FlickerAlpha = 0.05f
|
||||||
|
private val MinSize = 0.1f
|
||||||
|
private val MaxSize = 0.5f
|
||||||
|
private val GlowAlpha = 0.4f
|
||||||
|
}
|
||||||
@ -1,9 +1,6 @@
|
|||||||
package ocelot.desktop.node
|
package ocelot.desktop.node
|
||||||
|
|
||||||
import ocelot.desktop.OcelotDesktop
|
|
||||||
import ocelot.desktop.{Settings => DesktopSettings}
|
|
||||||
import ocelot.desktop.audio._
|
import ocelot.desktop.audio._
|
||||||
import ocelot.desktop.geometry.Vector2D
|
|
||||||
import ocelot.desktop.graphics.{Graphics, IconSource}
|
import ocelot.desktop.graphics.{Graphics, IconSource}
|
||||||
import ocelot.desktop.inventory.SyncedInventory
|
import ocelot.desktop.inventory.SyncedInventory
|
||||||
import ocelot.desktop.node.ComputerAwareNode._
|
import ocelot.desktop.node.ComputerAwareNode._
|
||||||
@ -11,85 +8,39 @@ import ocelot.desktop.node.Node.Size
|
|||||||
import ocelot.desktop.ui.UiHandler
|
import ocelot.desktop.ui.UiHandler
|
||||||
import ocelot.desktop.ui.event.BrainEvent
|
import ocelot.desktop.ui.event.BrainEvent
|
||||||
import ocelot.desktop.ui.event.handlers.DiskActivityHandler
|
import ocelot.desktop.ui.event.handlers.DiskActivityHandler
|
||||||
import ocelot.desktop.ui.widget.ComputerErrorMessageLabel
|
import ocelot.desktop.ui.particle.Particle
|
||||||
import ocelot.desktop.util.Messages
|
import ocelot.desktop.util.Messages
|
||||||
import totoro.ocelot.brain.Settings
|
import ocelot.desktop.{ColorScheme, Settings => DesktopSettings}
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware}
|
import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware}
|
||||||
import totoro.ocelot.brain.event._
|
import totoro.ocelot.brain.event._
|
||||||
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import scala.collection.mutable.ArrayBuffer
|
|
||||||
|
|
||||||
abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceAware)
|
abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceAware)
|
||||||
extends EntityNode(entity)
|
extends EntityNode(entity)
|
||||||
with SyncedInventory
|
with SyncedInventory
|
||||||
with DiskActivityHandler
|
with DiskActivityHandler
|
||||||
with OcelotLogParticleNode
|
with OcelotLogParticleNode
|
||||||
|
with BoomCardFxHandler
|
||||||
with ShiftClickNode {
|
with ShiftClickNode {
|
||||||
|
|
||||||
// access should be synchronized because messages are added in the update thread
|
|
||||||
private val messages = ArrayBuffer.empty[(Float, ComputerErrorMessageLabel)]
|
|
||||||
|
|
||||||
private def addErrorMessage(message: ComputerErrorMessageLabel): Unit = messages.synchronized {
|
|
||||||
messages += ((0f, message))
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy val soundCardSounds: (SoundStream, SoundSource) = Audio.newStream(SoundCategory.Records)
|
|
||||||
private def soundCardStream: SoundStream = soundCardSounds._1
|
|
||||||
private def soundCardSource: SoundSource = soundCardSounds._2
|
|
||||||
|
|
||||||
eventHandlers += {
|
eventHandlers += {
|
||||||
case BrainEvent(event: MachineCrashEvent) =>
|
case BrainEvent(event: MachineCrashEvent) =>
|
||||||
val message = Messages.lift(event.message) match {
|
val message = Messages.lift(event.message) match {
|
||||||
case Some(message) =>
|
case Some(message) =>
|
||||||
logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message code ${event.message}: $message")
|
logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message code ${event.message}: $message")
|
||||||
message
|
message
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message: ${event.message}")
|
logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message: ${event.message}")
|
||||||
event.message
|
event.message
|
||||||
}
|
}
|
||||||
|
UiHandler.root.workspaceView.particleSystem.add(new ErrorMessageParticle(message))
|
||||||
addErrorMessage(new ComputerErrorMessageLabel(this, message))
|
|
||||||
|
|
||||||
case BrainEvent(event: BeepEvent) if !Audio.isDisabled =>
|
case BrainEvent(event: BeepEvent) if !Audio.isDisabled =>
|
||||||
BeepGenerator.newBeep(".", event.frequency, event.duration).play()
|
BeepGenerator.newBeep(".", event.frequency, event.duration).play()
|
||||||
|
|
||||||
case BrainEvent(event: BeepPatternEvent) if !Audio.isDisabled =>
|
case BrainEvent(event: BeepPatternEvent) if !Audio.isDisabled =>
|
||||||
BeepGenerator.newBeep(event.pattern, 1000, 200).play()
|
BeepGenerator.newBeep(event.pattern, 1000, 200).play()
|
||||||
|
|
||||||
case BrainEvent(event: SoundCardAudioEvent) if !Audio.isDisabled =>
|
|
||||||
val samples = SoundSamples(event.data, Settings.get.soundCardSampleRate, SoundSamples.Format.Mono8)
|
|
||||||
soundCardStream.enqueue(samples)
|
|
||||||
soundCardSource.volume = event.volume
|
|
||||||
|
|
||||||
case BrainEvent(_: SelfDestructingCardBoomEvent) =>
|
|
||||||
OcelotDesktop.updateThreadTasks.add(() => {
|
|
||||||
SoundSource.MinecraftExplosion.play()
|
|
||||||
destroy()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override def update(): Unit = {
|
|
||||||
messages.synchronized {
|
|
||||||
messages.mapInPlace { case (t, message) => (t + ErrorMessageMoveSpeed * UiHandler.dt, message) }
|
|
||||||
messages.filterInPlace(_._1 <= 1f)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
private def drawMessageParticles(g: Graphics): Unit = messages.synchronized {
|
|
||||||
for ((time, message) <- messages.reverseIterator) {
|
|
||||||
message.position = message.initialPosition + Vector2D(0, -MaxErrorMessageDistance * time)
|
|
||||||
message.alpha = 1 - time
|
|
||||||
message.draw(g)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override def drawParticles(g: Graphics): Unit = {
|
|
||||||
super.drawParticles(g)
|
|
||||||
drawMessageParticles(g)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def drawOverlay(g: Graphics): Unit = HolidayIcon match {
|
protected def drawOverlay(g: Graphics): Unit = HolidayIcon match {
|
||||||
@ -110,9 +61,19 @@ abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceA
|
|||||||
|
|
||||||
override def draw(g: Graphics): Unit = {
|
override def draw(g: Graphics): Unit = {
|
||||||
super.draw(g)
|
super.draw(g)
|
||||||
|
|
||||||
drawOverlay(g)
|
drawOverlay(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ErrorMessageParticle(message: String) extends Particle(speed = ErrorMessageMoveSpeed) {
|
||||||
|
private val offsetX = size.width / 2 - message.length * 4
|
||||||
|
private val offsetY = -8
|
||||||
|
override def draw(g: Graphics): Unit = {
|
||||||
|
g.setSmallFont()
|
||||||
|
g.foreground = ColorScheme("ErrorMessage").withAlpha(1 - (2 * time - 1).min(1).max(0))
|
||||||
|
g.text(position.x + offsetX, position.y + offsetY - MaxErrorMessageDistance * time, message)
|
||||||
|
g.setNormalFont()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object ComputerAwareNode {
|
object ComputerAwareNode {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ abstract class EntityNode(val entity: Entity with Environment) extends Node {
|
|||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
if (exposeAddress && entity.node != null && entity.node.address != null) {
|
if (exposeAddress && entity.node != null && entity.node.address != null) {
|
||||||
menu.addEntry(
|
menu.addEntry(
|
||||||
ContextMenuEntry("Copy address", IconSource.Copy) {
|
ContextMenuEntry("Copy address", IconSource.Icons.Copy) {
|
||||||
UiHandler.clipboard = entity.node.address
|
UiHandler.clipboard = entity.node.address
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -26,7 +26,7 @@ trait LabeledNode extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry("Set label", IconSource.Label) {
|
menu.addEntry(ContextMenuEntry("Set label", IconSource.Icons.Label) {
|
||||||
new InputDialog(
|
new InputDialog(
|
||||||
"Set label",
|
"Set label",
|
||||||
text => {
|
text => {
|
||||||
@ -40,6 +40,8 @@ trait LabeledNode extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def drawLabel(g: Graphics): Unit = {
|
override def drawLabel(g: Graphics): Unit = {
|
||||||
|
super.drawLabel(g)
|
||||||
|
|
||||||
for (label <- label) {
|
for (label <- label) {
|
||||||
g.setSmallFont()
|
g.setSmallFont()
|
||||||
g.background = RGBAColor(0, 0, 0, 0)
|
g.background = RGBAColor(0, 0, 0, 0)
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import ocelot.desktop.util.Persistable
|
|||||||
import ocelot.desktop.util.animation.ColorAnimation
|
import ocelot.desktop.util.animation.ColorAnimation
|
||||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||||
import totoro.ocelot.brain.network
|
import totoro.ocelot.brain.network
|
||||||
|
import totoro.ocelot.brain.util.Direction.Direction
|
||||||
|
|
||||||
import scala.collection.mutable.ArrayBuffer
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
@ -89,12 +90,12 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
|||||||
|
|
||||||
def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
if (ports.nonEmpty) {
|
if (ports.nonEmpty) {
|
||||||
menu.addEntry(ContextMenuEntry("Disconnect", IconSource.LinkSlash, SoundSource.InterfaceClickLow) {
|
menu.addEntry(ContextMenuEntry("Disconnect", IconSource.Icons.LinkSlash, SoundSource.InterfaceClickLow) {
|
||||||
disconnectFromAll()
|
disconnectFromAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.addEntry(ContextMenuEntry("Remove", IconSource.Delete, SoundSource.InterfaceClickLow) {
|
menu.addEntry(ContextMenuEntry("Remove", IconSource.Icons.Delete, SoundSource.InterfaceClickLow) {
|
||||||
destroy()
|
destroy()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -103,11 +104,11 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
|||||||
super.update()
|
super.update()
|
||||||
|
|
||||||
if (isHovered || isMoving) {
|
if (isHovered || isMoving) {
|
||||||
root.get.statusBar.addMouseEntry("icons/RMB", "Menu")
|
root.get.statusBar.addMouseEntry(IconSource.Icons.RMB, "Menu")
|
||||||
root.get.statusBar.addMouseEntry("icons/DragLMB", "Move node")
|
root.get.statusBar.addMouseEntry(IconSource.Icons.DragLMB, "Move node")
|
||||||
|
|
||||||
if (ports.nonEmpty) {
|
if (ports.nonEmpty) {
|
||||||
root.get.statusBar.addMouseEntry("icons/DragRMB", "Connect/Disconnect")
|
root.get.statusBar.addMouseEntry(IconSource.Icons.DragRMB, "Connect/Disconnect")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,7 +119,7 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
|||||||
super.dispose()
|
super.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
def iconSource: IconSource = IconSource.NA
|
def icon: IconSource = IconSource.Icons.NA
|
||||||
|
|
||||||
def iconColor: Color = RGBAColor(255, 255, 255)
|
def iconColor: Color = RGBAColor(255, 255, 255)
|
||||||
|
|
||||||
@ -126,6 +127,8 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
|||||||
|
|
||||||
def getNodeByPort(port: NodePort): network.Node = throw new IllegalArgumentException("this node has no ports")
|
def getNodeByPort(port: NodePort): network.Node = throw new IllegalArgumentException("this node has no ports")
|
||||||
|
|
||||||
|
def rotatable: Boolean
|
||||||
|
|
||||||
def connections: Iterator[(NodePort, Node, NodePort)] = _connections.iterator
|
def connections: Iterator[(NodePort, Node, NodePort)] = _connections.iterator
|
||||||
|
|
||||||
def connect(portA: NodePort, node: Node, portB: NodePort): Unit = {
|
def connect(portA: NodePort, node: Node, portB: NodePort): Unit = {
|
||||||
@ -260,29 +263,51 @@ abstract class Node extends Widget with MouseHandler with HoverHandler with Pers
|
|||||||
drawHighlight(g)
|
drawHighlight(g)
|
||||||
|
|
||||||
g.sprite(
|
g.sprite(
|
||||||
iconSource.path,
|
icon,
|
||||||
position.x + HighlightThickness,
|
position.x + HighlightThickness,
|
||||||
position.y + HighlightThickness,
|
position.y + HighlightThickness,
|
||||||
size.width - HighlightThickness * 2,
|
size.width - HighlightThickness * 2,
|
||||||
size.height - HighlightThickness * 2,
|
size.height - HighlightThickness * 2,
|
||||||
iconColor,
|
iconColor,
|
||||||
iconSource.animation,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def drawPortLegend(g: Graphics): Unit = {
|
||||||
|
if (highlight.color.a < 0.001) return
|
||||||
|
|
||||||
|
g.setSmallFont()
|
||||||
|
g.background = PortLegendBg.mapA(_ * highlight.color.a)
|
||||||
|
|
||||||
|
val sidedPorts = ports.iterator.filter(_.direction.isDefined).toSeq.sorted
|
||||||
|
val legendHeight = sidedPorts.size * 8
|
||||||
|
val startY = position.y + (height - legendHeight) / 2
|
||||||
|
|
||||||
|
for ((port, line) <- sidedPorts.iterator.zipWithIndex) {
|
||||||
|
g.foreground = port.getColor.toRGBANorm.mapA(_ * highlight.color.a)
|
||||||
|
g.text(bounds.max.x + HoverInfoMargin, startY + line * 8, directionLabel(port.direction.get), shrink = 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setNormalFont()
|
||||||
|
}
|
||||||
|
|
||||||
def drawLight(g: Graphics): Unit = {}
|
def drawLight(g: Graphics): Unit = {}
|
||||||
|
|
||||||
def drawLabel(g: Graphics): Unit = {}
|
def drawLabel(g: Graphics): Unit = {
|
||||||
|
drawPortLegend(g)
|
||||||
def drawParticles(g: Graphics): Unit = {}
|
}
|
||||||
|
|
||||||
def drawPorts(g: Graphics): Unit = {
|
def drawPorts(g: Graphics): Unit = {
|
||||||
for ((port, rects) <- portsBounds) {
|
for ((port, rects) <- portsBounds) {
|
||||||
val color = port.getColor
|
val color = port.getColor
|
||||||
for (rect <- rects)
|
for (rect <- rects) {
|
||||||
g.rect(rect, color)
|
g.rect(rect, color)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def directionLabel(direction: Direction): String = {
|
||||||
|
(if (rotatable) direction.side else direction.cardinal).capitalize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Node {
|
object Node {
|
||||||
@ -290,9 +315,12 @@ object Node {
|
|||||||
protected val HoveredHighlight: RGBAColor = RGBAColor(160, 160, 160)
|
protected val HoveredHighlight: RGBAColor = RGBAColor(160, 160, 160)
|
||||||
protected val NoHighlight: RGBAColor = RGBAColor(160, 160, 160, 0)
|
protected val NoHighlight: RGBAColor = RGBAColor(160, 160, 160, 0)
|
||||||
|
|
||||||
|
private val PortLegendBg = Color.Black.withAlpha(0.8f)
|
||||||
|
|
||||||
val TexelCount = 16f
|
val TexelCount = 16f
|
||||||
val Scale = 4f
|
val Scale = 4f
|
||||||
val HighlightThickness = 2f
|
val HighlightThickness = 2f
|
||||||
|
private val HoverInfoMargin = 9f
|
||||||
|
|
||||||
val NoHighlightSize: Float = TexelCount * Scale
|
val NoHighlightSize: Float = TexelCount * Scale
|
||||||
val Size: Float = NoHighlightSize + HighlightThickness * 2f
|
val Size: Float = NoHighlightSize + HighlightThickness * 2f
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package ocelot.desktop.node
|
|||||||
|
|
||||||
import ocelot.desktop.ColorScheme
|
import ocelot.desktop.ColorScheme
|
||||||
import ocelot.desktop.color.Color
|
import ocelot.desktop.color.Color
|
||||||
|
import ocelot.desktop.graphics.IconSource
|
||||||
import totoro.ocelot.brain.util.Direction
|
import totoro.ocelot.brain.util.Direction
|
||||||
|
|
||||||
case class NodePort(direction: Option[Direction.Value] = None) extends Ordered[NodePort] {
|
case class NodePort(direction: Option[Direction.Value] = None) extends Ordered[NodePort] {
|
||||||
@ -15,6 +16,11 @@ case class NodePort(direction: Option[Direction.Value] = None) extends Ordered[N
|
|||||||
case _ => ColorScheme("PortAny")
|
case _ => ColorScheme("PortAny")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getIcon: IconSource = direction match {
|
||||||
|
case Some(direction) => IconSource.Icons.Side(direction)
|
||||||
|
case None => IconSource.Icons.SideAny
|
||||||
|
}
|
||||||
|
|
||||||
override def compare(that: NodePort): Int = this.direction.compare(that.direction)
|
override def compare(that: NodePort): Int = this.direction.compare(that.direction)
|
||||||
|
|
||||||
def toByte: Byte = direction match {
|
def toByte: Byte = direction match {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package ocelot.desktop.node
|
|||||||
|
|
||||||
import ocelot.desktop.color.Color
|
import ocelot.desktop.color.Color
|
||||||
import ocelot.desktop.geometry.Size2D
|
import ocelot.desktop.geometry.Size2D
|
||||||
import ocelot.desktop.graphics.Graphics
|
import ocelot.desktop.graphics.{Graphics, IconSource}
|
||||||
import ocelot.desktop.node.Node.Size
|
import ocelot.desktop.node.Node.Size
|
||||||
import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler}
|
import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler}
|
||||||
import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent}
|
import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent}
|
||||||
@ -39,20 +39,19 @@ class NodeTypeWidget(val nodeType: NodeType) extends Widget with MouseHandler wi
|
|||||||
val size = Spritesheet.spriteSize(nodeType.icon) * 4
|
val size = Spritesheet.spriteSize(nodeType.icon) * 4
|
||||||
|
|
||||||
g.sprite(
|
g.sprite(
|
||||||
nodeType.icon.path,
|
nodeType.icon,
|
||||||
position.x + Size / 2 - size.width / 2,
|
position.x + Size / 2 - size.width / 2,
|
||||||
position.y + Size / 2 - size.height / 2,
|
position.y + Size / 2 - size.height / 2,
|
||||||
size.width,
|
size.width,
|
||||||
size.height,
|
size.height,
|
||||||
nodeType.tier.map(TierColor.get).getOrElse(Color.White),
|
nodeType.tier.map(TierColor.get).getOrElse(Color.White),
|
||||||
nodeType.icon.animation,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def update(): Unit = {
|
override def update(): Unit = {
|
||||||
super.update()
|
super.update()
|
||||||
if (isHovered) {
|
if (isHovered) {
|
||||||
root.get.statusBar.addMouseEntry("icons/LMB", "Add node")
|
root.get.statusBar.addMouseEntry(IconSource.Icons.LMB, "Add node")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,62 +8,53 @@ import ocelot.desktop.graphics.Graphics
|
|||||||
import ocelot.desktop.node.OcelotLogParticleNode._
|
import ocelot.desktop.node.OcelotLogParticleNode._
|
||||||
import ocelot.desktop.ui.UiHandler
|
import ocelot.desktop.ui.UiHandler
|
||||||
import ocelot.desktop.ui.event.BrainEvent
|
import ocelot.desktop.ui.event.BrainEvent
|
||||||
|
import ocelot.desktop.ui.particle.Particle
|
||||||
|
|
||||||
import scala.collection.mutable
|
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
trait OcelotLogParticleNode extends Node {
|
trait OcelotLogParticleNode extends Node {
|
||||||
private case class LogParticle(
|
private var queuedParticles: Int = 0
|
||||||
var time: Float = -LogParticleGrow,
|
|
||||||
angle: Float = Random.between(0f, 2 * math.Pi.toFloat * LogParticleMaxAngle),
|
|
||||||
)
|
|
||||||
|
|
||||||
// access should be synchronized because log particles are added in the update thread
|
|
||||||
private val logParticles = mutable.ArrayDeque.empty[LogParticle]
|
|
||||||
|
|
||||||
private def addLogParticle(): Unit = logParticles.synchronized {
|
|
||||||
if (logParticles.length < MaxLogParticles) {
|
|
||||||
logParticles += LogParticle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventHandlers += {
|
eventHandlers += {
|
||||||
case BrainEvent(OcelotInterface.LogEvent.CardToUser(_, _)) =>
|
case BrainEvent(OcelotInterface.LogEvent.CardToUser(_, _)) =>
|
||||||
addLogParticle()
|
queuedParticles += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
override def update(): Unit = {
|
override def update(): Unit = {
|
||||||
super.update()
|
super.update()
|
||||||
|
spawnParticles()
|
||||||
logParticles.synchronized {
|
|
||||||
logParticles.foreach(particle => particle.time += LogParticleMoveSpeed * UiHandler.dt)
|
|
||||||
logParticles.filterInPlace(_.time <= 1f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def drawLogParticles(g: Graphics): Unit = logParticles.synchronized {
|
private def spawnParticles(): Unit = {
|
||||||
for (particle <- logParticles) {
|
val system = UiHandler.root.workspaceView.particleSystem
|
||||||
val size = (1 + particle.time / LogParticleGrow).clamp() * LogParticleSize
|
val toSpawn = queuedParticles min (MaxLogParticles - system.count[LogParticle](Some(this))) max 0
|
||||||
val offset = particle.time.clamp() * LogParticleMoveDistance
|
|
||||||
val alpha = 1 - particle.time.clamp()
|
|
||||||
|
|
||||||
val r1 = (bounds.w max bounds.h) / math.sqrt(2) + offset + LogParticlePadding
|
for (_ <- 0 until toSpawn) {
|
||||||
|
system.add(new LogParticle)
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedParticles = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LogParticle extends Particle(time = -LogParticleGrow, speed = LogParticleMoveSpeed, origin = Some(this)) {
|
||||||
|
private val angle: Float = Random.between(0f, 2 * math.Pi.toFloat * LogParticleMaxAngle)
|
||||||
|
override def draw(g: Graphics): Unit = {
|
||||||
|
val size = (1 + time / LogParticleGrow).clamped() * LogParticleSize
|
||||||
|
val offset = time.clamped() * LogParticleMoveDistance
|
||||||
|
val alpha = 1 - time.clamped()
|
||||||
|
|
||||||
|
val r1 = (bounds.w max bounds.h) / math.sqrt(2).toFloat + offset + LogParticlePadding
|
||||||
val r2 = r1 + size
|
val r2 = r1 + size
|
||||||
|
|
||||||
for (i <- 0 until LogParticleCount) {
|
for (i <- 0 until LogParticleCount) {
|
||||||
val angle = particle.angle + (2 * math.Pi).toFloat * i / LogParticleCount
|
val a = angle + (2 * math.Pi).toFloat * i / LogParticleCount
|
||||||
val v = Vector2D.unit(angle)
|
val v = Vector2D.unit(a)
|
||||||
val p1 = v * r1 + bounds.center
|
val p1 = v * r1 + bounds.center
|
||||||
val p2 = v * r2 + bounds.center
|
val p2 = v * r2 + bounds.center
|
||||||
g.line(p1, p2, 1f, ColorScheme("LogParticle").mapA(_ => alpha))
|
g.line(p1, p2, 1f, ColorScheme("LogParticle").mapA(_ => alpha))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def drawParticles(g: Graphics): Unit = {
|
|
||||||
super.drawParticles(g)
|
|
||||||
drawLogParticles(g)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object OcelotLogParticleNode {
|
object OcelotLogParticleNode {
|
||||||
|
|||||||
@ -4,18 +4,22 @@ import ocelot.desktop.audio.SoundSource
|
|||||||
import ocelot.desktop.geometry.Vector3D
|
import ocelot.desktop.geometry.Vector3D
|
||||||
import ocelot.desktop.{OcelotDesktop, Settings}
|
import ocelot.desktop.{OcelotDesktop, Settings}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates sound sources' position depending on where the camera is.
|
||||||
|
*
|
||||||
|
* @note OpenAL only applies positioning to mono sources!
|
||||||
|
* If your source is stereo, this trait will have no audible effect.
|
||||||
|
*/
|
||||||
trait PositionalSoundSourcesNode extends Node {
|
trait PositionalSoundSourcesNode extends Node {
|
||||||
// Every node can have multiple sound sources playing at the same time
|
// Every node can have multiple sound sources playing at the same time
|
||||||
def soundSources: Seq[SoundSource]
|
def soundSources: Seq[SoundSource] = Seq()
|
||||||
|
|
||||||
override def update(): Unit = {
|
override def update(): Unit = {
|
||||||
super.update()
|
super.update()
|
||||||
|
|
||||||
var soundPosition: Vector3D = null
|
|
||||||
|
|
||||||
// Calculating position of sound source relative to center of workspace
|
// Calculating position of sound source relative to center of workspace
|
||||||
// but only if corresponding setting is enabled
|
// but only if corresponding setting is enabled
|
||||||
if (Settings.get.soundPositional) {
|
val soundPosition = if (Settings.get.soundPositional) {
|
||||||
val rootWidthHalf = OcelotDesktop.root.width / 2
|
val rootWidthHalf = OcelotDesktop.root.width / 2
|
||||||
val rootHeightHalf = OcelotDesktop.root.height / 2
|
val rootHeightHalf = OcelotDesktop.root.height / 2
|
||||||
|
|
||||||
@ -26,13 +30,13 @@ trait PositionalSoundSourcesNode extends Node {
|
|||||||
// large monitors the sound may become too "non-audiophile"
|
// large monitors the sound may become too "non-audiophile"
|
||||||
val limit = 0.05f
|
val limit = 0.05f
|
||||||
|
|
||||||
soundPosition = Vector3D(
|
Vector3D(
|
||||||
(nodeCenterX - rootWidthHalf) / rootWidthHalf * limit,
|
(nodeCenterX - rootWidthHalf) / rootWidthHalf * limit,
|
||||||
(nodeCenterY - rootHeightHalf) / rootHeightHalf * limit,
|
(nodeCenterY - rootHeightHalf) / rootHeightHalf * limit,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
soundPosition = Vector3D.Zero
|
Vector3D.Zero
|
||||||
}
|
}
|
||||||
|
|
||||||
for (soundSource <- soundSources)
|
for (soundSource <- soundSources)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package ocelot.desktop.node
|
package ocelot.desktop.node
|
||||||
|
|
||||||
|
import ocelot.desktop.graphics.IconSource
|
||||||
import ocelot.desktop.ui.event.sources.KeyEvents
|
import ocelot.desktop.ui.event.sources.KeyEvents
|
||||||
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
|
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
|
||||||
|
|
||||||
@ -22,6 +23,6 @@ trait ShiftClickNode extends Node {
|
|||||||
super.update()
|
super.update()
|
||||||
|
|
||||||
if (isHovered || isMoving)
|
if (isHovered || isMoving)
|
||||||
root.get.statusBar.addKeyMouseEntry("icons/LMB", "SHIFT", hoveredShiftStatusBarText)
|
root.get.statusBar.addKeyMouseEntry(IconSource.Icons.LMB, "SHIFT", hoveredShiftStatusBarText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
79
src/main/scala/ocelot/desktop/node/SmokeParticleNode.scala
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package ocelot.desktop.node
|
||||||
|
|
||||||
|
import ocelot.desktop.color.RGBAColorNorm
|
||||||
|
import ocelot.desktop.geometry.{Size2D, Vector2D}
|
||||||
|
import ocelot.desktop.graphics.{Graphics, IconSource}
|
||||||
|
import ocelot.desktop.node.SmokeParticleNode._
|
||||||
|
import ocelot.desktop.ui.UiHandler
|
||||||
|
import ocelot.desktop.ui.particle.Particle
|
||||||
|
import ocelot.desktop.util.Spritesheet
|
||||||
|
|
||||||
|
import scala.util.Random
|
||||||
|
|
||||||
|
trait SmokeParticleNode extends Node {
|
||||||
|
protected def emitSmoke(): Unit = synchronized {
|
||||||
|
for (_ <- 1 to randomCount) {
|
||||||
|
UiHandler.root.workspaceView.particleSystem.add(new SmokeParticle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SmokeParticle extends Particle(ttl = randomDuration) {
|
||||||
|
private val color: RGBAColorNorm = randomColor
|
||||||
|
private var velocity: Vector2D = Vector2D(randomVelocityComponent, randomVelocityComponent)
|
||||||
|
private var offset: Vector2D = Vector2D(0, 0)
|
||||||
|
|
||||||
|
override def update(dt: Float): Unit = {
|
||||||
|
time += dt
|
||||||
|
offset += (velocity + SmokeParticleVolatilizationSpeed) * dt * speed
|
||||||
|
velocity *= math.pow(SmokeParticleVelocityDamping, dt).toFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
override def draw(g: Graphics): Unit = {
|
||||||
|
val spriteRect = Spritesheet.sprites(IconSource.Particles.Smoke.path)
|
||||||
|
|
||||||
|
val animationFrameCount = spriteRect.h / spriteRect.w
|
||||||
|
val animationFrame = (time / ttl * animationFrameCount).toInt
|
||||||
|
|
||||||
|
val particlePosition = bounds.center + offset - SmokeParticleSize.toVector * .5f
|
||||||
|
|
||||||
|
g.sprite(
|
||||||
|
IconSource.Particles.Smoke.path,
|
||||||
|
particlePosition.x,
|
||||||
|
particlePosition.y,
|
||||||
|
SmokeParticleSize.width,
|
||||||
|
SmokeParticleSize.height,
|
||||||
|
color,
|
||||||
|
Some(spriteRect.copy(
|
||||||
|
y = spriteRect.y + animationFrame * spriteRect.w,
|
||||||
|
h = spriteRect.w,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SmokeParticleNode {
|
||||||
|
private final val SmokeParticleSize: Size2D = Size2D(32, 32)
|
||||||
|
private final val SmokeParticleVelocityRange: Float = 300
|
||||||
|
private final val SmokeParticleVolatilizationSpeed: Vector2D = Vector2D(0, -50)
|
||||||
|
private final val SmokeParticleVelocityDamping: Float = .1f
|
||||||
|
private final val SmokeParticleCount: (Int, Int) = (10, 20)
|
||||||
|
private final val SmokeParticleAnimationDuration: (Float, Float) = (1f, 4f)
|
||||||
|
|
||||||
|
private def randomVelocityComponent: Float = Random.between(
|
||||||
|
-SmokeParticleVelocityRange,
|
||||||
|
SmokeParticleVelocityRange,
|
||||||
|
)
|
||||||
|
private def randomColor: RGBAColorNorm = {
|
||||||
|
val channel = Random.between(.5f, .9f)
|
||||||
|
RGBAColorNorm(channel, channel, channel)
|
||||||
|
}
|
||||||
|
private def randomDuration: Float = Random.between(
|
||||||
|
SmokeParticleAnimationDuration._1,
|
||||||
|
SmokeParticleAnimationDuration._2,
|
||||||
|
)
|
||||||
|
private def randomCount: Int = Random.between(
|
||||||
|
SmokeParticleCount._1,
|
||||||
|
SmokeParticleCount._2,
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
package ocelot.desktop.node
|
package ocelot.desktop.node
|
||||||
|
|
||||||
|
import ocelot.desktop.graphics.IconSource
|
||||||
import ocelot.desktop.ui.event.sources.KeyEvents
|
import ocelot.desktop.ui.event.sources.KeyEvents
|
||||||
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
|
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
|
||||||
import ocelot.desktop.ui.widget.window.{Window, Windowed}
|
import ocelot.desktop.ui.widget.window.{Window, Windowed}
|
||||||
import org.lwjgl.input.Keyboard
|
|
||||||
|
|
||||||
trait WindowedNode[T <: Window] extends Node with Windowed[T] {
|
trait WindowedNode[T <: Window] extends Node with Windowed[T] {
|
||||||
override def dispose(): Unit = {
|
override def dispose(): Unit = {
|
||||||
@ -14,7 +14,7 @@ trait WindowedNode[T <: Window] extends Node with Windowed[T] {
|
|||||||
|
|
||||||
override def update(): Unit = {
|
override def update(): Unit = {
|
||||||
if (isHovered || isMoving)
|
if (isHovered || isMoving)
|
||||||
root.get.statusBar.addMouseEntry("icons/LMB", if (windowCreated && window.isOpen) "Close" else "Open")
|
root.get.statusBar.addMouseEntry(IconSource.Icons.LMB, if (windowCreated && window.isOpen) "Close" else "Open")
|
||||||
|
|
||||||
super.update()
|
super.update()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import ocelot.desktop.node.EntityNode
|
|||||||
import totoro.ocelot.brain.entity.Cable
|
import totoro.ocelot.brain.entity.Cable
|
||||||
|
|
||||||
class CableNode(val cable: Cable) extends EntityNode(cable) {
|
class CableNode(val cable: Cable) extends EntityNode(cable) {
|
||||||
override def iconSource: IconSource = IconSource.Nodes.Cable
|
override def icon: IconSource = IconSource.Nodes.Cable
|
||||||
|
|
||||||
|
override def rotatable: Boolean = false
|
||||||
|
|
||||||
override def minimumSize: Size2D = Size2D(36, 36)
|
override def minimumSize: Size2D = Size2D(36, 36)
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import ocelot.desktop.node.{EntityNode, LabeledEntityNode, WindowedNode}
|
|||||||
import ocelot.desktop.windows.CameraWindow
|
import ocelot.desktop.windows.CameraWindow
|
||||||
|
|
||||||
class CameraNode(val camera: Camera) extends EntityNode(camera) with LabeledEntityNode with WindowedNode[CameraWindow] {
|
class CameraNode(val camera: Camera) extends EntityNode(camera) with LabeledEntityNode with WindowedNode[CameraWindow] {
|
||||||
override def iconSource: IconSource = IconSource.Nodes.Camera
|
override def icon: IconSource = IconSource.Nodes.Camera
|
||||||
|
|
||||||
|
override def rotatable: Boolean = true
|
||||||
|
|
||||||
override def createWindow(): CameraWindow = new CameraWindow(this)
|
override def createWindow(): CameraWindow = new CameraWindow(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,9 @@ import ocelot.desktop.windows.ChestWindow
|
|||||||
class ChestNode extends LabeledNode with WindowedNode[ChestWindow] with PersistedInventory {
|
class ChestNode extends LabeledNode with WindowedNode[ChestWindow] with PersistedInventory {
|
||||||
override type I = Item with PersistableItem
|
override type I = Item with PersistableItem
|
||||||
|
|
||||||
override def iconSource: IconSource = IconSource.Nodes.Chest
|
override def icon: IconSource = IconSource.Nodes.Chest
|
||||||
|
|
||||||
|
override def rotatable: Boolean = false
|
||||||
|
|
||||||
override def minimumSize: Size2D = Size2D(28, 30) * 2 + 4
|
override def minimumSize: Size2D = Size2D(28, 30) * 2 + 4
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,8 @@ class ColorfulLampNode(val lamp: ColorfulLamp) extends EntityNode(lamp) with Lab
|
|||||||
private var lastColor: RGBAColor = RGBAColor(0, 0, 0)
|
private var lastColor: RGBAColor = RGBAColor(0, 0, 0)
|
||||||
private var mouseHover: Boolean = false
|
private var mouseHover: Boolean = false
|
||||||
|
|
||||||
|
override def rotatable: Boolean = false
|
||||||
|
|
||||||
override def label: Option[String] = super.label.filter(_ => mouseHover)
|
override def label: Option[String] = super.label.filter(_ => mouseHover)
|
||||||
|
|
||||||
override def draw(g: Graphics): Unit = {
|
override def draw(g: Graphics): Unit = {
|
||||||
@ -22,12 +24,12 @@ class ColorfulLampNode(val lamp: ColorfulLamp) extends EntityNode(lamp) with Lab
|
|||||||
)
|
)
|
||||||
|
|
||||||
g.rect(position.x + 2, position.y + 2, size.width - 4, size.height - 4, lastColor)
|
g.rect(position.x + 2, position.y + 2, size.width - 4, size.height - 4, lastColor)
|
||||||
g.sprite(IconSource.Nodes.Lamp.Frame, position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
g.sprite(IconSource.Nodes.LampFrame, position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def drawLight(g: Graphics): Unit = {
|
override def drawLight(g: Graphics): Unit = {
|
||||||
super.drawLight(g)
|
super.drawLight(g)
|
||||||
g.sprite(IconSource.Nodes.Lamp.Glow, position - size / 2, size * 2, lastColor)
|
g.sprite(IconSource.Nodes.LampGlow, position - size / 2, size * 2, lastColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
eventHandlers += {
|
eventHandlers += {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package ocelot.desktop.node.nodes
|
|||||||
|
|
||||||
import ocelot.desktop.color.Color
|
import ocelot.desktop.color.Color
|
||||||
import ocelot.desktop.graphics.{Graphics, IconSource}
|
import ocelot.desktop.graphics.{Graphics, IconSource}
|
||||||
|
import ocelot.desktop.inventory.item.SelfDestructingCardItem
|
||||||
import ocelot.desktop.node.Node.HighlightThickness
|
import ocelot.desktop.node.Node.HighlightThickness
|
||||||
import ocelot.desktop.node.{ComputerAwareNode, WindowedNode}
|
import ocelot.desktop.node.{ComputerAwareNode, WindowedNode}
|
||||||
import ocelot.desktop.ui.event.ClickEvent
|
import ocelot.desktop.ui.event.ClickEvent
|
||||||
@ -15,10 +16,15 @@ import totoro.ocelot.brain.entity.traits.Inventory
|
|||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
|
|
||||||
class ComputerNode(val computerCase: Case)
|
class ComputerNode(val computerCase: Case)
|
||||||
extends ComputerAwareNode(computerCase) with AudibleComputerAware with WindowedNode[ComputerWindow] {
|
extends ComputerAwareNode(computerCase)
|
||||||
override val iconSource: IconSource = IconSource.Nodes.Computer.Default
|
with AudibleComputerAware
|
||||||
|
with WindowedNode[ComputerWindow] {
|
||||||
|
|
||||||
|
override val icon: IconSource = IconSource.Nodes.Computer.Default
|
||||||
override def iconColor: Color = TierColor.get(computerCase.tier)
|
override def iconColor: Color = TierColor.get(computerCase.tier)
|
||||||
|
|
||||||
|
override def rotatable: Boolean = true
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
addPowerContextMenuEntries(menu)
|
addPowerContextMenuEntries(menu)
|
||||||
addTierContextMenuEntries(menu)
|
addTierContextMenuEntries(menu)
|
||||||
@ -28,6 +34,12 @@ class ComputerNode(val computerCase: Case)
|
|||||||
super.setupContextMenu(menu, event)
|
super.setupContextMenu(menu, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override protected def selfDestructingCards: IterableOnce[SelfDestructingCardItem] = {
|
||||||
|
inventoryIterator.flatMap(_.get).collect {
|
||||||
|
case item: SelfDestructingCardItem => item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override def update(): Unit = {
|
override def update(): Unit = {
|
||||||
super.update()
|
super.update()
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,9 @@ class DiskDriveNode(entity: FloppyDiskDrive)
|
|||||||
with ShiftClickNode
|
with ShiftClickNode
|
||||||
with WindowedNode[DiskDriveWindow] {
|
with WindowedNode[DiskDriveWindow] {
|
||||||
|
|
||||||
override def iconSource: IconSource = IconSource.Nodes.DiskDrive.Default
|
override def icon: IconSource = IconSource.Nodes.DiskDrive.Default
|
||||||
|
|
||||||
|
override def rotatable: Boolean = true
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
if (isFloppyItemPresent) {
|
if (isFloppyItemPresent) {
|
||||||
|
|||||||
@ -8,7 +8,9 @@ import totoro.ocelot.brain.entity.HologramProjector
|
|||||||
class HologramProjectorNode(val hologramProjector: 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 icon: IconSource = IconSource.Nodes.HologramProjector(hologramProjector.tier)
|
||||||
|
|
||||||
|
override def rotatable: Boolean = false
|
||||||
|
|
||||||
override def createWindow(): HologramProjectorWindow = new HologramProjectorWindow(this)
|
override def createWindow(): HologramProjectorWindow = new HologramProjectorWindow(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,9 @@ import totoro.ocelot.brain.event.{EventBus, NoteBlockTriggerEvent}
|
|||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
class IronNoteBlockNode(val ironNoteBlock: IronNoteBlock) extends NoteBlockNodeBase(ironNoteBlock) {
|
class IronNoteBlockNode(val ironNoteBlock: IronNoteBlock) extends NoteBlockNodeBase(ironNoteBlock) {
|
||||||
override def iconSource: IconSource = IconSource.Nodes.IronNoteBlock
|
override def icon: IconSource = IconSource.Nodes.IronNoteBlock
|
||||||
|
|
||||||
|
override def rotatable: Boolean = false
|
||||||
|
|
||||||
override def onClick(event: ClickEvent): Unit = {
|
override def onClick(event: ClickEvent): Unit = {
|
||||||
event match {
|
event match {
|
||||||
|
|||||||
@ -20,7 +20,8 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
with ComputerAware
|
with ComputerAware
|
||||||
with DefaultSlotItemsFillable
|
with DefaultSlotItemsFillable
|
||||||
with WindowedNode[ComputerWindow] {
|
with WindowedNode[ComputerWindow] {
|
||||||
override val iconSource: IconSource = IconSource.Nodes.Microcontroller.Default
|
|
||||||
|
override val icon: IconSource = IconSource.Nodes.Microcontroller.Default
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
addPowerContextMenuEntries(menu)
|
addPowerContextMenuEntries(menu)
|
||||||
@ -54,6 +55,8 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
override def getNodeByPort(port: NodePort): network.Node =
|
override def getNodeByPort(port: NodePort): network.Node =
|
||||||
microcontroller.sidedNode(port.direction.get)
|
microcontroller.sidedNode(port.direction.get)
|
||||||
|
|
||||||
|
override def rotatable: Boolean = true
|
||||||
|
|
||||||
override def shouldReceiveEventsFor(address: String): Boolean = {
|
override def shouldReceiveEventsFor(address: String): Boolean = {
|
||||||
address == microcontroller.machine.node.address ||
|
address == microcontroller.machine.node.address ||
|
||||||
super.shouldReceiveEventsFor(address)
|
super.shouldReceiveEventsFor(address)
|
||||||
@ -65,6 +68,12 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
override def computerType: ComputerType = ComputerType.Microcontroller
|
override def computerType: ComputerType = ComputerType.Microcontroller
|
||||||
override def brainInventory: Inventory = microcontroller.inventory.owner
|
override def brainInventory: Inventory = microcontroller.inventory.owner
|
||||||
|
|
||||||
|
override protected def selfDestructingCards: IterableOnce[SelfDestructingCardItem] = {
|
||||||
|
inventoryIterator.flatMap(_.get).collect {
|
||||||
|
case item: SelfDestructingCardItem => item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override def addSlotsBasedOnTier(): Unit = {
|
override def addSlotsBasedOnTier(): Unit = {
|
||||||
microcontroller.tier match {
|
microcontroller.tier match {
|
||||||
case Tier.One =>
|
case Tier.One =>
|
||||||
@ -120,23 +129,26 @@ class MicrocontrollerNode(val microcontroller: Microcontroller)
|
|||||||
|
|
||||||
override def fillSlotsWithDefaultItems(): Unit = {
|
override def fillSlotsWithDefaultItems(): Unit = {
|
||||||
cardSlots(0).item = {
|
cardSlots(0).item = {
|
||||||
if (microcontroller.tier == Tier.Two)
|
if (microcontroller.tier >= Tier.Two) {
|
||||||
WirelessNetworkCardItem.Tier2.Factory.build()
|
WirelessNetworkCardItem.Tier2.Factory.build()
|
||||||
else
|
} else {
|
||||||
WirelessNetworkCardItem.Tier1.Factory.build()
|
WirelessNetworkCardItem.Tier1.Factory.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cardSlots(1).item = {
|
cardSlots(1).item = {
|
||||||
if (microcontroller.tier == Tier.Two)
|
if (microcontroller.tier == Tier.Creative) {
|
||||||
RedstoneCardItem.Tier2.Factory.build()
|
RedstoneCardItem.Tier2.Factory.build()
|
||||||
else
|
} else {
|
||||||
RedstoneCardItem.Tier1.Factory.build()
|
RedstoneCardItem.Tier1.Factory.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cpuSlot.item = new CpuItem.Factory(Tier.One).build()
|
cpuSlot.item = new CpuItem.Factory(Tier.One).build()
|
||||||
|
|
||||||
for (memorySlot <- memorySlots)
|
for (memorySlot <- memorySlots) {
|
||||||
memorySlot.item = new MemoryItem.Factory(Tier.One.toExtended(false)).build()
|
memorySlot.item = new MemoryItem.Factory(Tier.One.toExtended(false)).build()
|
||||||
|
}
|
||||||
|
|
||||||
eepromSlot.item = EepromItem.Factory.Empty.build()
|
eepromSlot.item = EepromItem.Factory.Empty.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,12 @@ import totoro.ocelot.brain.entity.NoteBlock
|
|||||||
import totoro.ocelot.brain.event.{EventBus, NoteBlockTriggerEvent}
|
import totoro.ocelot.brain.event.{EventBus, NoteBlockTriggerEvent}
|
||||||
|
|
||||||
class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBlock) {
|
class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBlock) {
|
||||||
override def iconSource: IconSource = IconSource.Nodes.NoteBlock
|
override def icon: IconSource = IconSource.Nodes.NoteBlock
|
||||||
|
|
||||||
|
override def rotatable: Boolean = false
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
menu.addEntry(new ContextMenuSubmenu("Instrument", Some(ContextMenuIcon(IconSource.Guitar))) {
|
menu.addEntry(new ContextMenuSubmenu("Instrument", Some(ContextMenuIcon(IconSource.Icons.Guitar))) {
|
||||||
{
|
{
|
||||||
val maxLen = NoteBlockNode.Instruments.map(_._2.length).max
|
val maxLen = NoteBlockNode.Instruments.map(_._2.length).max
|
||||||
|
|
||||||
|
|||||||
@ -3,15 +3,14 @@ package ocelot.desktop.node.nodes
|
|||||||
import ocelot.desktop.ColorScheme
|
import ocelot.desktop.ColorScheme
|
||||||
import ocelot.desktop.audio.{SoundBuffers, SoundCategory, SoundSource}
|
import ocelot.desktop.audio.{SoundBuffers, SoundCategory, SoundSource}
|
||||||
import ocelot.desktop.geometry.{Size2D, Vector2D}
|
import ocelot.desktop.geometry.{Size2D, Vector2D}
|
||||||
import ocelot.desktop.graphics.Graphics
|
import ocelot.desktop.graphics.{Graphics, IconSource}
|
||||||
import ocelot.desktop.node.{EntityNode, LabeledEntityNode}
|
import ocelot.desktop.node.{EntityNode, LabeledEntityNode}
|
||||||
import ocelot.desktop.ui.UiHandler
|
import ocelot.desktop.ui.UiHandler
|
||||||
import ocelot.desktop.ui.event.BrainEvent
|
import ocelot.desktop.ui.event.BrainEvent
|
||||||
|
import ocelot.desktop.ui.particle.Particle
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
||||||
import totoro.ocelot.brain.event.NoteBlockTriggerEvent
|
import totoro.ocelot.brain.event.NoteBlockTriggerEvent
|
||||||
|
|
||||||
import scala.collection.mutable
|
|
||||||
|
|
||||||
abstract class NoteBlockNodeBase(entity: Entity with Environment) extends EntityNode(entity) with LabeledEntityNode {
|
abstract class NoteBlockNodeBase(entity: Entity with Environment) extends EntityNode(entity) with LabeledEntityNode {
|
||||||
eventHandlers += {
|
eventHandlers += {
|
||||||
case BrainEvent(event: NoteBlockTriggerEvent) =>
|
case BrainEvent(event: NoteBlockTriggerEvent) =>
|
||||||
@ -22,31 +21,25 @@ abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Entity
|
|||||||
volume = event.volume.toFloat.min(1f).max(0f),
|
volume = event.volume.toFloat.min(1f).max(0f),
|
||||||
).play()
|
).play()
|
||||||
|
|
||||||
addParticle(event.pitch)
|
UiHandler.root.workspaceView.particleSystem.add(new NoteParticle(event.pitch))
|
||||||
}
|
|
||||||
|
|
||||||
private val particles = mutable.ArrayBuffer[(Float, Int)]()
|
|
||||||
|
|
||||||
private def addParticle(pitch: Int): Unit = {
|
|
||||||
synchronized {
|
|
||||||
particles += ((0f, pitch))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
particles.mapInPlace { case (t, p) => (t + 1.2f * UiHandler.dt, p) }
|
|
||||||
particles.filterInPlace(_._1 <= 1f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def update(): Unit = {
|
override def update(): Unit = {
|
||||||
super.update()
|
super.update()
|
||||||
|
if (isHovered || isMoving) {
|
||||||
|
root.get.statusBar.addMouseEntry(IconSource.Icons.LMB, "Play sample")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isHovered || isMoving)
|
private class NoteParticle(pitch: Int) extends Particle(speed = 1.2f) {
|
||||||
root.get.statusBar.addMouseEntry("icons/LMB", "Play sample")
|
override def draw(g: Graphics): Unit = {
|
||||||
|
val col = ColorScheme("Note" + pitch.min(24).max(0)).withAlpha(1 - (2 * time - 1).min(1).max(0))
|
||||||
|
g.sprite(
|
||||||
|
IconSource.Particles.Note,
|
||||||
|
position + Vector2D(pitch / 24f * 40f + 5, height / 2 - 10 - 100 * time),
|
||||||
|
Size2D(14, 20),
|
||||||
|
col,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,9 @@ class OcelotBlockNode(val ocelot: OcelotBlock)
|
|||||||
|
|
||||||
override def name: String = "Ocelot Block"
|
override def name: String = "Ocelot Block"
|
||||||
|
|
||||||
override def iconSource: IconSource = IconSource.Nodes.OcelotBlock.Default
|
override def icon: IconSource = IconSource.Nodes.OcelotBlock.Default
|
||||||
|
|
||||||
|
override def rotatable: Boolean = false
|
||||||
|
|
||||||
override def ocelotInterface: OcelotInterface = ocelot
|
override def ocelotInterface: OcelotInterface = ocelot
|
||||||
|
|
||||||
@ -62,17 +64,16 @@ class OcelotBlockNode(val ocelot: OcelotBlock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def drawActivity(g: Graphics, icon: IconSource, lastActivity: Long, currentTime: Long): Unit = {
|
private def drawActivity(g: Graphics, icon: IconSource, lastActivity: Long, currentTime: Long): Unit = {
|
||||||
val alpha = (1 - (currentTime - lastActivity) / ActivityFadeOutMs).clamp()
|
val alpha = (1 - (currentTime - lastActivity) / ActivityFadeOutMs).clamped()
|
||||||
|
|
||||||
if (alpha > 0) {
|
if (alpha > 0) {
|
||||||
g.sprite(
|
g.sprite(
|
||||||
icon.path,
|
icon,
|
||||||
position.x + HighlightThickness,
|
position.x + HighlightThickness,
|
||||||
position.y + HighlightThickness,
|
position.y + HighlightThickness,
|
||||||
size.width - HighlightThickness * 2,
|
size.width - HighlightThickness * 2,
|
||||||
size.height - HighlightThickness * 2,
|
size.height - HighlightThickness * 2,
|
||||||
RGBAColorNorm(1f, 1f, 1f, alpha),
|
RGBAColorNorm(1f, 1f, 1f, alpha),
|
||||||
icon.animation,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,9 @@ import ocelot.desktop.windows.OpenFMRadioWindow
|
|||||||
|
|
||||||
class OpenFMRadioNode(val openFMRadio: OpenFMRadio)
|
class OpenFMRadioNode(val openFMRadio: OpenFMRadio)
|
||||||
extends EntityNode(openFMRadio) with LabeledEntityNode with WindowedNode[OpenFMRadioWindow] {
|
extends EntityNode(openFMRadio) with LabeledEntityNode with WindowedNode[OpenFMRadioWindow] {
|
||||||
override def iconSource: IconSource = IconSource.Nodes.OpenFMRadio
|
override def icon: IconSource = IconSource.Nodes.OpenFMRadio
|
||||||
|
|
||||||
|
override def rotatable: Boolean = true
|
||||||
|
|
||||||
private var lastTick = OcelotDesktop.ticker.tick
|
private var lastTick = OcelotDesktop.ticker.tick
|
||||||
private var animationIndex = 0
|
private var animationIndex = 0
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package ocelot.desktop.node.nodes
|
|||||||
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
|
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
|
||||||
import ocelot.desktop.graphics.{Graphics, IconSource}
|
import ocelot.desktop.graphics.{Graphics, IconSource}
|
||||||
import ocelot.desktop.inventory.Item
|
import ocelot.desktop.inventory.Item
|
||||||
import ocelot.desktop.inventory.item.{DiskDriveMountableItem, ServerItem}
|
import ocelot.desktop.inventory.item.{DiskDriveMountableItem, SelfDestructingCardItem, ServerItem}
|
||||||
import ocelot.desktop.inventory.traits.RackMountableItem
|
import ocelot.desktop.inventory.traits.RackMountableItem
|
||||||
import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize, Size, TexelCount}
|
import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize, Size, TexelCount}
|
||||||
import ocelot.desktop.node.{ComputerAwareNode, NodePort, WindowedNode}
|
import ocelot.desktop.node.{ComputerAwareNode, NodePort, WindowedNode}
|
||||||
@ -18,7 +18,7 @@ import totoro.ocelot.brain.network
|
|||||||
import totoro.ocelot.brain.util.Direction
|
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 val icon: IconSource = IconSource.Nodes.Rack.Empty
|
||||||
override def exposeAddress = false
|
override def exposeAddress = false
|
||||||
|
|
||||||
override def ports: Array[NodePort] = Array(
|
override def ports: Array[NodePort] = Array(
|
||||||
@ -31,6 +31,8 @@ class RackNode(val rack: Rack) extends ComputerAwareNode(rack) with WindowedNode
|
|||||||
|
|
||||||
override def getNodeByPort(port: NodePort): network.Node = rack.sidedNode(port.direction.get)
|
override def getNodeByPort(port: NodePort): network.Node = rack.sidedNode(port.direction.get)
|
||||||
|
|
||||||
|
override def rotatable: Boolean = true
|
||||||
|
|
||||||
override def shouldReceiveEventsFor(address: String): Boolean = {
|
override def shouldReceiveEventsFor(address: String): Boolean = {
|
||||||
super.shouldReceiveEventsFor(address) ||
|
super.shouldReceiveEventsFor(address) ||
|
||||||
rack.inventory.entities.exists {
|
rack.inventory.entities.exists {
|
||||||
@ -43,6 +45,15 @@ class RackNode(val rack: Rack) extends ComputerAwareNode(rack) with WindowedNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override protected def selfDestructingCards: IterableOnce[SelfDestructingCardItem] = {
|
||||||
|
inventoryIterator.flatMap(_.get).collect({
|
||||||
|
case item: ServerItem =>
|
||||||
|
item.inventoryIterator.flatMap(_.get).collect {
|
||||||
|
case card: SelfDestructingCardItem => card
|
||||||
|
}
|
||||||
|
}).flatten
|
||||||
|
}
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
RackNode.addContextMenuEntriesOfMountable(menu, getMountableByClick(event))
|
RackNode.addContextMenuEntriesOfMountable(menu, getMountableByClick(event))
|
||||||
|
|
||||||
@ -183,7 +194,7 @@ object RackNode {
|
|||||||
def addContextMenuEntriesOfMountable(menu: ContextMenu, item: Option[RackMountableItem]): Unit = {
|
def addContextMenuEntriesOfMountable(menu: ContextMenu, item: Option[RackMountableItem]): Unit = {
|
||||||
item match {
|
item match {
|
||||||
case Some(serverItem: ServerItem) =>
|
case Some(serverItem: ServerItem) =>
|
||||||
menu.addEntry(ContextMenuEntry("Set up", IconSource.Window) {
|
menu.addEntry(ContextMenuEntry("Set up", IconSource.Icons.Window) {
|
||||||
serverItem.window.open()
|
serverItem.window.open()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -206,7 +217,7 @@ object RackNode {
|
|||||||
"Change floppy"
|
"Change floppy"
|
||||||
else
|
else
|
||||||
"Set floppy",
|
"Set floppy",
|
||||||
IconSource.Save,
|
IconSource.Icons.Save,
|
||||||
) {
|
) {
|
||||||
diskDriveMountableItem.window.open()
|
diskDriveMountableItem.window.open()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -8,26 +8,34 @@ import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize}
|
|||||||
import ocelot.desktop.node.{EntityNode, LabeledEntityNode, WindowedNode}
|
import ocelot.desktop.node.{EntityNode, LabeledEntityNode, WindowedNode}
|
||||||
import ocelot.desktop.ui.event.ClickEvent
|
import ocelot.desktop.ui.event.ClickEvent
|
||||||
import ocelot.desktop.ui.event.handlers.DiskActivityHandler
|
import ocelot.desktop.ui.event.handlers.DiskActivityHandler
|
||||||
import ocelot.desktop.ui.widget.contextmenu.ContextMenu
|
import ocelot.desktop.ui.widget.DiskEditWindow
|
||||||
|
import ocelot.desktop.ui.widget.DiskEditWindow.EditableDisk
|
||||||
|
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||||
import ocelot.desktop.ui.widget.slot.HddSlotWidget
|
import ocelot.desktop.ui.widget.slot.HddSlotWidget
|
||||||
|
import ocelot.desktop.ui.widget.window.Windowed
|
||||||
import ocelot.desktop.util.{DefaultSlotItemsFillable, DrawUtils}
|
import ocelot.desktop.util.{DefaultSlotItemsFillable, DrawUtils}
|
||||||
import ocelot.desktop.windows.RaidWindow
|
import ocelot.desktop.windows.RaidWindow
|
||||||
import totoro.ocelot.brain.entity.Raid
|
import totoro.ocelot.brain.entity.Raid
|
||||||
import totoro.ocelot.brain.entity.traits.Inventory
|
import totoro.ocelot.brain.entity.traits.{Environment, Inventory}
|
||||||
|
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
|
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
class RaidNode(val raid: Raid)
|
class RaidNode(val raid: Raid)
|
||||||
extends EntityNode(raid)
|
extends EntityNode(raid)
|
||||||
with SyncedInventory
|
with SyncedInventory
|
||||||
with LabeledEntityNode
|
with LabeledEntityNode
|
||||||
with DiskActivityHandler
|
with DiskActivityHandler
|
||||||
with DefaultSlotItemsFillable
|
with DefaultSlotItemsFillable
|
||||||
with WindowedNode[RaidWindow] {
|
with WindowedNode[RaidWindow]
|
||||||
|
with EditableDisk {
|
||||||
|
|
||||||
var diskSlots: Array[HddSlotWidget] = Array.tabulate(3)(index => new HddSlotWidget(Slot(index), Tier.Three))
|
var diskSlots: Array[HddSlotWidget] = Array.tabulate(3)(index => new HddSlotWidget(Slot(index), Tier.Three))
|
||||||
|
|
||||||
override val iconSource: IconSource = IconSource.Nodes.Raid.Default
|
override val icon: IconSource = IconSource.Nodes.Raid.Default
|
||||||
|
|
||||||
|
override def rotatable: Boolean = true
|
||||||
|
|
||||||
override def draw(g: Graphics): Unit = {
|
override def draw(g: Graphics): Unit = {
|
||||||
super.draw(g)
|
super.draw(g)
|
||||||
@ -55,31 +63,52 @@ class RaidNode(val raid: Raid)
|
|||||||
// Required for disk activity events processing
|
// Required for disk activity events processing
|
||||||
override def shouldReceiveEventsFor(address: String): Boolean =
|
override def shouldReceiveEventsFor(address: String): Boolean =
|
||||||
super.shouldReceiveEventsFor(address) || raid.filesystem.exists(_.node.address == address)
|
super.shouldReceiveEventsFor(address) || raid.filesystem.exists(_.node.address == address)
|
||||||
|
|
||||||
|
private val diskEditWindow = new Windowed[DiskEditWindow] {
|
||||||
|
override protected def createWindow(): DiskEditWindow = new DiskEditWindow(RaidNode.this)
|
||||||
|
|
||||||
|
override protected def windowNBTKey: String = "diskEditWindow"
|
||||||
|
}
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
DiskItem.addRealPathContextMenuEntries(menu, raid,
|
DiskItem.addRealPathContextMenuEntries(
|
||||||
|
menu,
|
||||||
|
raid,
|
||||||
realPathSetter => {
|
realPathSetter => {
|
||||||
realPathSetter()
|
realPathSetter()
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// TODO: Implement DiskDriveWindow later, because at this moment every
|
menu.addEntry(ContextMenuEntry("Edit disk", IconSource.Icons.Edit) {
|
||||||
// TODO: instance of 'Windowed' trait can have only 1 persistable window
|
diskEditWindow.window.open()
|
||||||
|
})
|
||||||
// TODO: Perhaps we should rework this system with List[Window] and
|
|
||||||
// TODO: registerWindow(...) or something similar
|
|
||||||
|
|
||||||
// menu.addEntry(ContextMenuEntry("Edit disk", IconSource.Edit) {
|
|
||||||
// window.open()
|
|
||||||
// })
|
|
||||||
|
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
|
|
||||||
super.setupContextMenu(menu, event)
|
super.setupContextMenu(menu, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def load(nbt: NBTTagCompound): Unit = {
|
||||||
|
super.load(nbt)
|
||||||
|
|
||||||
|
diskEditWindow.load(nbt)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def save(nbt: NBTTagCompound): Unit = {
|
||||||
|
super.save(nbt)
|
||||||
|
|
||||||
|
diskEditWindow.save(nbt)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def dispose(): Unit = {
|
||||||
|
diskEditWindow.closeAndDisposeWindow()
|
||||||
|
|
||||||
|
super.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------- LabeledEntityNode --------------------------------
|
// -------------------------------- LabeledEntityNode --------------------------------
|
||||||
|
|
||||||
override def fallbackLabelAddress: Option[String] = raid.filesystem.map(_.node.address)
|
override def fallbackLabelAddress: Option[String] = diskAddress
|
||||||
|
|
||||||
// -------------------------------- Inventory --------------------------------
|
// -------------------------------- Inventory --------------------------------
|
||||||
|
|
||||||
@ -97,4 +126,22 @@ class RaidNode(val raid: Raid)
|
|||||||
// -------------------------------- Window --------------------------------
|
// -------------------------------- Window --------------------------------
|
||||||
|
|
||||||
override def createWindow(): RaidWindow = new RaidWindow(this)
|
override def createWindow(): RaidWindow = new RaidWindow(this)
|
||||||
|
|
||||||
|
// -------------------------------- EditableDisk --------------------------------
|
||||||
|
|
||||||
|
override def disk: Option[Environment] = raid.filesystem
|
||||||
|
|
||||||
|
override def diskKind: String = "RAID"
|
||||||
|
|
||||||
|
override def diskAddress: Option[String] = raid.filesystem.map(_.node.address)
|
||||||
|
|
||||||
|
override def capacity: Long = raid.filesystem.fold(0L)(_.fileSystem.spaceTotal)
|
||||||
|
|
||||||
|
override def diskLabel: Option[String] = Option(raid.label.getLabel)
|
||||||
|
|
||||||
|
override def diskLabel_=(label: Option[String]): Unit = raid.label.setLabel(label.orNull)
|
||||||
|
|
||||||
|
override def isLabelWriteable: Boolean = true
|
||||||
|
|
||||||
|
override def editingAllowed: Boolean = raid.filesystem.isDefined
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import totoro.ocelot.brain.util.Direction
|
|||||||
class RelayNode(val relay: Relay)
|
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
|
override val icon: IconSource = IconSource.Nodes.Relay
|
||||||
|
|
||||||
override def ports: Array[NodePort] = Array(
|
override def ports: Array[NodePort] = Array(
|
||||||
NodePort(Some(Direction.North)),
|
NodePort(Some(Direction.North)),
|
||||||
@ -24,6 +24,8 @@ class RelayNode(val relay: Relay)
|
|||||||
|
|
||||||
override def getNodeByPort(port: NodePort): network.Node = relay.sidedNode(port.direction.get)
|
override def getNodeByPort(port: NodePort): network.Node = relay.sidedNode(port.direction.get)
|
||||||
|
|
||||||
|
override def rotatable: Boolean = false
|
||||||
|
|
||||||
override def exposeAddress = false
|
override def exposeAddress = false
|
||||||
|
|
||||||
override def shouldReceiveEventsFor(address: String): Boolean = {
|
override def shouldReceiveEventsFor(address: String): Boolean = {
|
||||||
|
|||||||
@ -91,32 +91,34 @@ class ScreenNode(val screen: Screen)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def iconSource: IconSource = IconSource.Nodes.Screen.Standalone
|
override def icon: IconSource = IconSource.Nodes.Screen.Standalone
|
||||||
|
|
||||||
override def iconColor: Color = TierColor.get(screen.tier)
|
override def iconColor: Color = TierColor.get(screen.tier)
|
||||||
|
|
||||||
|
override def rotatable: Boolean = true
|
||||||
|
|
||||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||||
// no synchronization here: the methods to turn the screen on/off are indirect.
|
// no synchronization here: the methods to turn the screen on/off are indirect.
|
||||||
if (screen.getPowerState) {
|
if (screen.getPowerState) {
|
||||||
menu.addEntry(ContextMenuEntry("Turn off", IconSource.Power) {
|
menu.addEntry(ContextMenuEntry("Turn off", IconSource.Icons.Power) {
|
||||||
screen.setPowerState(false)
|
screen.setPowerState(false)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
menu.addEntry(ContextMenuEntry("Turn on", IconSource.Power) {
|
menu.addEntry(ContextMenuEntry("Turn on", IconSource.Icons.Power) {
|
||||||
screen.setPowerState(true)
|
screen.setPowerState(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.addEntry(ContextMenuEntry("Set aspect ratio", IconSource.AspectRatio) {
|
menu.addEntry(ContextMenuEntry("Set aspect ratio", IconSource.Icons.AspectRatio) {
|
||||||
new ScreenAspectRatioDialog(this).show()
|
new ScreenAspectRatioDialog(this).show()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (keyboard.isDefined) {
|
if (keyboard.isDefined) {
|
||||||
menu.addEntry(ContextMenuEntry("Remove keyboard", IconSource.KeyboardOff) {
|
menu.addEntry(ContextMenuEntry("Remove keyboard", IconSource.Icons.KeyboardOff) {
|
||||||
detachKeyboard()
|
detachKeyboard()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
menu.addEntry(ContextMenuEntry("Add keyboard", IconSource.Keyboard) {
|
menu.addEntry(ContextMenuEntry("Add keyboard", IconSource.Icons.Keyboard) {
|
||||||
attachKeyboard()
|
attachKeyboard()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||