Added racks, servers & mountable disk drives
@ -1 +1 @@
|
||||
Subproject commit 40b9b506f9590a1a2add9bb1d47508a06f2b64cf
|
||||
Subproject commit 8222b1287c40965622fffcdeff4ae93f5d0418b5
|
||||
|
Before Width: | Height: | Size: 602 B After Width: | Height: | Size: 602 B |
|
Before Width: | Height: | Size: 150 B After Width: | Height: | Size: 150 B |
|
Before Width: | Height: | Size: 634 B After Width: | Height: | Size: 634 B |
|
Before Width: | Height: | Size: 151 B After Width: | Height: | Size: 151 B |
|
Before Width: | Height: | Size: 151 B After Width: | Height: | Size: 151 B |
|
Before Width: | Height: | Size: 150 B After Width: | Height: | Size: 150 B |
|
Before Width: | Height: | Size: 528 B After Width: | Height: | Size: 528 B |
|
Before Width: | Height: | Size: 508 B After Width: | Height: | Size: 508 B |
BIN
sprites/nodes/rack/Default.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/nodes/rack/Empty.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/nodes/rack/drive/0/Activity.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/nodes/rack/drive/0/Default.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
sprites/nodes/rack/drive/0/Floppy.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sprites/nodes/rack/drive/1/Activity.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/nodes/rack/drive/1/Default.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
sprites/nodes/rack/drive/1/Floppy.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sprites/nodes/rack/drive/2/Activity.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/nodes/rack/drive/2/Default.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
sprites/nodes/rack/drive/2/Floppy.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sprites/nodes/rack/drive/3/Activity.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/nodes/rack/drive/3/Default.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
sprites/nodes/rack/drive/3/Floppy.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sprites/nodes/rack/drive/Floppy.png
Normal file
|
After Width: | Height: | Size: 508 B |
BIN
sprites/nodes/rack/server/0/Activity.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
sprites/nodes/rack/server/0/Default.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sprites/nodes/rack/server/0/Error.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
sprites/nodes/rack/server/0/On.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
sprites/nodes/rack/server/1/Activity.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
sprites/nodes/rack/server/1/Default.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sprites/nodes/rack/server/1/Error.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
sprites/nodes/rack/server/1/On.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
sprites/nodes/rack/server/2/Activity.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
sprites/nodes/rack/server/2/Default.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sprites/nodes/rack/server/2/Error.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
sprites/nodes/rack/server/2/On.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
sprites/nodes/rack/server/3/Activity.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
sprites/nodes/rack/server/3/Default.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sprites/nodes/rack/server/3/Error.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
sprites/nodes/rack/server/3/On.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
BIN
sprites/window/rack/Lines.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
sprites/window/rack/Motherboard.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
sprites/window/rack/NetworkBack.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/window/rack/NetworkBottom.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/window/rack/NetworkConnector.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/window/rack/NetworkLeft.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/window/rack/NetworkRight.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/window/rack/NetworkTop.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/window/rack/NodeBack.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/window/rack/NodeBottom.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/window/rack/NodeLeft.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/window/rack/NodeRight.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/window/rack/NodeTop.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/window/rack/SideBack.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/window/rack/SideBottom.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
sprites/window/rack/SideConnector.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/window/rack/SideLeft.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/window/rack/SideRight.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
sprites/window/rack/SideTop.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
@ -2,8 +2,8 @@ PortDown = #8382d8
|
||||
PortUp = #75bdc1
|
||||
PortNorth = #c8ca5f
|
||||
PortSouth = #990da3
|
||||
PortEast = #db7d75
|
||||
PortWest = #7ec95f
|
||||
PortEast = #7ec95f
|
||||
PortWest = #db7d75
|
||||
PortAny = #9b9b9b
|
||||
|
||||
Tier0 = #ababab
|
||||
|
||||
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 100 KiB |
@ -1,14 +1,13 @@
|
||||
BackgroundPattern 0 0 304 304
|
||||
BarSegment 53 472 16 4
|
||||
ComputerMotherboard 305 129 79 70
|
||||
BarSegment 69 479 16 4
|
||||
Empty 426 305 16 16
|
||||
EmptySlot 404 330 18 18
|
||||
Knob 385 129 50 50
|
||||
KnobCenter 436 129 50 50
|
||||
KnobLimits 305 200 50 50
|
||||
Knob 459 208 50 50
|
||||
KnobCenter 406 129 50 50
|
||||
KnobLimits 457 129 50 50
|
||||
ShadowBorder 505 0 1 24
|
||||
ShadowCorner 301 305 24 24
|
||||
TabArrow 0 472 8 14
|
||||
TabArrow 16 479 8 14
|
||||
blocks/Generic 443 305 16 16
|
||||
blocks/HologramEffect 507 18 4 4
|
||||
blocks/HologramProjector1Top 460 305 16 16
|
||||
@ -17,174 +16,205 @@ blocks/HologramProjectorSide 494 305 16 16
|
||||
buttons/BottomDrawerClose 423 330 18 18
|
||||
buttons/BottomDrawerOpen 442 330 18 18
|
||||
buttons/OpenFMRadioCloseOff 502 117 7 8
|
||||
buttons/OpenFMRadioCloseOn 0 487 7 8
|
||||
buttons/OpenFMRadioCloseOn 131 494 7 8
|
||||
buttons/OpenFMRadioRedstoneOff 502 72 8 8
|
||||
buttons/OpenFMRadioRedstoneOn 502 81 8 8
|
||||
buttons/OpenFMRadioStartOff 326 305 24 24
|
||||
buttons/OpenFMRadioStartOn 351 305 24 24
|
||||
buttons/OpenFMRadioStopOff 376 305 24 24
|
||||
buttons/OpenFMRadioStopOn 401 305 24 24
|
||||
buttons/OpenFMRadioVolumeDownOff 9 472 10 10
|
||||
buttons/OpenFMRadioVolumeDownOn 20 472 10 10
|
||||
buttons/OpenFMRadioVolumeUpOff 31 472 10 10
|
||||
buttons/OpenFMRadioVolumeUpOn 42 472 10 10
|
||||
buttons/OpenFMRadioVolumeDownOff 25 479 10 10
|
||||
buttons/OpenFMRadioVolumeDownOn 36 479 10 10
|
||||
buttons/OpenFMRadioVolumeUpOff 47 479 10 10
|
||||
buttons/OpenFMRadioVolumeUpOn 58 479 10 10
|
||||
buttons/PowerOff 461 330 18 18
|
||||
buttons/PowerOn 480 330 18 18
|
||||
icons/ButtonCheck 301 378 17 17
|
||||
icons/ButtonClipboard 319 378 17 17
|
||||
icons/ButtonRandomize 337 378 17 17
|
||||
icons/CPU 356 200 16 16
|
||||
icons/Card 373 200 16 16
|
||||
icons/Close 0 439 15 14
|
||||
icons/ComponentBus 390 200 16 16
|
||||
icons/CPU 406 180 16 16
|
||||
icons/Card 423 180 16 16
|
||||
icons/Close 0 479 15 14
|
||||
icons/ComponentBus 440 180 16 16
|
||||
icons/DragLMB 301 363 21 14
|
||||
icons/DragRMB 323 363 21 14
|
||||
icons/EEPROM 407 200 16 16
|
||||
icons/Floppy 424 200 16 16
|
||||
icons/EEPROM 457 180 16 16
|
||||
icons/Floppy 474 180 16 16
|
||||
icons/Grid 335 330 22 22
|
||||
icons/GridOff 358 330 22 22
|
||||
icons/HDD 441 200 16 16
|
||||
icons/HDD 491 180 16 16
|
||||
icons/Home 381 330 22 22
|
||||
icons/LMB 71 454 11 14
|
||||
icons/Memory 458 200 16 16
|
||||
icons/NA 475 200 16 16
|
||||
icons/NotificationError 95 454 11 11
|
||||
icons/NotificationInfo 107 454 11 11
|
||||
icons/NotificationWarning 119 454 11 11
|
||||
icons/Pin 26 454 14 14
|
||||
icons/RMB 83 454 11 14
|
||||
icons/RackMountable 492 200 16 16
|
||||
icons/SettingsSound 0 454 12 17
|
||||
icons/SettingsUI 13 454 12 17
|
||||
icons/Tier0 356 217 16 16
|
||||
icons/Tier1 373 217 16 16
|
||||
icons/Tier2 390 217 16 16
|
||||
icons/Unpin 41 454 14 14
|
||||
icons/WaveLFSR 393 411 24 10
|
||||
icons/WaveNoise 418 411 24 10
|
||||
icons/WaveSawtooth 443 411 24 10
|
||||
icons/WaveSine 468 411 24 10
|
||||
icons/WaveSquare 0 428 24 10
|
||||
icons/WaveTriangle 25 428 24 10
|
||||
icons/LMB 71 494 11 14
|
||||
icons/Memory 459 259 16 16
|
||||
icons/NA 476 259 16 16
|
||||
icons/NotificationError 95 494 11 11
|
||||
icons/NotificationInfo 107 494 11 11
|
||||
icons/NotificationWarning 119 494 11 11
|
||||
icons/Pin 26 494 14 14
|
||||
icons/RMB 83 494 11 14
|
||||
icons/Server 493 259 16 16
|
||||
icons/SettingsSound 0 494 12 17
|
||||
icons/SettingsUI 13 494 12 17
|
||||
icons/Tier0 379 279 16 16
|
||||
icons/Tier1 396 279 16 16
|
||||
icons/Tier2 413 279 16 16
|
||||
icons/Unpin 41 494 14 14
|
||||
icons/WaveLFSR 359 462 24 10
|
||||
icons/WaveNoise 384 462 24 10
|
||||
icons/WaveSawtooth 409 462 24 10
|
||||
icons/WaveSine 434 462 24 10
|
||||
icons/WaveSquare 459 462 24 10
|
||||
icons/WaveTriangle 484 462 24 10
|
||||
icons/WireArrowLeft 507 0 4 8
|
||||
icons/WireArrowRight 507 9 4 8
|
||||
items/APU0 233 305 16 96
|
||||
items/APU1 250 305 16 96
|
||||
items/APU2 267 305 16 96
|
||||
items/CPU0 407 217 16 16
|
||||
items/CPU1 424 217 16 16
|
||||
items/CPU2 441 217 16 16
|
||||
items/CardBase 458 217 16 16
|
||||
items/CircuitBoard 475 217 16 16
|
||||
items/ComponentBus0 492 217 16 16
|
||||
items/ComponentBus1 356 234 16 16
|
||||
items/ComponentBus2 373 234 16 16
|
||||
items/ComponentBus3 390 234 16 16
|
||||
items/CPU0 430 279 16 16
|
||||
items/CPU1 447 279 16 16
|
||||
items/CPU2 464 279 16 16
|
||||
items/CardBase 481 279 16 16
|
||||
items/CircuitBoard 0 411 16 16
|
||||
items/ComponentBus0 17 411 16 16
|
||||
items/ComponentBus1 34 411 16 16
|
||||
items/ComponentBus2 51 411 16 16
|
||||
items/ComponentBus3 68 411 16 16
|
||||
items/DataCard0 434 0 16 128
|
||||
items/DataCard1 451 0 16 128
|
||||
items/DataCard2 468 0 16 128
|
||||
items/DebugCard 407 234 16 16
|
||||
items/DiskDriveMountable 424 234 16 16
|
||||
items/EEPROM 441 234 16 16
|
||||
items/FloppyDisk_dyeBlack 458 234 16 16
|
||||
items/FloppyDisk_dyeBlue 475 234 16 16
|
||||
items/FloppyDisk_dyeBrown 492 234 16 16
|
||||
items/FloppyDisk_dyeCyan 305 251 16 16
|
||||
items/FloppyDisk_dyeGray 322 251 16 16
|
||||
items/FloppyDisk_dyeGreen 339 251 16 16
|
||||
items/FloppyDisk_dyeLightBlue 356 251 16 16
|
||||
items/FloppyDisk_dyeLightGray 373 251 16 16
|
||||
items/FloppyDisk_dyeLime 390 251 16 16
|
||||
items/FloppyDisk_dyeMagenta 407 251 16 16
|
||||
items/FloppyDisk_dyeOrange 424 251 16 16
|
||||
items/FloppyDisk_dyePink 441 251 16 16
|
||||
items/FloppyDisk_dyePurple 458 251 16 16
|
||||
items/FloppyDisk_dyeRed 475 251 16 16
|
||||
items/FloppyDisk_dyeWhite 492 251 16 16
|
||||
items/FloppyDisk_dyeYellow 305 268 16 16
|
||||
items/GraphicsCard0 322 268 16 16
|
||||
items/GraphicsCard1 339 268 16 16
|
||||
items/GraphicsCard2 356 268 16 16
|
||||
items/HardDiskDrive0 373 268 16 16
|
||||
items/HardDiskDrive1 390 268 16 16
|
||||
items/HardDiskDrive2 407 268 16 16
|
||||
items/DebugCard 85 411 16 16
|
||||
items/DiskDriveMountable 102 411 16 16
|
||||
items/EEPROM 119 411 16 16
|
||||
items/FloppyDisk_dyeBlack 136 411 16 16
|
||||
items/FloppyDisk_dyeBlue 153 411 16 16
|
||||
items/FloppyDisk_dyeBrown 170 411 16 16
|
||||
items/FloppyDisk_dyeCyan 187 411 16 16
|
||||
items/FloppyDisk_dyeGray 204 411 16 16
|
||||
items/FloppyDisk_dyeGreen 221 411 16 16
|
||||
items/FloppyDisk_dyeLightBlue 238 411 16 16
|
||||
items/FloppyDisk_dyeLightGray 255 411 16 16
|
||||
items/FloppyDisk_dyeLime 272 411 16 16
|
||||
items/FloppyDisk_dyeMagenta 289 411 16 16
|
||||
items/FloppyDisk_dyeOrange 306 411 16 16
|
||||
items/FloppyDisk_dyePink 323 411 16 16
|
||||
items/FloppyDisk_dyePurple 340 411 16 16
|
||||
items/FloppyDisk_dyeRed 357 411 16 16
|
||||
items/FloppyDisk_dyeWhite 374 411 16 16
|
||||
items/FloppyDisk_dyeYellow 391 411 16 16
|
||||
items/GraphicsCard0 408 411 16 16
|
||||
items/GraphicsCard1 425 411 16 16
|
||||
items/GraphicsCard2 442 411 16 16
|
||||
items/HardDiskDrive0 459 411 16 16
|
||||
items/HardDiskDrive1 476 411 16 16
|
||||
items/HardDiskDrive2 493 411 16 16
|
||||
items/InternetCard 301 330 16 32
|
||||
items/LinkedCard 284 305 16 96
|
||||
items/Memory0 424 268 16 16
|
||||
items/Memory1 441 268 16 16
|
||||
items/Memory2 458 268 16 16
|
||||
items/Memory3 475 268 16 16
|
||||
items/Memory4 492 268 16 16
|
||||
items/Memory5 305 285 16 16
|
||||
items/NetworkCard 322 285 16 16
|
||||
items/RedstoneCard0 339 285 16 16
|
||||
items/RedstoneCard1 356 285 16 16
|
||||
items/Memory0 0 428 16 16
|
||||
items/Memory1 17 428 16 16
|
||||
items/Memory2 34 428 16 16
|
||||
items/Memory3 51 428 16 16
|
||||
items/Memory4 68 428 16 16
|
||||
items/Memory5 85 428 16 16
|
||||
items/NetworkCard 102 428 16 16
|
||||
items/RedstoneCard0 119 428 16 16
|
||||
items/RedstoneCard1 136 428 16 16
|
||||
items/SelfDestructingCard 318 330 16 32
|
||||
items/Server0 373 285 16 16
|
||||
items/Server1 390 285 16 16
|
||||
items/Server2 407 285 16 16
|
||||
items/Server3 424 285 16 16
|
||||
items/Server0 153 428 16 16
|
||||
items/Server1 170 428 16 16
|
||||
items/Server2 187 428 16 16
|
||||
items/Server3 204 428 16 16
|
||||
items/SoundCard 485 0 16 128
|
||||
items/WirelessNetworkCard0 441 285 16 16
|
||||
items/WirelessNetworkCard1 458 285 16 16
|
||||
light-panel/BookmarkLeft 374 411 18 14
|
||||
items/WirelessNetworkCard0 221 428 16 16
|
||||
items/WirelessNetworkCard1 238 428 16 16
|
||||
light-panel/BookmarkLeft 340 462 18 14
|
||||
light-panel/BookmarkRight 355 378 20 14
|
||||
light-panel/BorderB 8 487 4 4
|
||||
light-panel/BorderL 98 487 4 2
|
||||
light-panel/BorderR 13 487 4 4
|
||||
light-panel/BorderT 18 487 4 4
|
||||
light-panel/CornerBL 23 487 4 4
|
||||
light-panel/CornerBR 28 487 4 4
|
||||
light-panel/CornerTL 33 487 4 4
|
||||
light-panel/CornerTR 38 487 4 4
|
||||
light-panel/Fill 4 496 2 2
|
||||
light-panel/BorderB 139 494 4 4
|
||||
light-panel/BorderL 229 494 4 2
|
||||
light-panel/BorderR 144 494 4 4
|
||||
light-panel/BorderT 149 494 4 4
|
||||
light-panel/CornerBL 154 494 4 4
|
||||
light-panel/CornerBR 159 494 4 4
|
||||
light-panel/CornerTL 164 494 4 4
|
||||
light-panel/CornerTR 169 494 4 4
|
||||
light-panel/Fill 88 479 2 2
|
||||
light-panel/Vent 502 0 2 38
|
||||
nodes/Cable 502 90 8 8
|
||||
nodes/Camera 475 285 16 16
|
||||
nodes/Chest 56 454 14 14
|
||||
nodes/Computer 492 285 16 16
|
||||
nodes/ComputerActivityOverlay 487 129 16 16
|
||||
nodes/ComputerErrorOverlay 487 146 16 16
|
||||
nodes/ComputerOnOverlay 487 163 16 16
|
||||
nodes/DiskDrive 385 180 16 16
|
||||
nodes/DiskDriveActivity 402 180 16 16
|
||||
nodes/DiskDriveFloppy 419 180 16 16
|
||||
nodes/HologramProjector0 436 180 16 16
|
||||
nodes/HologramProjector1 453 180 16 16
|
||||
nodes/IronNoteBlock 470 180 16 16
|
||||
nodes/Lamp 487 180 16 16
|
||||
nodes/LampFrame 0 411 16 16
|
||||
nodes/Camera 255 428 16 16
|
||||
nodes/Chest 56 494 14 14
|
||||
nodes/HologramProjector0 272 428 16 16
|
||||
nodes/HologramProjector1 289 428 16 16
|
||||
nodes/IronNoteBlock 306 428 16 16
|
||||
nodes/Lamp 323 428 16 16
|
||||
nodes/LampFrame 340 428 16 16
|
||||
nodes/LampGlow 305 0 128 128
|
||||
nodes/NewNode 17 411 16 16
|
||||
nodes/NoteBlock 34 411 16 16
|
||||
nodes/OpenFMRadio 51 411 16 16
|
||||
nodes/Relay 68 411 16 16
|
||||
nodes/screen/BottomLeft 85 411 16 16
|
||||
nodes/screen/BottomMiddle 102 411 16 16
|
||||
nodes/screen/BottomRight 119 411 16 16
|
||||
nodes/screen/ColumnBottom 136 411 16 16
|
||||
nodes/screen/ColumnMiddle 153 411 16 16
|
||||
nodes/screen/ColumnTop 170 411 16 16
|
||||
nodes/screen/Middle 187 411 16 16
|
||||
nodes/screen/MiddleLeft 204 411 16 16
|
||||
nodes/screen/MiddleRight 221 411 16 16
|
||||
nodes/screen/PowerOnOverlay 238 411 16 16
|
||||
nodes/screen/RowLeft 255 411 16 16
|
||||
nodes/screen/RowMiddle 272 411 16 16
|
||||
nodes/screen/RowRight 289 411 16 16
|
||||
nodes/screen/Standalone 306 411 16 16
|
||||
nodes/screen/TopLeft 323 411 16 16
|
||||
nodes/screen/TopMiddle 340 411 16 16
|
||||
nodes/screen/TopRight 357 411 16 16
|
||||
panel/BorderB 43 487 4 4
|
||||
panel/BorderL 103 487 4 2
|
||||
panel/BorderR 48 487 4 4
|
||||
panel/BorderT 53 487 4 4
|
||||
panel/CornerBL 58 487 4 4
|
||||
panel/CornerBR 63 487 4 4
|
||||
panel/CornerTL 68 487 4 4
|
||||
panel/CornerTR 73 487 4 4
|
||||
panel/Fill 7 496 2 2
|
||||
nodes/NewNode 357 428 16 16
|
||||
nodes/NoteBlock 374 428 16 16
|
||||
nodes/OpenFMRadio 391 428 16 16
|
||||
nodes/Relay 408 428 16 16
|
||||
nodes/computer/Activity 425 428 16 16
|
||||
nodes/computer/Default 442 428 16 16
|
||||
nodes/computer/Error 459 428 16 16
|
||||
nodes/computer/On 476 428 16 16
|
||||
nodes/disk-drive/Activity 493 428 16 16
|
||||
nodes/disk-drive/Default 0 445 16 16
|
||||
nodes/disk-drive/Floppy 17 445 16 16
|
||||
nodes/rack/Default 34 445 16 16
|
||||
nodes/rack/Empty 51 445 16 16
|
||||
nodes/rack/drive/0/Activity 68 445 16 16
|
||||
nodes/rack/drive/0/Default 85 445 16 16
|
||||
nodes/rack/drive/0/Floppy 102 445 16 16
|
||||
nodes/rack/drive/1/Activity 119 445 16 16
|
||||
nodes/rack/drive/1/Default 136 445 16 16
|
||||
nodes/rack/drive/1/Floppy 153 445 16 16
|
||||
nodes/rack/drive/2/Activity 170 445 16 16
|
||||
nodes/rack/drive/2/Default 187 445 16 16
|
||||
nodes/rack/drive/2/Floppy 204 445 16 16
|
||||
nodes/rack/drive/3/Activity 221 445 16 16
|
||||
nodes/rack/drive/3/Default 238 445 16 16
|
||||
nodes/rack/drive/3/Floppy 255 445 16 16
|
||||
nodes/rack/drive/Floppy 272 445 16 16
|
||||
nodes/rack/server/0/Activity 289 445 16 16
|
||||
nodes/rack/server/0/Default 306 445 16 16
|
||||
nodes/rack/server/0/Error 323 445 16 16
|
||||
nodes/rack/server/0/On 340 445 16 16
|
||||
nodes/rack/server/1/Activity 357 445 16 16
|
||||
nodes/rack/server/1/Default 374 445 16 16
|
||||
nodes/rack/server/1/Error 391 445 16 16
|
||||
nodes/rack/server/1/On 408 445 16 16
|
||||
nodes/rack/server/2/Activity 425 445 16 16
|
||||
nodes/rack/server/2/Default 442 445 16 16
|
||||
nodes/rack/server/2/Error 459 445 16 16
|
||||
nodes/rack/server/2/On 476 445 16 16
|
||||
nodes/rack/server/3/Activity 493 445 16 16
|
||||
nodes/rack/server/3/Default 0 462 16 16
|
||||
nodes/rack/server/3/Error 17 462 16 16
|
||||
nodes/rack/server/3/On 34 462 16 16
|
||||
nodes/screen/BottomLeft 51 462 16 16
|
||||
nodes/screen/BottomMiddle 68 462 16 16
|
||||
nodes/screen/BottomRight 85 462 16 16
|
||||
nodes/screen/ColumnBottom 102 462 16 16
|
||||
nodes/screen/ColumnMiddle 119 462 16 16
|
||||
nodes/screen/ColumnTop 136 462 16 16
|
||||
nodes/screen/Middle 153 462 16 16
|
||||
nodes/screen/MiddleLeft 170 462 16 16
|
||||
nodes/screen/MiddleRight 187 462 16 16
|
||||
nodes/screen/PowerOnOverlay 204 462 16 16
|
||||
nodes/screen/RowLeft 221 462 16 16
|
||||
nodes/screen/RowMiddle 238 462 16 16
|
||||
nodes/screen/RowRight 255 462 16 16
|
||||
nodes/screen/Standalone 272 462 16 16
|
||||
nodes/screen/TopLeft 289 462 16 16
|
||||
nodes/screen/TopMiddle 306 462 16 16
|
||||
nodes/screen/TopRight 323 462 16 16
|
||||
panel/BorderB 174 494 4 4
|
||||
panel/BorderL 234 494 4 2
|
||||
panel/BorderR 179 494 4 4
|
||||
panel/BorderT 184 494 4 4
|
||||
panel/CornerBL 189 494 4 4
|
||||
panel/CornerBR 194 494 4 4
|
||||
panel/CornerTL 199 494 4 4
|
||||
panel/CornerTR 204 494 4 4
|
||||
panel/Fill 91 479 2 2
|
||||
particles/Note 502 61 7 10
|
||||
screen/BorderB 508 25 2 8
|
||||
screen/BorderT 505 25 2 10
|
||||
@ -192,10 +222,30 @@ screen/CornerBL 502 99 8 8
|
||||
screen/CornerBR 502 108 8 8
|
||||
screen/CornerTL 502 39 8 10
|
||||
screen/CornerTR 502 50 8 10
|
||||
window/BorderDark 0 496 1 4
|
||||
window/BorderLight 2 496 1 4
|
||||
window/CornerBL 78 487 4 4
|
||||
window/CornerBR 83 487 4 4
|
||||
window/CornerTL 88 487 4 4
|
||||
window/CornerTR 93 487 4 4
|
||||
window/BorderDark 510 117 1 4
|
||||
window/BorderLight 86 479 1 4
|
||||
window/CornerBL 209 494 4 4
|
||||
window/CornerBR 214 494 4 4
|
||||
window/CornerTL 219 494 4 4
|
||||
window/CornerTR 224 494 4 4
|
||||
window/OpenFMRadio 0 305 232 105
|
||||
window/case/Motherboard 379 208 79 70
|
||||
window/rack/Lines 305 208 73 91
|
||||
window/rack/Motherboard 305 129 100 78
|
||||
window/rack/NetworkBack 149 499 1 2
|
||||
window/rack/NetworkBottom 151 499 1 2
|
||||
window/rack/NetworkConnector 153 499 1 2
|
||||
window/rack/NetworkLeft 155 499 1 2
|
||||
window/rack/NetworkRight 157 499 1 2
|
||||
window/rack/NetworkTop 159 499 1 2
|
||||
window/rack/NodeBack 239 494 5 1
|
||||
window/rack/NodeBottom 245 494 5 1
|
||||
window/rack/NodeLeft 251 494 5 1
|
||||
window/rack/NodeRight 257 494 5 1
|
||||
window/rack/NodeTop 263 494 5 1
|
||||
window/rack/SideBack 510 122 1 3
|
||||
window/rack/SideBottom 139 499 1 3
|
||||
window/rack/SideConnector 141 499 1 3
|
||||
window/rack/SideLeft 143 499 1 3
|
||||
window/rack/SideRight 145 499 1 3
|
||||
window/rack/SideTop 147 499 1 3
|
||||
|
||||
@ -56,6 +56,16 @@ class SoundSource(val kind: SoundSource.Kind,
|
||||
}
|
||||
|
||||
object SoundSource {
|
||||
lazy val InterfaceClick: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceClick, SoundCategory.Interface)
|
||||
lazy val InterfaceClickLow: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceClick, SoundCategory.Interface, pitch = 0.8f)
|
||||
lazy val InterfaceTick: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceTick, SoundCategory.Interface)
|
||||
|
||||
lazy val MinecraftClick: SoundSource = SoundSource.fromBuffer(SoundBuffers.MinecraftClick, SoundCategory.Interface)
|
||||
lazy val MinecraftExplosion: SoundSource = SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)
|
||||
|
||||
lazy val MachineFloppyInsert: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
|
||||
lazy val MachineFloppyEject: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, SoundCategory.Environment)
|
||||
|
||||
sealed trait Kind
|
||||
|
||||
case class KindSoundBuffer(buffer: SoundBuffer) extends Kind
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
package ocelot.desktop.audio
|
||||
|
||||
//noinspection TypeAnnotation
|
||||
object SoundSources {
|
||||
val InterfaceClick = SoundSource.fromBuffer(SoundBuffers.InterfaceClick, SoundCategory.Interface)
|
||||
val InterfaceClickLow = SoundSource.fromBuffer(SoundBuffers.InterfaceClick, SoundCategory.Interface, pitch = 0.8f)
|
||||
val InterfaceTick = SoundSource.fromBuffer(SoundBuffers.InterfaceTick, SoundCategory.Interface)
|
||||
|
||||
val MinecraftClick = SoundSource.fromBuffer(SoundBuffers.MinecraftClick, SoundCategory.Interface)
|
||||
val MinecraftExplosion = SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)
|
||||
}
|
||||
@ -4,7 +4,7 @@ import ocelot.desktop.util.Persistable
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
|
||||
object Vector2D {
|
||||
val Zero = Vector2D(0, 0)
|
||||
val Zero: Vector2D = Vector2D(0, 0)
|
||||
|
||||
def apply(x: Double, y: Double): Vector2D = Vector2D(x.toFloat, y.toFloat)
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ object Icons {
|
||||
val EepromIcon: IconDef = IconDef("icons/EEPROM")
|
||||
val FloppyIcon: IconDef = IconDef("icons/Floppy")
|
||||
val MemoryIcon: IconDef = IconDef("icons/Memory")
|
||||
val ServerIcon: IconDef = IconDef("icons/Server")
|
||||
val ComponentBusIcon: IconDef = IconDef("icons/ComponentBus")
|
||||
|
||||
val TierIcon: Tier => IconDef = { tier =>
|
||||
IconDef(s"icons/Tier${tier.id}")
|
||||
@ -75,6 +77,16 @@ object Icons {
|
||||
IconDef(s"items/Memory${tier.id}")
|
||||
}
|
||||
|
||||
val Server: Tier => IconDef = { tier =>
|
||||
IconDef(s"items/Server${tier.id}")
|
||||
}
|
||||
|
||||
val ComponentBus: Tier => IconDef = { tier =>
|
||||
IconDef(s"items/ComponentBus${tier.id}")
|
||||
}
|
||||
|
||||
val DiskDriveMountable: IconDef = IconDef("items/DiskDriveMountable")
|
||||
|
||||
//noinspection ScalaWeakerAccess
|
||||
object Animations {
|
||||
val Apu: Animation = Array(
|
||||
|
||||
@ -3,15 +3,18 @@ package ocelot.desktop.inventory
|
||||
import ocelot.desktop.ColorScheme
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.graphics.IconDef
|
||||
import ocelot.desktop.ui.event.EventAware
|
||||
import ocelot.desktop.ui.widget.Updatable
|
||||
import ocelot.desktop.ui.widget.contextmenu.ContextMenu
|
||||
import ocelot.desktop.ui.widget.tooltip.ItemTooltip
|
||||
import ocelot.desktop.util.Disposable
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
/**
|
||||
* Something that can be stored in an [[Inventory]].
|
||||
*/
|
||||
trait Item {
|
||||
trait Item extends EventAware with Updatable with Disposable {
|
||||
private var _slot: Option[Inventory#Slot] = None
|
||||
|
||||
/**
|
||||
@ -24,7 +27,8 @@ trait Item {
|
||||
|
||||
if (slot.nonEmpty) {
|
||||
onInserted()
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
onRemoved()
|
||||
}
|
||||
}
|
||||
@ -102,6 +106,12 @@ trait Item {
|
||||
* Keep this rather cheap.
|
||||
*/
|
||||
def factory: ItemFactory
|
||||
|
||||
/**
|
||||
* Override this in subclasses to implement some specific item behavior
|
||||
* during UI update
|
||||
*/
|
||||
def update(): Unit = {}
|
||||
}
|
||||
|
||||
object Item {
|
||||
|
||||
@ -139,4 +139,8 @@ object Items extends Logging {
|
||||
}
|
||||
registerSingleton(SoundCardItem.Factory)
|
||||
registerSingleton(SelfDestructingCardItem.Factory)
|
||||
|
||||
registerTiered("Server", Tier.One to Tier.Creative)(new ServerItem.Factory(_))
|
||||
registerTiered("Component bus", Tier.One to Tier.Creative)(new ComponentBusItem.Factory(_))
|
||||
registerSingleton(DiskDriveMountableItem.Factory)
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@ package ocelot.desktop.inventory
|
||||
import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.inventory.PersistedInventory._
|
||||
import ocelot.desktop.inventory.traits.{ComponentItem, PersistableItem}
|
||||
import ocelot.desktop.util.{Logging, Persistable}
|
||||
import ocelot.desktop.util.ReflectionUtils.findUnaryConstructor
|
||||
import ocelot.desktop.util.{Logging, Persistable}
|
||||
import totoro.ocelot.brain.nbt.ExtendedNBT.{extendNBTTagCompound, extendNBTTagList}
|
||||
import totoro.ocelot.brain.nbt.persistence.NBTPersistence
|
||||
import totoro.ocelot.brain.nbt.{NBT, NBTTagCompound}
|
||||
|
||||
@ -4,7 +4,7 @@ import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.inventory.PersistedInventory.ItemLoadException
|
||||
import ocelot.desktop.inventory.SyncedInventory.SlotStatus.SlotStatus
|
||||
import ocelot.desktop.inventory.SyncedInventory.SyncDirection.SyncDirection
|
||||
import ocelot.desktop.inventory.SyncedInventory._
|
||||
import ocelot.desktop.inventory.SyncedInventory.{SyncDirection, _}
|
||||
import ocelot.desktop.inventory.traits.ComponentItem
|
||||
import ocelot.desktop.ui.event.BrainEvent
|
||||
import ocelot.desktop.ui.widget.EventHandlers
|
||||
@ -54,12 +54,7 @@ trait SyncedInventory extends PersistedInventory with Logging {
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
val occupiedSlots = inventoryIterator.map(_.index).toSet
|
||||
.union(brainInventory.inventory.iterator.map(_.index).toSet)
|
||||
|
||||
for (slotIndex <- occupiedSlots) {
|
||||
sync(slotIndex, SyncDirection.Reconcile)
|
||||
}
|
||||
syncSlots()
|
||||
}
|
||||
|
||||
@throws[ItemLoadException]("if the item could not be loaded")
|
||||
@ -116,15 +111,23 @@ trait SyncedInventory extends PersistedInventory with Logging {
|
||||
}
|
||||
}
|
||||
|
||||
def syncSlots(direction: SyncDirection = SyncDirection.Reconcile): Unit = {
|
||||
val occupiedSlots = inventoryIterator.map(_.index).toSet
|
||||
.union(brainInventory.inventory.iterator.map(_.index).toSet)
|
||||
|
||||
for (slotIndex <- occupiedSlots)
|
||||
sync(slotIndex, direction)
|
||||
}
|
||||
|
||||
private def sync(slotIndex: Int, direction: SyncDirection): Unit = {
|
||||
val initialSync = syncFuel == InitialSyncFuel
|
||||
|
||||
try {
|
||||
doSync(slotIndex, direction)
|
||||
} finally {
|
||||
if (initialSync) {
|
||||
refuel()
|
||||
}
|
||||
finally {
|
||||
if (initialSync)
|
||||
refuel()
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +141,8 @@ trait SyncedInventory extends PersistedInventory with Logging {
|
||||
|
||||
if (syncFuel < 0) {
|
||||
// ignore: the limit has already been reached
|
||||
} else if (syncFuel == 0) {
|
||||
}
|
||||
else if (syncFuel == 0) {
|
||||
logger.error(
|
||||
s"Got trapped in an infinite loop while trying to synchronize the slot $slotIndex " +
|
||||
s"in $this (class ${this.getClass.getName})!",
|
||||
@ -158,7 +162,8 @@ trait SyncedInventory extends PersistedInventory with Logging {
|
||||
logger.error("Breaking the loop forcefully by removing the items.")
|
||||
Slot(slotIndex).remove()
|
||||
brainInventory.inventory(slotIndex).remove()
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
direction match {
|
||||
case _ if checkSlotStatus(slotIndex) == SlotStatus.Synchronized =>
|
||||
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package ocelot.desktop.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.{IconDef, Icons}
|
||||
import ocelot.desktop.inventory.traits.ComponentItem
|
||||
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
||||
import totoro.ocelot.brain.entity.ComponentBus
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class ComponentBusItem(val componentBus: ComponentBus)
|
||||
extends Item
|
||||
with ComponentItem
|
||||
{
|
||||
override def component: Entity with Environment = componentBus
|
||||
override def factory: ItemFactory = new ComponentBusItem.Factory(componentBus.tier)
|
||||
}
|
||||
|
||||
object ComponentBusItem {
|
||||
class Factory(_tier: Tier) extends ItemFactory {
|
||||
override type I = ComponentBusItem
|
||||
|
||||
override def itemClass: Class[I] = classOf
|
||||
|
||||
override def name: String = s"ComponentBus (${_tier.label})"
|
||||
|
||||
override def tier: Option[Tier] = Some(_tier)
|
||||
|
||||
override def icon: IconDef = Icons.ComponentBus(_tier)
|
||||
|
||||
override def build(): ComponentBusItem = new ComponentBusItem(new ComponentBus(_tier))
|
||||
|
||||
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new ComponentBusItem(_)))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package ocelot.desktop.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.{IconDef, Icons}
|
||||
import ocelot.desktop.inventory.{ItemFactory, ItemRecoverer}
|
||||
import ocelot.desktop.util.DiskDriveAware
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, RackMountable}
|
||||
import totoro.ocelot.brain.entity.{DiskDriveMountable, FloppyDiskDrive}
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class DiskDriveMountableItem(val diskDriveMountable: DiskDriveMountable)
|
||||
extends RackMountableItem
|
||||
with DiskDriveAware
|
||||
{
|
||||
override def floppyDiskDrive: FloppyDiskDrive = diskDriveMountable
|
||||
|
||||
override def component: DiskDriveMountable = diskDriveMountable
|
||||
override def factory: ItemFactory = DiskDriveMountableItem.Factory
|
||||
}
|
||||
|
||||
object DiskDriveMountableItem {
|
||||
object Factory extends ItemFactory {
|
||||
override type I = DiskDriveMountableItem
|
||||
|
||||
override def itemClass: Class[I] = classOf
|
||||
|
||||
override def tier: Option[Tier] = None
|
||||
|
||||
override def name: String = "Disk drive"
|
||||
|
||||
override def icon: IconDef = Icons.DiskDriveMountable
|
||||
|
||||
override def build(): DiskDriveMountableItem = new DiskDriveMountableItem(new DiskDriveMountable())
|
||||
|
||||
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new DiskDriveMountableItem(_)))
|
||||
}
|
||||
}
|
||||
@ -4,12 +4,12 @@ import ocelot.desktop.graphics.{IconDef, Icons}
|
||||
import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem}
|
||||
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
||||
import totoro.ocelot.brain.entity.NetworkCard
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
||||
import totoro.ocelot.brain.entity.traits.Entity
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class NetworkCardItem(val networkCard: NetworkCard) extends Item with ComponentItem with PersistableItem with CardItem {
|
||||
override def component: Entity with Environment = networkCard
|
||||
class NetworkCardItem(val card: NetworkCard) extends Item with ComponentItem with PersistableItem with CardItem {
|
||||
override def component: Entity with NetworkCard = card
|
||||
|
||||
override def factory: ItemFactory = NetworkCardItem.Factory
|
||||
}
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
package ocelot.desktop.inventory.item
|
||||
|
||||
import ocelot.desktop.inventory.Item
|
||||
import ocelot.desktop.inventory.traits.ComponentItem
|
||||
import ocelot.desktop.node.nodes.RackNode
|
||||
import ocelot.desktop.ui.widget.contextmenu.ContextMenu
|
||||
import totoro.ocelot.brain.entity.{Rack, result}
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, RackMountable}
|
||||
|
||||
trait RackMountableItem
|
||||
extends Item
|
||||
with ComponentItem
|
||||
{
|
||||
override def component: Entity with RackMountable
|
||||
|
||||
def isInRack: Boolean = slot.exists(_.inventory.isInstanceOf[RackNode])
|
||||
|
||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||
RackNode.addContextMenuEntriesOfMountable(menu, Some(this))
|
||||
|
||||
super.fillRmbMenu(menu)
|
||||
}
|
||||
}
|
||||
127
src/main/scala/ocelot/desktop/inventory/item/ServerItem.scala
Normal file
@ -0,0 +1,127 @@
|
||||
package ocelot.desktop.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.{IconDef, Icons}
|
||||
import ocelot.desktop.inventory.{ItemFactory, ItemRecoverer}
|
||||
import ocelot.desktop.ui.widget.slot._
|
||||
import ocelot.desktop.util.ComputerAware
|
||||
import totoro.ocelot.brain.entity.Server
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
class ServerItem(val server: Server)
|
||||
extends RackMountableItem
|
||||
with ComputerAware
|
||||
{
|
||||
override def component: Server = server
|
||||
override def factory: ItemFactory = new ServerItem.Factory(server.tier)
|
||||
|
||||
override def update(): Unit = {
|
||||
updateRunningSound()
|
||||
}
|
||||
|
||||
override def onRemoved(): Unit = {
|
||||
turnOff()
|
||||
window.get.close()
|
||||
|
||||
super.onRemoved()
|
||||
}
|
||||
|
||||
override def load(nbt: NBTTagCompound): Unit = {
|
||||
super.load(nbt)
|
||||
}
|
||||
|
||||
// ---------------------------- ComputerAware ----------------------------
|
||||
|
||||
override def computer: Server = server
|
||||
override def brainInventory: Inventory = server.inventory.owner
|
||||
|
||||
override def addSlotsBasedOnTier(): Unit = {
|
||||
floppySlot = None
|
||||
|
||||
server.tier match {
|
||||
case Tier.One =>
|
||||
cardSlots = addSlotWidgets(new CardSlotWidget(_, Tier.Two), new CardSlotWidget(_, Tier.Two))
|
||||
cpuSlot = addSlotWidget(new CpuSlotWidget(_, this, Tier.Two))
|
||||
componentBusSlots = addSlotWidgets(new ComponentBusSlotWidget(_, Tier.Two))
|
||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Two), new MemorySlotWidget(_, Tier.Two))
|
||||
diskSlots = addSlotWidgets(new HddSlotWidget(_, Tier.Two))
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
|
||||
case Tier.Two =>
|
||||
cardSlots = addSlotWidgets(new CardSlotWidget(_, Tier.Three), new CardSlotWidget(_, Tier.Two), new CardSlotWidget(_, Tier.Two))
|
||||
cpuSlot = addSlotWidget(new CpuSlotWidget(_, this, Tier.Three))
|
||||
componentBusSlots = addSlotWidgets(new ComponentBusSlotWidget(_, Tier.Three), new ComponentBusSlotWidget(_, Tier.Three))
|
||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
|
||||
diskSlots = addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Three))
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
|
||||
case _ =>
|
||||
cardSlots =
|
||||
if (server.tier == Tier.Three) {
|
||||
addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Two),
|
||||
new CardSlotWidget(_, Tier.Two)
|
||||
)
|
||||
}
|
||||
else {
|
||||
addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
)
|
||||
}
|
||||
|
||||
cpuSlot = addSlotWidget(new CpuSlotWidget(_, this, Tier.Three))
|
||||
|
||||
componentBusSlots = addSlotWidgets(
|
||||
new ComponentBusSlotWidget(_, Tier.Three),
|
||||
new ComponentBusSlotWidget(_, Tier.Three),
|
||||
new ComponentBusSlotWidget(_, Tier.Three),
|
||||
)
|
||||
|
||||
memorySlots = addSlotWidgets(
|
||||
new MemorySlotWidget(_, Tier.Three),
|
||||
new MemorySlotWidget(_, Tier.Three),
|
||||
new MemorySlotWidget(_, Tier.Three),
|
||||
new MemorySlotWidget(_, Tier.Three)
|
||||
)
|
||||
|
||||
diskSlots = addSlotWidgets(
|
||||
new HddSlotWidget(_, Tier.Three),
|
||||
new HddSlotWidget(_, Tier.Three),
|
||||
new HddSlotWidget(_, Tier.Three),
|
||||
new HddSlotWidget(_, Tier.Three),
|
||||
)
|
||||
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ServerItem {
|
||||
class Factory(_tier: Tier) extends ItemFactory {
|
||||
override type I = ServerItem
|
||||
|
||||
override def itemClass: Class[I] = classOf
|
||||
|
||||
override def name: String = s"Server (${_tier.label})"
|
||||
|
||||
override def tier: Option[Tier] = Some(_tier)
|
||||
|
||||
override def icon: IconDef = Icons.Server(_tier)
|
||||
|
||||
override def build(): ServerItem = {
|
||||
val item = new ServerItem(new Server(_tier))
|
||||
item.fillSlotsWithDefaultItems()
|
||||
item.syncSlots()
|
||||
item
|
||||
}
|
||||
|
||||
override def recoverers: Iterable[ItemRecoverer[_, _]] = Some(ItemRecoverer(new ServerItem(_)))
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,13 @@
|
||||
package ocelot.desktop.inventory.item
|
||||
|
||||
import ocelot.desktop.graphics.{IconDef, Icons}
|
||||
import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem}
|
||||
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
||||
import ocelot.desktop.inventory.{ItemFactory, ItemRecoverer}
|
||||
import totoro.ocelot.brain.entity.WirelessNetworkCard
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
abstract class WirelessNetworkCardItem(val card: WirelessNetworkCard)
|
||||
extends Item
|
||||
with ComponentItem
|
||||
with PersistableItem
|
||||
with CardItem {
|
||||
|
||||
override def component: Entity with Environment = card
|
||||
abstract class WirelessNetworkCardItem(override val card: WirelessNetworkCard)
|
||||
extends NetworkCardItem(card) {
|
||||
}
|
||||
|
||||
object WirelessNetworkCardItem {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package ocelot.desktop.inventory.traits
|
||||
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.ui.widget.window.Window
|
||||
|
||||
|
||||
88
src/main/scala/ocelot/desktop/node/ComputerAwareNode.scala
Normal file
@ -0,0 +1,88 @@
|
||||
package ocelot.desktop.node
|
||||
|
||||
import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.audio._
|
||||
import ocelot.desktop.geometry.Vector2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.node.ComputerAwareNode.{ErrorMessageMoveSpeed, MaxErrorMessageDistance}
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.BrainEvent
|
||||
import ocelot.desktop.ui.event.handlers.DiskActivityHandler
|
||||
import ocelot.desktop.ui.widget.ComputerErrorMessageLabel
|
||||
import ocelot.desktop.util.{Logging, Messages}
|
||||
import totoro.ocelot.brain.Settings
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware}
|
||||
import totoro.ocelot.brain.event._
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceAware)
|
||||
extends SyncedInventoryEntityNode(entity)
|
||||
with DiskActivityHandler
|
||||
with ShiftClickNode
|
||||
{
|
||||
private val messages = mutable.ArrayBuffer[(Float, ComputerErrorMessageLabel)]()
|
||||
|
||||
protected def addErrorMessage(message: ComputerErrorMessageLabel): Unit = synchronized {
|
||||
messages += ((0f, message))
|
||||
}
|
||||
|
||||
// TODO: Scala has lazy vals. Use them.
|
||||
private var soundCardStream: SoundStream = _
|
||||
private var soundCardSource: SoundSource = _
|
||||
|
||||
eventHandlers += {
|
||||
case BrainEvent(event: MachineCrashEvent) =>
|
||||
val message = Messages.lift(event.message) match {
|
||||
case Some(message) =>
|
||||
logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message code ${event.message}: $message")
|
||||
message
|
||||
|
||||
case None =>
|
||||
logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message: ${event.message}")
|
||||
event.message
|
||||
}
|
||||
|
||||
addErrorMessage(new ComputerErrorMessageLabel(this, message))
|
||||
|
||||
case BrainEvent(event: BeepEvent) if !Audio.isDisabled =>
|
||||
BeepGenerator.newBeep(".", event.frequency, event.duration).play()
|
||||
|
||||
case BrainEvent(event: BeepPatternEvent) if !Audio.isDisabled =>
|
||||
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)
|
||||
if (soundCardStream == null) {
|
||||
val (stream, source) = Audio.newStream(SoundCategory.Beep)
|
||||
soundCardStream = stream
|
||||
soundCardSource = source
|
||||
}
|
||||
soundCardStream.enqueue(samples)
|
||||
soundCardSource.volume = event.volume
|
||||
|
||||
case BrainEvent(_: SelfDestructingCardBoomEvent) =>
|
||||
OcelotDesktop.workspace.runLater(() => {
|
||||
SoundSource.MinecraftExplosion.play()
|
||||
destroy()
|
||||
})
|
||||
}
|
||||
|
||||
override protected val canOpen = true
|
||||
|
||||
override def drawParticles(g: Graphics): Unit = synchronized {
|
||||
for ((time, message) <- messages.reverseIterator) {
|
||||
message.position = message.initialPosition + Vector2D(0, -MaxErrorMessageDistance * time)
|
||||
message.alpha = 1 - time
|
||||
message.draw(g)
|
||||
}
|
||||
|
||||
messages.mapInPlace { case (t, message) => (t + ErrorMessageMoveSpeed * UiHandler.dt, message) }
|
||||
messages.filterInPlace(_._1 <= 1f)
|
||||
}
|
||||
}
|
||||
|
||||
object ComputerAwareNode {
|
||||
private val MaxErrorMessageDistance: Float = 50
|
||||
private val ErrorMessageMoveSpeed: Float = 0.5f
|
||||
}
|
||||
@ -2,6 +2,7 @@ package ocelot.desktop.node
|
||||
|
||||
import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.ClickEvent
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, SidedEnvironment}
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
@ -30,7 +31,7 @@ abstract class EntityNode(val entity: Entity with Environment) extends Node {
|
||||
}
|
||||
}
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
if (exposeAddress && entity.node != null && entity.node.address != null) {
|
||||
menu.addEntry(
|
||||
ContextMenuEntry("Copy address") {
|
||||
@ -39,14 +40,15 @@ abstract class EntityNode(val entity: Entity with Environment) extends Node {
|
||||
)
|
||||
}
|
||||
|
||||
super.setupContextMenu(menu)
|
||||
super.setupContextMenu(menu, event)
|
||||
}
|
||||
|
||||
override def ports: Array[NodePort] = Array(NodePort())
|
||||
|
||||
override def getNodeByPort(port: NodePort): network.Node = entity.node
|
||||
|
||||
override def shouldReceiveEventsFor(address: String): Boolean = address == entity.node.address
|
||||
override def shouldReceiveEventsFor(address: String): Boolean =
|
||||
entity.node != null && address == entity.node.address
|
||||
|
||||
override def dispose(): Unit = {
|
||||
super.dispose()
|
||||
|
||||
@ -2,6 +2,7 @@ package ocelot.desktop.node
|
||||
|
||||
import ocelot.desktop.color.RGBAColor
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.event.ClickEvent
|
||||
import ocelot.desktop.ui.widget.InputDialog
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.util.DrawUtils
|
||||
@ -24,9 +25,8 @@ trait LabeledNode extends Node {
|
||||
nbt.setString("label", _label.getOrElse(""))
|
||||
}
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
menu.addEntry(
|
||||
ContextMenuEntry("Set label") {
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
menu.addEntry(ContextMenuEntry("Set label") {
|
||||
new InputDialog(
|
||||
"Set label",
|
||||
text => {
|
||||
@ -34,10 +34,9 @@ trait LabeledNode extends Node {
|
||||
},
|
||||
_label.getOrElse(""),
|
||||
).show()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
super.setupContextMenu(menu)
|
||||
super.setupContextMenu(menu, event)
|
||||
}
|
||||
|
||||
override def drawLabel(g: Graphics): Unit = {
|
||||
|
||||
@ -3,13 +3,13 @@ package ocelot.desktop.node
|
||||
import ocelot.desktop.color.{Color, RGBAColor}
|
||||
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.node.Node.{HighlightSize, HoveredHighlight, MovingHighlight, NoHighlight, Size}
|
||||
import ocelot.desktop.node.Node._
|
||||
import ocelot.desktop.ui.event.handlers.{ClickHandler, DragHandler, HoverHandler}
|
||||
import ocelot.desktop.ui.event.{ClickEvent, DragEvent, HoverEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.ui.widget.window.Window
|
||||
import ocelot.desktop.ui.widget.window.Windowed
|
||||
import ocelot.desktop.ui.widget.{Widget, WorkspaceView}
|
||||
import ocelot.desktop.util.Persistable
|
||||
import ocelot.desktop.util.Disposable
|
||||
import ocelot.desktop.util.animation.ColorAnimation
|
||||
import org.lwjgl.input.Keyboard
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
@ -17,7 +17,7 @@ import totoro.ocelot.brain.network
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
abstract class Node extends Widget with DragHandler with ClickHandler with HoverHandler with Persistable {
|
||||
abstract class Node extends Widget with DragHandler with ClickHandler with HoverHandler with Windowed {
|
||||
var workspaceView: WorkspaceView = _
|
||||
|
||||
protected val highlight = new ColorAnimation(RGBAColor(0, 0, 0, 0))
|
||||
@ -31,23 +31,17 @@ abstract class Node extends Widget with DragHandler with ClickHandler with Hover
|
||||
size = minimumSize
|
||||
|
||||
override def load(nbt: NBTTagCompound): Unit = {
|
||||
super.load(nbt)
|
||||
|
||||
position = new Vector2D(nbt.getCompoundTag("pos"))
|
||||
window.foreach(window => {
|
||||
val tag = nbt.getCompoundTag("window")
|
||||
window.load(tag)
|
||||
})
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound): Unit = {
|
||||
val posTag = new NBTTagCompound
|
||||
position.save(posTag)
|
||||
nbt.setTag("pos", posTag)
|
||||
super.save(nbt)
|
||||
|
||||
window.foreach(window => {
|
||||
val tag = new NBTTagCompound
|
||||
window.save(tag)
|
||||
nbt.setTag("window", tag)
|
||||
})
|
||||
position.save(tag)
|
||||
nbt.setTag("pos", tag)
|
||||
}
|
||||
|
||||
override def receiveMouseEvents = true
|
||||
@ -92,7 +86,7 @@ abstract class Node extends Widget with DragHandler with ClickHandler with Hover
|
||||
highlight.goto(NoHighlight)
|
||||
}
|
||||
|
||||
def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
if (ports.nonEmpty) {
|
||||
menu.addEntry(
|
||||
ContextMenuEntry("Disconnect") {
|
||||
@ -182,9 +176,10 @@ abstract class Node extends Widget with DragHandler with ClickHandler with Hover
|
||||
window.foreach(_.open())
|
||||
else
|
||||
window.foreach(_.close())
|
||||
|
||||
case ClickEvent(MouseEvent.Button.Right, _) =>
|
||||
val menu = new ContextMenu
|
||||
setupContextMenu(menu)
|
||||
setupContextMenu(menu, event)
|
||||
root.get.contextMenus.open(menu)
|
||||
case _ =>
|
||||
}
|
||||
@ -272,10 +267,10 @@ abstract class Node extends Widget with DragHandler with ClickHandler with Hover
|
||||
|
||||
g.sprite(
|
||||
icon,
|
||||
position. x + HighlightSize,
|
||||
position.y + HighlightSize,
|
||||
size.width - HighlightSize * 2,
|
||||
size.height - HighlightSize * 2,
|
||||
position. x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
size.width - HighlightThickness * 2,
|
||||
size.height - HighlightThickness * 2,
|
||||
iconColor
|
||||
)
|
||||
}
|
||||
@ -294,9 +289,9 @@ abstract class Node extends Widget with DragHandler with ClickHandler with Hover
|
||||
}
|
||||
}
|
||||
|
||||
def window: Option[Window] = None
|
||||
override def dispose(): Unit = {
|
||||
super.dispose()
|
||||
|
||||
def dispose(): Unit = {
|
||||
disconnectFromAll()
|
||||
window.foreach(_.closeAndDispose())
|
||||
}
|
||||
@ -307,7 +302,10 @@ object Node {
|
||||
protected val HoveredHighlight: RGBAColor = RGBAColor(160, 160, 160)
|
||||
protected val NoHighlight: RGBAColor = RGBAColor(160, 160, 160, 0)
|
||||
|
||||
val Size = 68f
|
||||
val HighlightSize = 2f
|
||||
val SizeWithoutHighlight: Float = Size - HighlightSize * 2
|
||||
val TexelCount = 16f
|
||||
val Scale = 4f
|
||||
val HighlightThickness = 2f
|
||||
|
||||
val NoHighlightSize: Float = TexelCount * Scale
|
||||
val Size: Float = NoHighlightSize + HighlightThickness * 2f
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ case class NodePort(direction: Option[Direction.Value] = None) extends Ordered[N
|
||||
case Some(Direction.South) => ColorScheme("PortSouth")
|
||||
case Some(Direction.East) => ColorScheme("PortEast")
|
||||
case Some(Direction.West) => ColorScheme("PortWest")
|
||||
case None => ColorScheme("PortAny")
|
||||
case _ => ColorScheme("PortAny")
|
||||
}
|
||||
|
||||
override def compare(that: NodePort): Int = this.direction.compare(that.direction)
|
||||
|
||||
@ -2,7 +2,7 @@ package ocelot.desktop.node
|
||||
|
||||
import ocelot.desktop.entity.{Camera, OpenFMRadio}
|
||||
import ocelot.desktop.node.nodes._
|
||||
import totoro.ocelot.brain.entity.{Cable, Case, ColorfulLamp, FloppyDiskDrive, HologramProjector, IronNoteBlock, NoteBlock, Relay, Screen}
|
||||
import totoro.ocelot.brain.entity.{Cable, Case, ColorfulLamp, FloppyDiskDrive, HologramProjector, IronNoteBlock, NoteBlock, Rack, Relay, Screen}
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -27,20 +27,30 @@ object NodeRegistry {
|
||||
|
||||
for (tier <- Tier.One to Tier.Three) {
|
||||
addType(NodeType(s"Screen (${tier.label})", "nodes/screen/Standalone", tier) {
|
||||
new ScreenNode(new Screen(tier)).setup()
|
||||
val node = new ScreenNode(new Screen(tier))
|
||||
node.attachKeyboard()
|
||||
node
|
||||
})
|
||||
}
|
||||
|
||||
addType(NodeType("Disk Drive", "nodes/DiskDrive", None) {
|
||||
new DiskDriveNode(new FloppyDiskDrive(), initDisk = true)
|
||||
addType(NodeType("Disk Drive", "nodes/disk-drive/Default", None) {
|
||||
val node = new DiskDriveNode(new FloppyDiskDrive())
|
||||
node.fillSlotsWithDefaultItems()
|
||||
node
|
||||
})
|
||||
|
||||
for (tier <- Tier.One to Tier.Creative) {
|
||||
addType(NodeType(s"Computer Case (${tier.label})", "nodes/Computer", tier) {
|
||||
new ComputerNode(new Case(tier)).setup()
|
||||
addType(NodeType(s"Computer Case (${tier.label})", "nodes/computer/Default", tier) {
|
||||
val node = new ComputerNode(new Case(tier))
|
||||
node.fillSlotsWithDefaultItems()
|
||||
node
|
||||
})
|
||||
}
|
||||
|
||||
addType(NodeType("Rack", "nodes/rack/Default", None) {
|
||||
new RackNode(new Rack)
|
||||
})
|
||||
|
||||
addType(NodeType("Relay", "nodes/Relay", None) {
|
||||
new RelayNode(new Relay)
|
||||
})
|
||||
|
||||
28
src/main/scala/ocelot/desktop/node/ShiftClickNode.scala
Normal file
@ -0,0 +1,28 @@
|
||||
package ocelot.desktop.node
|
||||
|
||||
import ocelot.desktop.ui.event.sources.KeyEvents
|
||||
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
|
||||
import org.lwjgl.input.Keyboard
|
||||
|
||||
trait ShiftClickNode extends Node {
|
||||
protected def onShiftClick(event: ClickEvent): Unit
|
||||
protected def hoveredShiftStatusBarText: String
|
||||
|
||||
override def onClick(event: ClickEvent): Unit = {
|
||||
event match {
|
||||
case ClickEvent(MouseEvent.Button.Left, _) if KeyEvents.isDown(Keyboard.KEY_LSHIFT) =>
|
||||
onShiftClick(event)
|
||||
return
|
||||
case _ =>
|
||||
}
|
||||
|
||||
super.onClick(event)
|
||||
}
|
||||
|
||||
override def update(): Unit = {
|
||||
super.update()
|
||||
|
||||
if (isHovered || isMoving)
|
||||
root.get.statusBar.addKeyMouseEntry("icons/LMB", "SHIFT", hoveredShiftStatusBarText)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package ocelot.desktop.node
|
||||
|
||||
import ocelot.desktop.inventory.{Item, SyncedInventory}
|
||||
import ocelot.desktop.ui.event.Event
|
||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
||||
|
||||
abstract class SyncedInventoryEntityNode(entity: Entity with Environment)
|
||||
extends EntityNode(entity)
|
||||
with SyncedInventory
|
||||
{
|
||||
override def shouldReceiveEventsFor(address: String): Boolean =
|
||||
super.shouldReceiveEventsFor(address) ||
|
||||
brainInventory.inventory.entities.exists {
|
||||
case env: Environment => env.node.address == address
|
||||
}
|
||||
|
||||
override def handleEvent(event: Event): Unit = {
|
||||
super.handleEvent(event)
|
||||
|
||||
inventoryIterator.foreach(slot =>
|
||||
slot.get match {
|
||||
case Some(item: Item) =>
|
||||
item.handleEvent(event)
|
||||
case _ =>
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,396 +1,121 @@
|
||||
package ocelot.desktop.node.nodes
|
||||
|
||||
import ocelot.desktop.ColorScheme
|
||||
import ocelot.desktop.audio._
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.geometry.Vector2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.inventory.item._
|
||||
import ocelot.desktop.inventory.traits.ComponentItem
|
||||
import ocelot.desktop.inventory.{Item, SyncedInventory}
|
||||
import ocelot.desktop.node.{EntityNode, LabeledEntityNode}
|
||||
import ocelot.desktop.node.nodes.ComputerNode.{ErrorMessageMoveSpeed, MaxErrorMessageDistance}
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.sources.KeyEvents
|
||||
import ocelot.desktop.ui.event.{BrainEvent, ClickEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.Label
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import ocelot.desktop.inventory.SyncedInventory
|
||||
import ocelot.desktop.node.ComputerAwareNode
|
||||
import ocelot.desktop.node.Node.HighlightThickness
|
||||
import ocelot.desktop.ui.event.ClickEvent
|
||||
import ocelot.desktop.ui.widget.contextmenu.ContextMenu
|
||||
import ocelot.desktop.ui.widget.slot._
|
||||
import ocelot.desktop.util.{Logging, Messages, TierColor}
|
||||
import ocelot.desktop.util.{ComputerAware, DrawUtils, TierColor}
|
||||
import ocelot.desktop.windows.ComputerWindow
|
||||
import org.lwjgl.input.Keyboard
|
||||
import totoro.ocelot.brain.Settings
|
||||
import totoro.ocelot.brain.entity.Case
|
||||
import totoro.ocelot.brain.entity.traits.{Environment, Inventory}
|
||||
import totoro.ocelot.brain.event.FileSystemActivityType.Floppy
|
||||
import totoro.ocelot.brain.event._
|
||||
import totoro.ocelot.brain.loot.Loot
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.entity.traits.{Computer, Inventory, TieredPersistable}
|
||||
import totoro.ocelot.brain.util.Tier
|
||||
import totoro.ocelot.brain.util.Tier.Tier
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.math.Ordering.Implicits.infixOrderingOps
|
||||
import scala.reflect.ClassTag
|
||||
import scala.util.Random
|
||||
class ComputerNode(val computerCase: Case)
|
||||
extends ComputerAwareNode(computerCase)
|
||||
with ComputerAware
|
||||
{
|
||||
override val icon: String = "nodes/computer/Default"
|
||||
override def iconColor: Color = TierColor.get(computerCase.tier)
|
||||
|
||||
class ComputerNode(val computer: Case)
|
||||
extends EntityNode(computer)
|
||||
with LabeledEntityNode
|
||||
with Logging
|
||||
with SyncedInventory {
|
||||
|
||||
node =>
|
||||
|
||||
override type I = Item with ComponentItem
|
||||
|
||||
var eepromSlot: EepromSlotWidget = _
|
||||
var cpuSlot: CpuSlotWidget = _
|
||||
var memorySlots: Array[MemorySlotWidget] = Array.empty
|
||||
var cardSlots: Array[CardSlotWidget] = Array.empty
|
||||
var diskSlots: Array[HddSlotWidget] = Array.empty
|
||||
var floppySlot: Option[FloppySlotWidget] = None
|
||||
|
||||
override def brainInventory: Inventory = computer.inventory.owner
|
||||
|
||||
private def slots: IterableOnce[SlotWidget[I]] = (
|
||||
// slots may be null during initialization
|
||||
Option(eepromSlot).iterator ++
|
||||
Option(cpuSlot).iterator ++
|
||||
memorySlots.iterator ++
|
||||
cardSlots.iterator ++
|
||||
diskSlots.iterator ++
|
||||
floppySlot.iterator
|
||||
).map(_.asInstanceOf[SlotWidget[I]])
|
||||
|
||||
// NOTE: `soundComputerRunning` must be lazy so that it doesn't get loaded before the audio subsystem is initialized
|
||||
private lazy val soundComputerRunning = SoundSource.fromBuffer(
|
||||
SoundBuffers.MachineComputerRunning,
|
||||
SoundCategory.Environment,
|
||||
looping = true,
|
||||
)
|
||||
// TODO: Scala has lazy vals. Use them.
|
||||
private var soundCardStream: SoundStream = _
|
||||
private var soundCardSource: SoundSource = _
|
||||
|
||||
setupSlots()
|
||||
|
||||
private class ErrorMessageLabel(override val text: String) extends Label {
|
||||
override def isSmall: Boolean = true
|
||||
|
||||
var alpha: Float = 1f
|
||||
|
||||
override def color: Color = ColorScheme("ErrorMessage").toRGBANorm.mapA(_ * alpha)
|
||||
|
||||
val initialPosition: Vector2D = {
|
||||
val position = node.position
|
||||
val size = node.size
|
||||
|
||||
position + Vector2D(size.width / 2 - minimumSize.width / 2, -minimumSize.height)
|
||||
}
|
||||
|
||||
position = initialPosition
|
||||
}
|
||||
|
||||
eventHandlers += {
|
||||
case BrainEvent(event: MachineCrashEvent) =>
|
||||
val message = Messages.lift(event.message) match {
|
||||
case Some(message) =>
|
||||
logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message code ${event.message}: $message")
|
||||
message
|
||||
|
||||
case None =>
|
||||
logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message: ${event.message}")
|
||||
event.message
|
||||
}
|
||||
|
||||
addErrorMessage(new ErrorMessageLabel(message))
|
||||
|
||||
case BrainEvent(event: BeepEvent) if !Audio.isDisabled =>
|
||||
BeepGenerator.newBeep(".", event.frequency, event.duration).play()
|
||||
|
||||
case BrainEvent(event: BeepPatternEvent) if !Audio.isDisabled =>
|
||||
BeepGenerator.newBeep(event.pattern, 1000, 200).play()
|
||||
|
||||
case BrainEvent(event: FileSystemActivityEvent) if !Audio.isDisabled =>
|
||||
val soundFloppyAccess = SoundBuffers.MachineFloppyAccess
|
||||
.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
||||
val soundHDDAccess = SoundBuffers.MachineHDDAccess
|
||||
.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
||||
val sound = if (event.activityType == Floppy) soundFloppyAccess else soundHDDAccess
|
||||
sound(Random.between(0, sound.length)).play()
|
||||
|
||||
case BrainEvent(event: SoundCardAudioEvent) if !Audio.isDisabled =>
|
||||
val samples = SoundSamples(event.data, Settings.get.soundCardSampleRate, SoundSamples.Format.Mono8)
|
||||
if (soundCardStream == null) {
|
||||
val (stream, source) = Audio.newStream(SoundCategory.Beep)
|
||||
soundCardStream = stream
|
||||
soundCardSource = source
|
||||
}
|
||||
soundCardStream.enqueue(samples)
|
||||
soundCardSource.volume = event.volume
|
||||
|
||||
case BrainEvent(_: SelfDestructingCardBoomEvent) =>
|
||||
computer.workspace.runLater(
|
||||
() => {
|
||||
SoundSources.MinecraftExplosion.play()
|
||||
destroy()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override def shouldReceiveEventsFor(address: String): Boolean = super.shouldReceiveEventsFor(address) ||
|
||||
computer.inventory.entities.exists { case env: Environment => env.node.address == address }
|
||||
|
||||
def setup(): ComputerNode = {
|
||||
cpuSlot.item = new CpuItem.Factory(computer.tier min Tier.Three).build()
|
||||
memorySlots(0).item = new MemoryItem.Factory((computer.tier min Tier.Three).toExtended(true)).build()
|
||||
memorySlots(1).item = new MemoryItem.Factory((computer.tier min Tier.Three).toExtended(true)).build()
|
||||
cardSlots(0).item = new GraphicsCardItem.Factory(computer.tier min Tier.Two).build()
|
||||
|
||||
for (floppySlot <- floppySlot) {
|
||||
floppySlot.item = new FloppyItem.Factory.Loot(Loot.OpenOsFloppy).build()
|
||||
}
|
||||
|
||||
eepromSlot.item = new EepromItem.Factory.Loot(Loot.LuaBiosEEPROM).build()
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
override val icon: String = "nodes/Computer"
|
||||
|
||||
override def iconColor: Color = TierColor.get(computer.tier)
|
||||
|
||||
override protected val canOpen = true
|
||||
|
||||
def turnOn(): Unit = {
|
||||
computer.turnOn()
|
||||
soundComputerRunning.play()
|
||||
}
|
||||
|
||||
def turnOff(): Unit = {
|
||||
computer.turnOff()
|
||||
soundComputerRunning.stop()
|
||||
}
|
||||
|
||||
def isRunning: Boolean = computer.machine.isRunning
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
if (isRunning) {
|
||||
menu.addEntry(
|
||||
ContextMenuEntry("Turn off") {
|
||||
turnOff()
|
||||
},
|
||||
)
|
||||
menu.addEntry(
|
||||
ContextMenuEntry("Reboot") {
|
||||
computer.turnOff()
|
||||
computer.turnOn()
|
||||
},
|
||||
)
|
||||
} else {
|
||||
menu.addEntry(
|
||||
ContextMenuEntry("Turn on") {
|
||||
turnOn()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
menu.addEntry(
|
||||
new ContextMenuSubmenu("Set tier") {
|
||||
for (tier <- Tier.One to Tier.Creative) {
|
||||
addEntry(
|
||||
ContextMenuEntry(tier.label) {
|
||||
changeTier(tier)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
addPowerContextMenuEntries(menu)
|
||||
addTierContextMenuEntries(menu)
|
||||
|
||||
menu.addSeparator()
|
||||
super.setupContextMenu(menu)
|
||||
}
|
||||
|
||||
override def onClick(event: ClickEvent): Unit = {
|
||||
event match {
|
||||
case ClickEvent(MouseEvent.Button.Left, _) =>
|
||||
if (KeyEvents.isDown(Keyboard.KEY_LSHIFT)) {
|
||||
if (isRunning) {
|
||||
turnOff()
|
||||
} else {
|
||||
turnOn()
|
||||
}
|
||||
} else {
|
||||
super.onClick(event)
|
||||
}
|
||||
|
||||
case event => super.onClick(event)
|
||||
}
|
||||
}
|
||||
|
||||
private def changeTier(tier: Tier): Unit = {
|
||||
computer.tier = tier
|
||||
|
||||
val items = slots.iterator.flatMap(_.item).toArray
|
||||
clearInventory()
|
||||
setupSlots()
|
||||
insertItems(items)
|
||||
|
||||
if (currentWindow != null) {
|
||||
currentWindow.reloadWindow()
|
||||
}
|
||||
}
|
||||
|
||||
private def insertItems(items: IterableOnce[I]): Unit = {
|
||||
def findBestSlot[A <: I](item: A, candidates: IterableOnce[SlotWidget[A]]): Option[SlotWidget[A]] = {
|
||||
candidates.iterator
|
||||
.filter(_.item.isEmpty)
|
||||
.filter(_.isItemAccepted(item.factory))
|
||||
.minByOption(_.slotTier)
|
||||
}
|
||||
|
||||
for (item <- items; newSlot <- findBestSlot(item, slots)) {
|
||||
newSlot.item = item
|
||||
}
|
||||
}
|
||||
|
||||
private def setupSlots(): Unit = {
|
||||
var slotIndex = 0
|
||||
|
||||
def nextSlot(): Slot = {
|
||||
val result = Slot(slotIndex)
|
||||
slotIndex += 1
|
||||
result
|
||||
}
|
||||
|
||||
def addSlot[T <: SlotWidget[_]](factory: Slot => T): T = {
|
||||
val slot = nextSlot()
|
||||
val widget = factory(slot)
|
||||
|
||||
widget
|
||||
}
|
||||
|
||||
def addSlots[T <: SlotWidget[_] : ClassTag](factories: (Slot => T)*): Array[T] = {
|
||||
val array = Array.newBuilder[T]
|
||||
|
||||
for (factory <- factories) {
|
||||
array += addSlot(factory)
|
||||
}
|
||||
|
||||
array.result()
|
||||
}
|
||||
|
||||
for (slot <- slots) {
|
||||
slot.dispose()
|
||||
}
|
||||
|
||||
computer.tier match {
|
||||
case Tier.One =>
|
||||
cardSlots = addSlots(new CardSlotWidget(_, Tier.One), new CardSlotWidget(_, Tier.One))
|
||||
memorySlots = addSlots(new MemorySlotWidget(_, Tier.One))
|
||||
diskSlots = addSlots(new HddSlotWidget(_, Tier.One))
|
||||
floppySlot = None
|
||||
cpuSlot = addSlot(new CpuSlotWidget(_, this, Tier.One))
|
||||
// no idea why on earth the memory slots are split in two here
|
||||
memorySlots :+= addSlot(new MemorySlotWidget(_, Tier.One))
|
||||
eepromSlot = addSlot(new EepromSlotWidget(_))
|
||||
|
||||
case Tier.Two =>
|
||||
cardSlots = addSlots(new CardSlotWidget(_, Tier.Two), new CardSlotWidget(_, Tier.One))
|
||||
memorySlots = addSlots(new MemorySlotWidget(_, Tier.Two), new MemorySlotWidget(_, Tier.Two))
|
||||
diskSlots = addSlots(new HddSlotWidget(_, Tier.Two), new HddSlotWidget(_, Tier.One))
|
||||
floppySlot = None
|
||||
cpuSlot = addSlot(new CpuSlotWidget(_, this, Tier.Two))
|
||||
eepromSlot = addSlot(new EepromSlotWidget(_))
|
||||
|
||||
case _ =>
|
||||
cardSlots = if (computer.tier == Tier.Three) {
|
||||
addSlots(new CardSlotWidget(_, Tier.Three), new CardSlotWidget(_, Tier.Two), new CardSlotWidget(_, Tier.Two))
|
||||
} else {
|
||||
addSlots(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
)
|
||||
}
|
||||
|
||||
memorySlots = addSlots(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
|
||||
|
||||
diskSlots = if (computer.tier == Tier.Three) {
|
||||
addSlots(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Two))
|
||||
} else {
|
||||
addSlots(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Three))
|
||||
}
|
||||
|
||||
floppySlot = Some(addSlot(new FloppySlotWidget(_)))
|
||||
cpuSlot = addSlot(new CpuSlotWidget(_, this, Tier.Three))
|
||||
eepromSlot = addSlot(new EepromSlotWidget(_))
|
||||
}
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
super.draw(g)
|
||||
|
||||
val hasErred = computer.machine.lastError != null
|
||||
|
||||
if (isRunning && !hasErred)
|
||||
g.sprite("nodes/ComputerOnOverlay", position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
||||
|
||||
if (!isRunning && hasErred)
|
||||
g.sprite("nodes/ComputerErrorOverlay", position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
||||
|
||||
if (isRunning && System.currentTimeMillis() - computer.machine.lastDiskAccess < 400 && Math.random() > 0.1)
|
||||
g.sprite("nodes/ComputerActivityOverlay", position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
||||
super.setupContextMenu(menu, event)
|
||||
}
|
||||
|
||||
override def update(): Unit = {
|
||||
super.update()
|
||||
|
||||
if (!isRunning && soundComputerRunning.isPlaying) {
|
||||
soundComputerRunning.stop()
|
||||
} else if (isRunning && !soundComputerRunning.isPlaying && !Audio.isDisabled) {
|
||||
soundComputerRunning.play()
|
||||
}
|
||||
|
||||
if (isHovered || isMoving) {
|
||||
root.get.statusBar.addKeyMouseEntry("icons/LMB", "SHIFT", if (isRunning) "Turn Off" else "Turn On")
|
||||
}
|
||||
updateRunningSound()
|
||||
}
|
||||
|
||||
override def dispose(): Unit = {
|
||||
super.dispose()
|
||||
|
||||
soundComputerRunning.stop()
|
||||
}
|
||||
|
||||
private var currentWindow: ComputerWindow = _
|
||||
override def draw(g: Graphics): Unit = {
|
||||
super.draw(g)
|
||||
|
||||
override def window: Option[ComputerWindow] = {
|
||||
if (currentWindow == null) {
|
||||
currentWindow = new ComputerWindow(this)
|
||||
Some(currentWindow)
|
||||
} else Some(currentWindow)
|
||||
DrawUtils.drawComputerNodeActivity(
|
||||
g,
|
||||
position.x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
computerCase.machine,
|
||||
"nodes/computer/"
|
||||
)
|
||||
}
|
||||
|
||||
private val messages = mutable.ArrayBuffer[(Float, ErrorMessageLabel)]()
|
||||
// ---------------------------- ComputerAware ----------------------------
|
||||
|
||||
private def addErrorMessage(message: ErrorMessageLabel): Unit = synchronized {
|
||||
messages += ((0f, message))
|
||||
override def computer: Case = computerCase
|
||||
override def brainInventory: Inventory = computerCase.inventory.owner
|
||||
|
||||
override def addSlotsBasedOnTier(): Unit = {
|
||||
computerCase.tier match {
|
||||
case Tier.One =>
|
||||
cardSlots = addSlotWidgets(new CardSlotWidget(_, Tier.One), new CardSlotWidget(_, Tier.One))
|
||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.One))
|
||||
diskSlots = addSlotWidgets(new HddSlotWidget(_, Tier.One))
|
||||
floppySlot = None
|
||||
cpuSlot = addSlotWidget(new CpuSlotWidget(_, this, Tier.One))
|
||||
// no idea why on earth the memory slots are split in two here
|
||||
memorySlots :+= addSlotWidget(new MemorySlotWidget(_, Tier.One))
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
|
||||
case Tier.Two =>
|
||||
cardSlots = addSlotWidgets(new CardSlotWidget(_, Tier.Two), new CardSlotWidget(_, Tier.One))
|
||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Two), new MemorySlotWidget(_, Tier.Two))
|
||||
diskSlots = addSlotWidgets(new HddSlotWidget(_, Tier.Two), new HddSlotWidget(_, Tier.One))
|
||||
floppySlot = None
|
||||
cpuSlot = addSlotWidget(new CpuSlotWidget(_, this, Tier.Two))
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
|
||||
case _ =>
|
||||
cardSlots =
|
||||
if (computerCase.tier == Tier.Three) {
|
||||
addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Two),
|
||||
new CardSlotWidget(_, Tier.Two)
|
||||
)
|
||||
}
|
||||
else {
|
||||
addSlotWidgets(
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
new CardSlotWidget(_, Tier.Three),
|
||||
)
|
||||
}
|
||||
|
||||
override def drawParticles(g: Graphics): Unit = synchronized {
|
||||
for ((time, message) <- messages.reverseIterator) {
|
||||
message.position = message.initialPosition + Vector2D(0, -MaxErrorMessageDistance * time)
|
||||
message.alpha = 1 - time
|
||||
message.draw(g)
|
||||
memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
|
||||
|
||||
diskSlots =
|
||||
if (computerCase.tier == Tier.Three) {
|
||||
addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Two))
|
||||
}
|
||||
else {
|
||||
addSlotWidgets(new HddSlotWidget(_, Tier.Three), new HddSlotWidget(_, Tier.Three))
|
||||
}
|
||||
|
||||
messages.mapInPlace { case (t, message) => (t + ErrorMessageMoveSpeed * UiHandler.dt, message) }
|
||||
messages.filterInPlace(_._1 <= 1f)
|
||||
floppySlot = Some(addSlotWidget(new FloppySlotWidget(_)))
|
||||
cpuSlot = addSlotWidget(new CpuSlotWidget(_, this, Tier.Three))
|
||||
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
|
||||
}
|
||||
}
|
||||
|
||||
object ComputerNode {
|
||||
private val MaxErrorMessageDistance: Float = 50
|
||||
private val ErrorMessageMoveSpeed: Float = 0.5f
|
||||
}
|
||||
|
||||
// ---------------------------- ShiftClickNode ----------------------------
|
||||
|
||||
override protected def onShiftClick(event: ClickEvent): Unit = toggleIsTurnedOn()
|
||||
|
||||
override protected def hoveredShiftStatusBarText: String = if (computer.machine.isRunning) "Turn off" else "Turn on"
|
||||
}
|
||||
@ -1,77 +1,58 @@
|
||||
package ocelot.desktop.node.nodes
|
||||
|
||||
import ocelot.desktop.color.IntColor
|
||||
import ocelot.desktop.geometry.Rect2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.inventory.SyncedInventory
|
||||
import ocelot.desktop.inventory.item.FloppyItem
|
||||
import ocelot.desktop.node.{EntityNode, LabeledEntityNode}
|
||||
import ocelot.desktop.ui.widget.slot.FloppySlotWidget
|
||||
import ocelot.desktop.windows.DiskDriveWindow
|
||||
import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize}
|
||||
import ocelot.desktop.node.{LabeledEntityNode, ShiftClickNode, SyncedInventoryEntityNode}
|
||||
import ocelot.desktop.ui.event.ClickEvent
|
||||
import ocelot.desktop.ui.event.handlers.DiskActivityHandler
|
||||
import ocelot.desktop.ui.widget.contextmenu.ContextMenu
|
||||
import ocelot.desktop.util.DiskDriveAware
|
||||
import totoro.ocelot.brain.entity.FloppyDiskDrive
|
||||
import totoro.ocelot.brain.entity.traits.Inventory
|
||||
import totoro.ocelot.brain.loot.Loot
|
||||
import totoro.ocelot.brain.util.DyeColor
|
||||
|
||||
class DiskDriveNode(val diskDrive: FloppyDiskDrive, initDisk: Boolean)
|
||||
extends EntityNode(diskDrive)
|
||||
class DiskDriveNode(entity: FloppyDiskDrive)
|
||||
extends SyncedInventoryEntityNode(entity)
|
||||
with LabeledEntityNode
|
||||
with SyncedInventory {
|
||||
|
||||
def this(diskDrive: FloppyDiskDrive) = {
|
||||
this(diskDrive, false)
|
||||
}
|
||||
|
||||
override type I = FloppyItem
|
||||
|
||||
override def brainInventory: Inventory = diskDrive.inventory.owner
|
||||
|
||||
val slot: FloppySlotWidget = new FloppySlotWidget(Slot(0))
|
||||
|
||||
if (initDisk) {
|
||||
slot.item = new FloppyItem.Factory.Loot(Loot.OpenOsFloppy).build()
|
||||
}
|
||||
|
||||
override def icon: String = "nodes/DiskDrive"
|
||||
|
||||
with DiskDriveAware
|
||||
with DiskActivityHandler
|
||||
with ShiftClickNode
|
||||
{
|
||||
override def icon: String = "nodes/disk-drive/Default"
|
||||
override protected val canOpen = true
|
||||
|
||||
private val colorMap: Map[DyeColor, Int] = Map(
|
||||
DyeColor.Black -> 0x444444, // 0x1E1B1B
|
||||
DyeColor.Red -> 0xB3312C,
|
||||
DyeColor.Green -> 0x339911, // 0x3B511A
|
||||
DyeColor.Brown -> 0x51301A,
|
||||
DyeColor.Blue -> 0x6666FF, // 0x253192
|
||||
DyeColor.Purple -> 0x7B2FBE,
|
||||
DyeColor.Cyan -> 0x66FFFF, // 0x287697
|
||||
DyeColor.Silver -> 0xABABAB,
|
||||
DyeColor.Gray -> 0x666666, // 0x434343
|
||||
DyeColor.Pink -> 0xD88198,
|
||||
DyeColor.Lime -> 0x66FF66, // 0x41CD34
|
||||
DyeColor.Yellow -> 0xFFFF66, // 0xDECF2A
|
||||
DyeColor.LightBlue -> 0xAAAAFF, // 0x6689D3
|
||||
DyeColor.Magenta -> 0xC354CD,
|
||||
DyeColor.Orange -> 0xEB8844,
|
||||
DyeColor.White -> 0xF0F0F0
|
||||
)
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
addDiskDriveMenuEntries(menu)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
super.setupContextMenu(menu, event)
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
super.draw(g)
|
||||
|
||||
if (System.currentTimeMillis() - diskDrive.lastDiskAccess < 400 && Math.random() > 0.1) {
|
||||
g.sprite("nodes/DiskDriveActivity", position.x + 2, position.y + 2, size.width - 4, size.height - 4)
|
||||
}
|
||||
|
||||
for (item <- slot.item) {
|
||||
g.sprite(
|
||||
"nodes/DiskDriveFloppy",
|
||||
position.x + 2,
|
||||
position.y + 2,
|
||||
size.width - 4,
|
||||
size.height - 4,
|
||||
IntColor(colorMap(item.color.value)),
|
||||
drawActivityAndFloppy(
|
||||
g,
|
||||
Rect2D(
|
||||
position.x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize
|
||||
),
|
||||
"nodes/disk-drive/"
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------- DiskDriveAware ----------------------------
|
||||
|
||||
override def floppyDiskDrive: FloppyDiskDrive = entity
|
||||
|
||||
// ---------------------------- ShiftClickNode ----------------------------
|
||||
|
||||
override protected def onShiftClick(event: ClickEvent): Unit = {
|
||||
if (isFloppyItemPresent)
|
||||
eject()
|
||||
}
|
||||
|
||||
override val window: Option[DiskDriveWindow] = Some(new DiskDriveWindow(this))
|
||||
override protected def hoveredShiftStatusBarText: String = "Eject floppy"
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
package ocelot.desktop.node.nodes
|
||||
|
||||
import ocelot.desktop.ui.event.ClickEvent
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import totoro.ocelot.brain.entity.NoteBlock
|
||||
|
||||
class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBlock) {
|
||||
override def icon: String = "nodes/NoteBlock"
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
menu.addEntry(new ContextMenuSubmenu("Instrument") {
|
||||
{
|
||||
val maxLen = NoteBlockNode.Instruments.map(_._2.length).max
|
||||
@ -20,7 +21,8 @@ class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBloc
|
||||
})
|
||||
|
||||
menu.addSeparator()
|
||||
super.setupContextMenu(menu)
|
||||
|
||||
super.setupContextMenu(menu, event)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
200
src/main/scala/ocelot/desktop/node/nodes/RackNode.scala
Normal file
@ -0,0 +1,200 @@
|
||||
package ocelot.desktop.node.nodes
|
||||
|
||||
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
|
||||
import ocelot.desktop.graphics.{Graphics, Icons}
|
||||
import ocelot.desktop.inventory.item.{DiskDriveMountableItem, RackMountableItem, ServerItem}
|
||||
import ocelot.desktop.inventory.{Item, SyncedInventory}
|
||||
import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize, Size, TexelCount}
|
||||
import ocelot.desktop.node.{ComputerAwareNode, NodePort}
|
||||
import ocelot.desktop.ui.event.ClickEvent
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.ui.widget.window.Window
|
||||
import ocelot.desktop.util.DrawUtils
|
||||
import ocelot.desktop.windows.RackWindow
|
||||
import totoro.ocelot.brain.entity.Rack
|
||||
import totoro.ocelot.brain.entity.traits.{ComponentInventory, Environment, Inventory, RackMountable, WorkspaceAware}
|
||||
import totoro.ocelot.brain.network
|
||||
import totoro.ocelot.brain.util.Direction
|
||||
|
||||
class RackNode(val rack: Rack)
|
||||
extends ComputerAwareNode(rack)
|
||||
{
|
||||
override val icon: String = "nodes/rack/Empty"
|
||||
|
||||
override def exposeAddress = false
|
||||
|
||||
override def ports: Array[NodePort] = Array(
|
||||
NodePort(Some(Direction.Bottom)),
|
||||
NodePort(Some(Direction.Top)),
|
||||
NodePort(Some(Direction.Back)),
|
||||
NodePort(Some(Direction.Right)),
|
||||
NodePort(Some(Direction.Left))
|
||||
)
|
||||
|
||||
override def getNodeByPort(port: NodePort): network.Node = rack.sidedNode(port.direction.get)
|
||||
|
||||
override def shouldReceiveEventsFor(address: String): Boolean =
|
||||
super.shouldReceiveEventsFor(address) ||
|
||||
rack.inventory.entities.exists {
|
||||
case mountable: RackMountable if mountable.node.address == address => true
|
||||
case mountable: RackMountable with ComponentInventory => mountable.inventory.entities.exists {
|
||||
case environment: Environment => environment.node.address == address
|
||||
}
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
RackNode.addContextMenuEntriesOfMountable(menu, getMountableByClick(event))
|
||||
|
||||
super.setupContextMenu(menu, event)
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
super.draw(g)
|
||||
|
||||
val x = position.x + HighlightThickness
|
||||
val y = position.y + HighlightThickness
|
||||
var prefix: String = null
|
||||
|
||||
for (i <- 0 until 4) {
|
||||
Slot(i).get match {
|
||||
case Some(serverItem: ServerItem) =>
|
||||
prefix = s"nodes/rack/server/$i/"
|
||||
|
||||
// Background
|
||||
g.sprite(
|
||||
s"${prefix}Default",
|
||||
x,
|
||||
y,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize
|
||||
)
|
||||
|
||||
// Activity overlay
|
||||
DrawUtils.drawComputerNodeActivity(
|
||||
g,
|
||||
x,
|
||||
y,
|
||||
serverItem.server.machine,
|
||||
prefix
|
||||
)
|
||||
case Some(diskDriveMountableItem: DiskDriveMountableItem) =>
|
||||
prefix = s"nodes/rack/drive/$i/"
|
||||
|
||||
// Background
|
||||
g.sprite(
|
||||
s"${prefix}Default",
|
||||
x,
|
||||
y,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize
|
||||
)
|
||||
|
||||
diskDriveMountableItem.drawActivityAndFloppy(
|
||||
g,
|
||||
Rect2D(
|
||||
x,
|
||||
y,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize
|
||||
),
|
||||
prefix
|
||||
)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def dispose(): Unit = {
|
||||
for (i <- 0 until 4) {
|
||||
Slot(i).get match {
|
||||
case Some(serverItem: ServerItem) => serverItem.dispose()
|
||||
case _=>
|
||||
}
|
||||
}
|
||||
|
||||
super.dispose()
|
||||
}
|
||||
|
||||
// -------------------------------- Inventory --------------------------------
|
||||
|
||||
override type I = Item with RackMountableItem
|
||||
override def brainInventory: Inventory = rack.inventory.owner
|
||||
|
||||
// -------------------------------- Window --------------------------------
|
||||
|
||||
private lazy val currentWindow = new RackWindow(this)
|
||||
|
||||
override def window: Option[Window] = Some(currentWindow)
|
||||
|
||||
// ---------------------------- ShiftClickNode ----------------------------
|
||||
|
||||
private def getMountableByClick(event: ClickEvent): Option[RackMountableItem] = {
|
||||
val horizontalMargin = HighlightThickness + (1 / TexelCount) * Size
|
||||
val verticalMargin = HighlightThickness + (2 / TexelCount) * Size
|
||||
|
||||
val localPosition = Vector2D(
|
||||
event.mousePos.x - position.x - horizontalMargin,
|
||||
event.mousePos.y - position.y - verticalMargin
|
||||
)
|
||||
|
||||
val m = Size2D(
|
||||
this.width - horizontalMargin * 2,
|
||||
this.height - verticalMargin * 2
|
||||
)
|
||||
|
||||
// Checking if click was inside mountables area
|
||||
if (localPosition.x < 0 || localPosition.y < 0 || localPosition.x > m.width || localPosition.y > m.height)
|
||||
return None
|
||||
|
||||
val mountableIndex = (localPosition.y / m.height * 4).toInt
|
||||
|
||||
Slot(mountableIndex).get.collect {
|
||||
case item: RackMountableItem => item
|
||||
}
|
||||
}
|
||||
|
||||
override protected def onShiftClick(event: ClickEvent): Unit = {
|
||||
getMountableByClick(event) match {
|
||||
case Some(serverItem: ServerItem) =>
|
||||
serverItem.toggleIsTurnedOn()
|
||||
|
||||
case Some(diskDriveMountableItem: DiskDriveMountableItem) =>
|
||||
if (diskDriveMountableItem.isFloppyItemPresent)
|
||||
diskDriveMountableItem.eject()
|
||||
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
override protected def hoveredShiftStatusBarText: String = "Turn server on/off"
|
||||
}
|
||||
|
||||
object RackNode {
|
||||
def addContextMenuEntriesOfMountable(menu: ContextMenu, item: Option[RackMountableItem]): Unit = {
|
||||
item match {
|
||||
case Some(serverItem: ServerItem) =>
|
||||
if (serverItem.isInRack) {
|
||||
serverItem.addPowerContextMenuEntries(menu)
|
||||
menu.addSeparator()
|
||||
}
|
||||
|
||||
serverItem.addTierContextMenuEntries(menu)
|
||||
|
||||
menu.addEntry(ContextMenuEntry("Configure server") {
|
||||
serverItem.window.get.open()
|
||||
})
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
case Some(diskDriveMountableItem: DiskDriveMountableItem) =>
|
||||
if (diskDriveMountableItem.isFloppyItemPresent) {
|
||||
diskDriveMountableItem.addDiskDriveMenuEntries(menu)
|
||||
|
||||
menu.addSeparator()
|
||||
}
|
||||
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
package ocelot.desktop.node.nodes
|
||||
|
||||
import ocelot.desktop.{OcelotDesktop, Settings}
|
||||
import ocelot.desktop.color.{Color, IntColor}
|
||||
import ocelot.desktop.geometry.{Rect2D, Size2D}
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.node.Node.{HighlightSize, Size, SizeWithoutHighlight}
|
||||
import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize, Size}
|
||||
import ocelot.desktop.node.nodes.ScreenNode.{BorderSize, FontHeight, FontWidth}
|
||||
import ocelot.desktop.node.{EntityNode, LabeledEntityNode, Node}
|
||||
import ocelot.desktop.node.{EntityNode, LabeledEntityNode}
|
||||
import ocelot.desktop.ui.event.ClickEvent
|
||||
import ocelot.desktop.ui.widget.ScreenAspectRatioDialog
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.util.TierColor
|
||||
import ocelot.desktop.windows.ScreenWindow
|
||||
import ocelot.desktop.{OcelotDesktop, Settings}
|
||||
import totoro.ocelot.brain.entity.{Keyboard, Screen}
|
||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||
import totoro.ocelot.brain.util.PackedColor
|
||||
@ -50,12 +51,11 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
}
|
||||
}
|
||||
|
||||
def setup(): ScreenNode = {
|
||||
def attachKeyboard(): Unit = {
|
||||
val kbd = new Keyboard
|
||||
OcelotDesktop.workspace.add(kbd)
|
||||
screen.connect(kbd)
|
||||
keyboard = Some(kbd)
|
||||
this
|
||||
}
|
||||
|
||||
override def icon: String = "nodes/screen/Standalone"
|
||||
@ -64,7 +64,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
|
||||
override protected val canOpen = true
|
||||
|
||||
override def setupContextMenu(menu: ContextMenu): Unit = {
|
||||
override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
|
||||
if (screen.getPowerState)
|
||||
menu.addEntry(ContextMenuEntry("Turn off") { screen.setPowerState(false) })
|
||||
else
|
||||
@ -76,7 +76,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
super.setupContextMenu(menu)
|
||||
super.setupContextMenu(menu, event)
|
||||
}
|
||||
|
||||
def drawScreenData(g: Graphics, startX: Float, startY: Float, scaleX: Float, scaleY: Float): Unit = {
|
||||
@ -131,10 +131,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
if (aspectRatioHeight == 1) {
|
||||
drawScreenPart(
|
||||
"nodes/screen/Standalone",
|
||||
position.x + HighlightSize,
|
||||
position.y + HighlightSize,
|
||||
SizeWithoutHighlight,
|
||||
SizeWithoutHighlight,
|
||||
position.x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize,
|
||||
)
|
||||
}
|
||||
// 1 x n
|
||||
@ -142,19 +142,19 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
// Top
|
||||
drawScreenPart(
|
||||
"nodes/screen/ColumnTop",
|
||||
position.x + HighlightSize,
|
||||
position.y + HighlightSize,
|
||||
SizeWithoutHighlight,
|
||||
Size - HighlightSize,
|
||||
position.x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
NoHighlightSize,
|
||||
Size - HighlightThickness,
|
||||
)
|
||||
|
||||
// Middle
|
||||
for (y <- 1 until aspectRatioHeight - 1) {
|
||||
drawScreenPart(
|
||||
"nodes/screen/ColumnMiddle",
|
||||
position.x + HighlightSize,
|
||||
position.x + HighlightThickness,
|
||||
position.y + y * Size,
|
||||
SizeWithoutHighlight,
|
||||
NoHighlightSize,
|
||||
Size
|
||||
)
|
||||
}
|
||||
@ -162,10 +162,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
// Bottom
|
||||
drawScreenPart(
|
||||
"nodes/screen/ColumnBottom",
|
||||
position.x + HighlightSize,
|
||||
position.x + HighlightThickness,
|
||||
position.y + (aspectRatioHeight - 1) * Size,
|
||||
SizeWithoutHighlight,
|
||||
SizeWithoutHighlight,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -176,10 +176,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
// Left
|
||||
drawScreenPart(
|
||||
"nodes/screen/RowLeft",
|
||||
position.x + HighlightSize,
|
||||
position.y + HighlightSize,
|
||||
Size - HighlightSize,
|
||||
SizeWithoutHighlight,
|
||||
position.x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
Size - HighlightThickness,
|
||||
NoHighlightSize,
|
||||
)
|
||||
|
||||
// Middle
|
||||
@ -187,18 +187,18 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
drawScreenPart(
|
||||
"nodes/screen/RowMiddle",
|
||||
position.x + x * Size,
|
||||
position.y + HighlightSize,
|
||||
position.y + HighlightThickness,
|
||||
Size,
|
||||
SizeWithoutHighlight
|
||||
NoHighlightSize
|
||||
)
|
||||
|
||||
// Right
|
||||
drawScreenPart(
|
||||
"nodes/screen/RowRight",
|
||||
position.x + (aspectRatioWidth - 1) * Size,
|
||||
position.y + HighlightSize,
|
||||
Size - HighlightSize,
|
||||
SizeWithoutHighlight
|
||||
position.y + HighlightThickness,
|
||||
Size - HighlightThickness,
|
||||
NoHighlightSize
|
||||
)
|
||||
}
|
||||
// n x n
|
||||
@ -207,7 +207,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
// Left
|
||||
drawScreenPart(
|
||||
leftName,
|
||||
position.x + HighlightSize,
|
||||
position.x + HighlightThickness,
|
||||
y,
|
||||
Size,
|
||||
height
|
||||
@ -226,7 +226,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
// Right
|
||||
drawScreenPart(
|
||||
rightName,
|
||||
position.x + (aspectRatioWidth - 1) * Size - HighlightSize,
|
||||
position.x + (aspectRatioWidth - 1) * Size - HighlightThickness,
|
||||
y,
|
||||
Size,
|
||||
height
|
||||
@ -235,8 +235,8 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
|
||||
// Top
|
||||
drawLine(
|
||||
position.y + HighlightSize,
|
||||
Size - HighlightSize,
|
||||
position.y + HighlightThickness,
|
||||
Size - HighlightThickness,
|
||||
"nodes/screen/TopLeft",
|
||||
"nodes/screen/TopMiddle",
|
||||
"nodes/screen/TopRight"
|
||||
@ -255,7 +255,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
// Bottom
|
||||
drawLine(
|
||||
position.y + (aspectRatioHeight - 1) * Size,
|
||||
Size - HighlightSize,
|
||||
Size - HighlightThickness,
|
||||
"nodes/screen/BottomLeft",
|
||||
"nodes/screen/BottomMiddle",
|
||||
"nodes/screen/BottomRight"
|
||||
@ -289,11 +289,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
screen.getHeight * FontHeight
|
||||
)
|
||||
|
||||
var scale = virtualScreenBounds.w / pixelDataSize.width
|
||||
val scaleY = virtualScreenBounds.h / pixelDataSize.height
|
||||
|
||||
if (scaleY < scale)
|
||||
scale = scaleY
|
||||
val scale = Math.min(
|
||||
virtualScreenBounds.w / pixelDataSize.width,
|
||||
virtualScreenBounds.h / pixelDataSize.height
|
||||
)
|
||||
|
||||
// Drawing pixel data
|
||||
drawScreenData(
|
||||
@ -308,10 +307,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
|
||||
else {
|
||||
g.sprite(
|
||||
"nodes/screen/PowerOnOverlay",
|
||||
position.x + HighlightSize,
|
||||
position.y + HighlightSize,
|
||||
SizeWithoutHighlight,
|
||||
SizeWithoutHighlight
|
||||
position.x + HighlightThickness,
|
||||
position.y + HighlightThickness,
|
||||
NoHighlightSize,
|
||||
NoHighlightSize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ object UiHandler extends Logging {
|
||||
def init(): Unit = {
|
||||
scalingFactor = Settings.get.scaleFactor
|
||||
fullScreen = Settings.get.windowFullscreen
|
||||
windowTitle = "Ocelot Desktop v" + BuildInfo.version
|
||||
windowTitle = "Ocelot Desktop"
|
||||
|
||||
loadIcons()
|
||||
|
||||
@ -375,13 +375,13 @@ object UiHandler extends Logging {
|
||||
hierarchy.reverseIterator.foreach(_.handleEvent(event))
|
||||
|
||||
for (event <- MouseEvents.events)
|
||||
hierarchy.reverseIterator.filter(_.receiveAllMouseEvents).foreach(_.handleEvent(event))
|
||||
hierarchy.reverseIterator.filter(w => w.enabled && w.receiveAllMouseEvents).foreach(_.handleEvent(event))
|
||||
|
||||
val scrollTarget = hierarchy.reverseIterator
|
||||
.find(w => w.receiveScrollEvents && w.clippedBounds.contains(mousePos))
|
||||
|
||||
val mouseTarget = hierarchy.reverseIterator
|
||||
.find(w => w.receiveMouseEvents && w.clippedBounds.contains(mousePos))
|
||||
.find(w => w.enabled && w.receiveMouseEvents && w.clippedBounds.contains(mousePos))
|
||||
|
||||
for (event <- ScrollEvents.events)
|
||||
scrollTarget.foreach(_.handleEvent(event))
|
||||
|
||||
11
src/main/scala/ocelot/desktop/ui/event/EventAware.scala
Normal file
@ -0,0 +1,11 @@
|
||||
package ocelot.desktop.ui.event
|
||||
|
||||
import ocelot.desktop.ui.widget.EventHandlers
|
||||
|
||||
trait EventAware {
|
||||
protected val eventHandlers = new EventHandlers
|
||||
|
||||
def handleEvent(event: Event): Unit = {
|
||||
eventHandlers(event)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package ocelot.desktop.ui.event.handlers
|
||||
|
||||
import ocelot.desktop.audio.{Audio, SoundBuffers, SoundCategory, SoundSource}
|
||||
import ocelot.desktop.ui.event.{BrainEvent, EventAware}
|
||||
import totoro.ocelot.brain.event.FileSystemActivityEvent
|
||||
import totoro.ocelot.brain.event.FileSystemActivityType.Floppy
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
trait DiskActivityHandler extends EventAware {
|
||||
eventHandlers += {
|
||||
case BrainEvent(event: FileSystemActivityEvent) if !Audio.isDisabled =>
|
||||
val sound =
|
||||
if (event.activityType == Floppy)
|
||||
SoundBuffers.MachineFloppyAccess
|
||||
.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
||||
else
|
||||
SoundBuffers.MachineHDDAccess
|
||||
.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
||||
|
||||
sound(Random.between(0, sound.length)).play()
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
package ocelot.desktop.ui.widget
|
||||
|
||||
import ocelot.desktop.ColorScheme
|
||||
import ocelot.desktop.audio.{SoundSource, SoundSources}
|
||||
import ocelot.desktop.audio.SoundSource
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.geometry.Size2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
@ -14,8 +14,6 @@ class Button extends Widget with ClickHandler with ClickSoundSource {
|
||||
|
||||
def onClick(): Unit = {}
|
||||
|
||||
def enabled: Boolean = true
|
||||
|
||||
override def receiveMouseEvents: Boolean = true
|
||||
|
||||
eventHandlers += {
|
||||
@ -49,5 +47,5 @@ class Button extends Widget with ClickHandler with ClickSoundSource {
|
||||
g.text(position.x + ((width - textWidth) / 2).round, position.y + 4, text)
|
||||
}
|
||||
|
||||
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClick
|
||||
override protected def clickSoundSource: SoundSource = SoundSource.InterfaceClick
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package ocelot.desktop.ui.widget
|
||||
|
||||
import ocelot.desktop.OcelotDesktop
|
||||
import ocelot.desktop.audio.{SoundSource, SoundSources}
|
||||
import ocelot.desktop.audio.SoundSource
|
||||
import ocelot.desktop.geometry.Padding2D
|
||||
import ocelot.desktop.ui.layout.LinearLayout
|
||||
import ocelot.desktop.ui.widget.ChangeSimulationSpeedDialog.validateIntervalUs
|
||||
@ -87,7 +87,7 @@ class ChangeSimulationSpeedDialog() extends ModalDialog {
|
||||
|
||||
children :+= new PaddingBox(new Button {
|
||||
override def text: String = "Cancel"
|
||||
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClickLow
|
||||
override protected def clickSoundSource: SoundSource = SoundSource.InterfaceClickLow
|
||||
override def onClick(): Unit = close()
|
||||
}, Padding2D(right = 8))
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package ocelot.desktop.ui.widget
|
||||
|
||||
import ocelot.desktop.audio.{SoundSource, SoundSources}
|
||||
import ocelot.desktop.audio.SoundSource
|
||||
import ocelot.desktop.geometry.Padding2D
|
||||
import ocelot.desktop.ui.layout.LinearLayout
|
||||
import ocelot.desktop.ui.widget.modal.ModalDialog
|
||||
@ -19,7 +19,7 @@ class CloseConfirmationDialog extends ModalDialog {
|
||||
children :+= new Widget {
|
||||
children :+= new PaddingBox(new Button {
|
||||
override def text: String = "Cancel"
|
||||
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClickLow
|
||||
override protected def clickSoundSource: SoundSource = SoundSource.InterfaceClickLow
|
||||
override def onClick(): Unit = close()
|
||||
}, Padding2D(left = 8))
|
||||
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
package ocelot.desktop.ui.widget
|
||||
|
||||
import ocelot.desktop.ColorScheme
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.geometry.Vector2D
|
||||
import ocelot.desktop.node.Node
|
||||
|
||||
class ComputerErrorMessageLabel(node: Node, override val text: String) extends Label {
|
||||
override def isSmall: Boolean = true
|
||||
|
||||
var alpha: Float = 1f
|
||||
|
||||
override def color: Color = ColorScheme("ErrorMessage").toRGBANorm.mapA(_ * alpha)
|
||||
|
||||
val initialPosition: Vector2D = {
|
||||
val position = node.position
|
||||
val size = node.size
|
||||
|
||||
position + Vector2D(size.width / 2 - minimumSize.width / 2, -minimumSize.height)
|
||||
}
|
||||
|
||||
position = initialPosition
|
||||
}
|
||||
@ -2,26 +2,14 @@ package ocelot.desktop.ui.widget
|
||||
|
||||
import ocelot.desktop.ColorScheme
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.geometry.Size2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.event.HoverEvent
|
||||
import ocelot.desktop.ui.event.handlers.HoverHandler
|
||||
import ocelot.desktop.ui.widget.tooltip.Tooltip
|
||||
|
||||
class Histogram extends Widget with HoverHandler {
|
||||
override def minimumSize: Size2D = Size2D(274, 70)
|
||||
override def maximumSize: Size2D = minimumSize
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
var text = "N/A"
|
||||
var history: Array[Float] = Seq.fill(21)(0.0f).toArray
|
||||
|
||||
override def receiveMouseEvents = true
|
||||
|
||||
eventHandlers += {
|
||||
case HoverEvent(state) =>
|
||||
if (state == HoverEvent.State.Enter) onHoverEnter()
|
||||
else onHoverLeave()
|
||||
}
|
||||
class Histogram extends Widget {
|
||||
var value = "N/A"
|
||||
var title = "Hello world"
|
||||
var history: ArrayBuffer[Float] = ArrayBuffer(50)
|
||||
|
||||
private def drawBars(g: Graphics): Unit = {
|
||||
def drawBarSegment(i: Int, color: Color): Unit = {
|
||||
@ -33,52 +21,90 @@ class Histogram extends Widget with HoverHandler {
|
||||
val fillBars = (ratio * 10).round
|
||||
val emptyBars = (9 - fillBars).max(0)
|
||||
|
||||
for (i <- 0 until emptyBars) {
|
||||
for (i <- 0 until emptyBars)
|
||||
drawBarSegment(i, ColorScheme("HistogramBarEmpty"))
|
||||
}
|
||||
for (i <- emptyBars + 1 until 10) {
|
||||
|
||||
for (i <- emptyBars + 1 until 10)
|
||||
drawBarSegment(i, ColorScheme("HistogramBarFill"))
|
||||
}
|
||||
|
||||
drawBarSegment(emptyBars, ColorScheme("HistogramBarTop"))
|
||||
|
||||
drawText(g, position.x + 17, value)
|
||||
}
|
||||
|
||||
private def drawText(g: Graphics): Unit = {
|
||||
private def drawText(g: Graphics, x: Float, text: String): Unit = {
|
||||
g.setSmallFont()
|
||||
g.text(position.x + 17 - text.length * 4, position.y + 62, text)
|
||||
g.text(x - text.length * 4, position.y + 62, text)
|
||||
g.setNormalFont()
|
||||
}
|
||||
|
||||
private def drawHistogram(g: Graphics): Unit = {
|
||||
for (i <- 0 until 22) {
|
||||
g.rect(position.x + 41 + i * 11, position.y, 2, 57, ColorScheme("HistogramGrid"))
|
||||
}
|
||||
for (i <- 0 until 6) {
|
||||
g.rect(position.x + 41, position.y + i * 11, 233, 2, ColorScheme("HistogramGrid"))
|
||||
val marginLeft = 41
|
||||
val marginBottom = 12
|
||||
var x = position.x + marginLeft
|
||||
|
||||
val cellThickness = 2f
|
||||
val cellSize = 11f
|
||||
val horizontalLineCount = ((height - cellThickness - marginBottom) / cellSize).toInt
|
||||
val verticalLineCount = ((width - cellThickness - marginLeft) / cellSize).toInt
|
||||
val gridWidth = verticalLineCount * cellSize
|
||||
val gridHeight = horizontalLineCount * cellSize
|
||||
|
||||
// Text
|
||||
drawText(g, x + gridWidth / 2, title)
|
||||
|
||||
// Horizontal lines
|
||||
for (i <- 0 until horizontalLineCount + 1)
|
||||
g.rect(x, position.y + i * cellSize, gridWidth, cellThickness, ColorScheme("HistogramGrid"))
|
||||
|
||||
// Additional line closes grid from right
|
||||
for (i <- 0 until verticalLineCount + 1) {
|
||||
val lesserThenPreLast = i < verticalLineCount - 1
|
||||
|
||||
// Vertical line
|
||||
g.rect(
|
||||
x,
|
||||
position.y,
|
||||
cellThickness,
|
||||
if (lesserThenPreLast) gridHeight else gridHeight + cellThickness,
|
||||
ColorScheme("HistogramGrid")
|
||||
)
|
||||
|
||||
// History
|
||||
val historyIndex = history.length - verticalLineCount + i - 1
|
||||
val historyValueWidth = if (i < verticalLineCount) cellSize else cellThickness
|
||||
var historyValueHeight: Float = cellThickness
|
||||
|
||||
// Value fill
|
||||
if (historyIndex > 0) {
|
||||
val historyValue = history(historyIndex)
|
||||
|
||||
historyValueHeight = (historyValue * (gridHeight + cellThickness)).max(cellThickness)
|
||||
|
||||
g.rect(
|
||||
x,
|
||||
position.y + gridHeight + cellThickness - historyValueHeight,
|
||||
historyValueWidth,
|
||||
historyValueHeight,
|
||||
ColorScheme("HistogramFill")
|
||||
)
|
||||
}
|
||||
|
||||
for ((entry, i) <- history.zipWithIndex) {
|
||||
val width = if (i == 20) 13 else 11
|
||||
val height = (entry * 57).max(2)
|
||||
g.rect(position.x + 41 + i * 11, position.y + 57 - height, width, 2, ColorScheme("HistogramEdge"))
|
||||
g.rect(position.x + 41 + i * 11, position.y + 59 - height, width, height - 2, ColorScheme("HistogramFill"))
|
||||
}
|
||||
}
|
||||
// Value line
|
||||
g.rect(
|
||||
x,
|
||||
position.y + gridHeight + cellThickness - historyValueHeight,
|
||||
historyValueWidth,
|
||||
cellThickness,
|
||||
ColorScheme("HistogramEdge")
|
||||
)
|
||||
|
||||
protected val tooltip: Option[Tooltip] = None
|
||||
|
||||
def onHoverEnter(): Unit = {
|
||||
if (tooltip.isDefined)
|
||||
root.get.tooltipPool.addTooltip(tooltip.get)
|
||||
x += cellSize
|
||||
}
|
||||
|
||||
def onHoverLeave(): Unit = {
|
||||
if (tooltip.isDefined)
|
||||
root.get.tooltipPool.closeTooltip(tooltip.get)
|
||||
}
|
||||
|
||||
override def draw(g: Graphics): Unit = {
|
||||
drawBars(g)
|
||||
drawText(g)
|
||||
drawHistogram(g)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package ocelot.desktop.ui.widget
|
||||
|
||||
import ocelot.desktop.ColorScheme
|
||||
import ocelot.desktop.audio.{SoundSource, SoundSources}
|
||||
import ocelot.desktop.audio.SoundSource
|
||||
import ocelot.desktop.color.Color
|
||||
import ocelot.desktop.geometry.Size2D
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
@ -143,7 +143,7 @@ class IconButton(
|
||||
alphaAnimation.update()
|
||||
}
|
||||
|
||||
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClick
|
||||
override protected def clickSoundSource: SoundSource = SoundSource.InterfaceClick
|
||||
}
|
||||
|
||||
object IconButton {
|
||||
|
||||