Added racks, servers & mountable disk drives

This commit is contained in:
Igor Timofeev 2023-06-28 14:58:06 +00:00
parent 109e3e0c72
commit b055436b43
136 changed files with 2133 additions and 987 deletions

@ -1 +1 @@
Subproject commit 40b9b506f9590a1a2add9bb1d47508a06f2b64cf Subproject commit 8222b1287c40965622fffcdeff4ae93f5d0418b5

View File

Before

Width:  |  Height:  |  Size: 602 B

After

Width:  |  Height:  |  Size: 602 B

View File

Before

Width:  |  Height:  |  Size: 150 B

After

Width:  |  Height:  |  Size: 150 B

View File

Before

Width:  |  Height:  |  Size: 634 B

After

Width:  |  Height:  |  Size: 634 B

View File

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 151 B

View File

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 151 B

View File

Before

Width:  |  Height:  |  Size: 150 B

After

Width:  |  Height:  |  Size: 150 B

View File

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 528 B

View File

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -2,8 +2,8 @@ PortDown = #8382d8
PortUp = #75bdc1 PortUp = #75bdc1
PortNorth = #c8ca5f PortNorth = #c8ca5f
PortSouth = #990da3 PortSouth = #990da3
PortEast = #db7d75 PortEast = #7ec95f
PortWest = #7ec95f PortWest = #db7d75
PortAny = #9b9b9b PortAny = #9b9b9b
Tier0 = #ababab Tier0 = #ababab

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -1,14 +1,13 @@
BackgroundPattern 0 0 304 304 BackgroundPattern 0 0 304 304
BarSegment 53 472 16 4 BarSegment 69 479 16 4
ComputerMotherboard 305 129 79 70
Empty 426 305 16 16 Empty 426 305 16 16
EmptySlot 404 330 18 18 EmptySlot 404 330 18 18
Knob 385 129 50 50 Knob 459 208 50 50
KnobCenter 436 129 50 50 KnobCenter 406 129 50 50
KnobLimits 305 200 50 50 KnobLimits 457 129 50 50
ShadowBorder 505 0 1 24 ShadowBorder 505 0 1 24
ShadowCorner 301 305 24 24 ShadowCorner 301 305 24 24
TabArrow 0 472 8 14 TabArrow 16 479 8 14
blocks/Generic 443 305 16 16 blocks/Generic 443 305 16 16
blocks/HologramEffect 507 18 4 4 blocks/HologramEffect 507 18 4 4
blocks/HologramProjector1Top 460 305 16 16 blocks/HologramProjector1Top 460 305 16 16
@ -17,174 +16,205 @@ blocks/HologramProjectorSide 494 305 16 16
buttons/BottomDrawerClose 423 330 18 18 buttons/BottomDrawerClose 423 330 18 18
buttons/BottomDrawerOpen 442 330 18 18 buttons/BottomDrawerOpen 442 330 18 18
buttons/OpenFMRadioCloseOff 502 117 7 8 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/OpenFMRadioRedstoneOff 502 72 8 8
buttons/OpenFMRadioRedstoneOn 502 81 8 8 buttons/OpenFMRadioRedstoneOn 502 81 8 8
buttons/OpenFMRadioStartOff 326 305 24 24 buttons/OpenFMRadioStartOff 326 305 24 24
buttons/OpenFMRadioStartOn 351 305 24 24 buttons/OpenFMRadioStartOn 351 305 24 24
buttons/OpenFMRadioStopOff 376 305 24 24 buttons/OpenFMRadioStopOff 376 305 24 24
buttons/OpenFMRadioStopOn 401 305 24 24 buttons/OpenFMRadioStopOn 401 305 24 24
buttons/OpenFMRadioVolumeDownOff 9 472 10 10 buttons/OpenFMRadioVolumeDownOff 25 479 10 10
buttons/OpenFMRadioVolumeDownOn 20 472 10 10 buttons/OpenFMRadioVolumeDownOn 36 479 10 10
buttons/OpenFMRadioVolumeUpOff 31 472 10 10 buttons/OpenFMRadioVolumeUpOff 47 479 10 10
buttons/OpenFMRadioVolumeUpOn 42 472 10 10 buttons/OpenFMRadioVolumeUpOn 58 479 10 10
buttons/PowerOff 461 330 18 18 buttons/PowerOff 461 330 18 18
buttons/PowerOn 480 330 18 18 buttons/PowerOn 480 330 18 18
icons/ButtonCheck 301 378 17 17 icons/ButtonCheck 301 378 17 17
icons/ButtonClipboard 319 378 17 17 icons/ButtonClipboard 319 378 17 17
icons/ButtonRandomize 337 378 17 17 icons/ButtonRandomize 337 378 17 17
icons/CPU 356 200 16 16 icons/CPU 406 180 16 16
icons/Card 373 200 16 16 icons/Card 423 180 16 16
icons/Close 0 439 15 14 icons/Close 0 479 15 14
icons/ComponentBus 390 200 16 16 icons/ComponentBus 440 180 16 16
icons/DragLMB 301 363 21 14 icons/DragLMB 301 363 21 14
icons/DragRMB 323 363 21 14 icons/DragRMB 323 363 21 14
icons/EEPROM 407 200 16 16 icons/EEPROM 457 180 16 16
icons/Floppy 424 200 16 16 icons/Floppy 474 180 16 16
icons/Grid 335 330 22 22 icons/Grid 335 330 22 22
icons/GridOff 358 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/Home 381 330 22 22
icons/LMB 71 454 11 14 icons/LMB 71 494 11 14
icons/Memory 458 200 16 16 icons/Memory 459 259 16 16
icons/NA 475 200 16 16 icons/NA 476 259 16 16
icons/NotificationError 95 454 11 11 icons/NotificationError 95 494 11 11
icons/NotificationInfo 107 454 11 11 icons/NotificationInfo 107 494 11 11
icons/NotificationWarning 119 454 11 11 icons/NotificationWarning 119 494 11 11
icons/Pin 26 454 14 14 icons/Pin 26 494 14 14
icons/RMB 83 454 11 14 icons/RMB 83 494 11 14
icons/RackMountable 492 200 16 16 icons/Server 493 259 16 16
icons/SettingsSound 0 454 12 17 icons/SettingsSound 0 494 12 17
icons/SettingsUI 13 454 12 17 icons/SettingsUI 13 494 12 17
icons/Tier0 356 217 16 16 icons/Tier0 379 279 16 16
icons/Tier1 373 217 16 16 icons/Tier1 396 279 16 16
icons/Tier2 390 217 16 16 icons/Tier2 413 279 16 16
icons/Unpin 41 454 14 14 icons/Unpin 41 494 14 14
icons/WaveLFSR 393 411 24 10 icons/WaveLFSR 359 462 24 10
icons/WaveNoise 418 411 24 10 icons/WaveNoise 384 462 24 10
icons/WaveSawtooth 443 411 24 10 icons/WaveSawtooth 409 462 24 10
icons/WaveSine 468 411 24 10 icons/WaveSine 434 462 24 10
icons/WaveSquare 0 428 24 10 icons/WaveSquare 459 462 24 10
icons/WaveTriangle 25 428 24 10 icons/WaveTriangle 484 462 24 10
icons/WireArrowLeft 507 0 4 8 icons/WireArrowLeft 507 0 4 8
icons/WireArrowRight 507 9 4 8 icons/WireArrowRight 507 9 4 8
items/APU0 233 305 16 96 items/APU0 233 305 16 96
items/APU1 250 305 16 96 items/APU1 250 305 16 96
items/APU2 267 305 16 96 items/APU2 267 305 16 96
items/CPU0 407 217 16 16 items/CPU0 430 279 16 16
items/CPU1 424 217 16 16 items/CPU1 447 279 16 16
items/CPU2 441 217 16 16 items/CPU2 464 279 16 16
items/CardBase 458 217 16 16 items/CardBase 481 279 16 16
items/CircuitBoard 475 217 16 16 items/CircuitBoard 0 411 16 16
items/ComponentBus0 492 217 16 16 items/ComponentBus0 17 411 16 16
items/ComponentBus1 356 234 16 16 items/ComponentBus1 34 411 16 16
items/ComponentBus2 373 234 16 16 items/ComponentBus2 51 411 16 16
items/ComponentBus3 390 234 16 16 items/ComponentBus3 68 411 16 16
items/DataCard0 434 0 16 128 items/DataCard0 434 0 16 128
items/DataCard1 451 0 16 128 items/DataCard1 451 0 16 128
items/DataCard2 468 0 16 128 items/DataCard2 468 0 16 128
items/DebugCard 407 234 16 16 items/DebugCard 85 411 16 16
items/DiskDriveMountable 424 234 16 16 items/DiskDriveMountable 102 411 16 16
items/EEPROM 441 234 16 16 items/EEPROM 119 411 16 16
items/FloppyDisk_dyeBlack 458 234 16 16 items/FloppyDisk_dyeBlack 136 411 16 16
items/FloppyDisk_dyeBlue 475 234 16 16 items/FloppyDisk_dyeBlue 153 411 16 16
items/FloppyDisk_dyeBrown 492 234 16 16 items/FloppyDisk_dyeBrown 170 411 16 16
items/FloppyDisk_dyeCyan 305 251 16 16 items/FloppyDisk_dyeCyan 187 411 16 16
items/FloppyDisk_dyeGray 322 251 16 16 items/FloppyDisk_dyeGray 204 411 16 16
items/FloppyDisk_dyeGreen 339 251 16 16 items/FloppyDisk_dyeGreen 221 411 16 16
items/FloppyDisk_dyeLightBlue 356 251 16 16 items/FloppyDisk_dyeLightBlue 238 411 16 16
items/FloppyDisk_dyeLightGray 373 251 16 16 items/FloppyDisk_dyeLightGray 255 411 16 16
items/FloppyDisk_dyeLime 390 251 16 16 items/FloppyDisk_dyeLime 272 411 16 16
items/FloppyDisk_dyeMagenta 407 251 16 16 items/FloppyDisk_dyeMagenta 289 411 16 16
items/FloppyDisk_dyeOrange 424 251 16 16 items/FloppyDisk_dyeOrange 306 411 16 16
items/FloppyDisk_dyePink 441 251 16 16 items/FloppyDisk_dyePink 323 411 16 16
items/FloppyDisk_dyePurple 458 251 16 16 items/FloppyDisk_dyePurple 340 411 16 16
items/FloppyDisk_dyeRed 475 251 16 16 items/FloppyDisk_dyeRed 357 411 16 16
items/FloppyDisk_dyeWhite 492 251 16 16 items/FloppyDisk_dyeWhite 374 411 16 16
items/FloppyDisk_dyeYellow 305 268 16 16 items/FloppyDisk_dyeYellow 391 411 16 16
items/GraphicsCard0 322 268 16 16 items/GraphicsCard0 408 411 16 16
items/GraphicsCard1 339 268 16 16 items/GraphicsCard1 425 411 16 16
items/GraphicsCard2 356 268 16 16 items/GraphicsCard2 442 411 16 16
items/HardDiskDrive0 373 268 16 16 items/HardDiskDrive0 459 411 16 16
items/HardDiskDrive1 390 268 16 16 items/HardDiskDrive1 476 411 16 16
items/HardDiskDrive2 407 268 16 16 items/HardDiskDrive2 493 411 16 16
items/InternetCard 301 330 16 32 items/InternetCard 301 330 16 32
items/LinkedCard 284 305 16 96 items/LinkedCard 284 305 16 96
items/Memory0 424 268 16 16 items/Memory0 0 428 16 16
items/Memory1 441 268 16 16 items/Memory1 17 428 16 16
items/Memory2 458 268 16 16 items/Memory2 34 428 16 16
items/Memory3 475 268 16 16 items/Memory3 51 428 16 16
items/Memory4 492 268 16 16 items/Memory4 68 428 16 16
items/Memory5 305 285 16 16 items/Memory5 85 428 16 16
items/NetworkCard 322 285 16 16 items/NetworkCard 102 428 16 16
items/RedstoneCard0 339 285 16 16 items/RedstoneCard0 119 428 16 16
items/RedstoneCard1 356 285 16 16 items/RedstoneCard1 136 428 16 16
items/SelfDestructingCard 318 330 16 32 items/SelfDestructingCard 318 330 16 32
items/Server0 373 285 16 16 items/Server0 153 428 16 16
items/Server1 390 285 16 16 items/Server1 170 428 16 16
items/Server2 407 285 16 16 items/Server2 187 428 16 16
items/Server3 424 285 16 16 items/Server3 204 428 16 16
items/SoundCard 485 0 16 128 items/SoundCard 485 0 16 128
items/WirelessNetworkCard0 441 285 16 16 items/WirelessNetworkCard0 221 428 16 16
items/WirelessNetworkCard1 458 285 16 16 items/WirelessNetworkCard1 238 428 16 16
light-panel/BookmarkLeft 374 411 18 14 light-panel/BookmarkLeft 340 462 18 14
light-panel/BookmarkRight 355 378 20 14 light-panel/BookmarkRight 355 378 20 14
light-panel/BorderB 8 487 4 4 light-panel/BorderB 139 494 4 4
light-panel/BorderL 98 487 4 2 light-panel/BorderL 229 494 4 2
light-panel/BorderR 13 487 4 4 light-panel/BorderR 144 494 4 4
light-panel/BorderT 18 487 4 4 light-panel/BorderT 149 494 4 4
light-panel/CornerBL 23 487 4 4 light-panel/CornerBL 154 494 4 4
light-panel/CornerBR 28 487 4 4 light-panel/CornerBR 159 494 4 4
light-panel/CornerTL 33 487 4 4 light-panel/CornerTL 164 494 4 4
light-panel/CornerTR 38 487 4 4 light-panel/CornerTR 169 494 4 4
light-panel/Fill 4 496 2 2 light-panel/Fill 88 479 2 2
light-panel/Vent 502 0 2 38 light-panel/Vent 502 0 2 38
nodes/Cable 502 90 8 8 nodes/Cable 502 90 8 8
nodes/Camera 475 285 16 16 nodes/Camera 255 428 16 16
nodes/Chest 56 454 14 14 nodes/Chest 56 494 14 14
nodes/Computer 492 285 16 16 nodes/HologramProjector0 272 428 16 16
nodes/ComputerActivityOverlay 487 129 16 16 nodes/HologramProjector1 289 428 16 16
nodes/ComputerErrorOverlay 487 146 16 16 nodes/IronNoteBlock 306 428 16 16
nodes/ComputerOnOverlay 487 163 16 16 nodes/Lamp 323 428 16 16
nodes/DiskDrive 385 180 16 16 nodes/LampFrame 340 428 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/LampGlow 305 0 128 128 nodes/LampGlow 305 0 128 128
nodes/NewNode 17 411 16 16 nodes/NewNode 357 428 16 16
nodes/NoteBlock 34 411 16 16 nodes/NoteBlock 374 428 16 16
nodes/OpenFMRadio 51 411 16 16 nodes/OpenFMRadio 391 428 16 16
nodes/Relay 68 411 16 16 nodes/Relay 408 428 16 16
nodes/screen/BottomLeft 85 411 16 16 nodes/computer/Activity 425 428 16 16
nodes/screen/BottomMiddle 102 411 16 16 nodes/computer/Default 442 428 16 16
nodes/screen/BottomRight 119 411 16 16 nodes/computer/Error 459 428 16 16
nodes/screen/ColumnBottom 136 411 16 16 nodes/computer/On 476 428 16 16
nodes/screen/ColumnMiddle 153 411 16 16 nodes/disk-drive/Activity 493 428 16 16
nodes/screen/ColumnTop 170 411 16 16 nodes/disk-drive/Default 0 445 16 16
nodes/screen/Middle 187 411 16 16 nodes/disk-drive/Floppy 17 445 16 16
nodes/screen/MiddleLeft 204 411 16 16 nodes/rack/Default 34 445 16 16
nodes/screen/MiddleRight 221 411 16 16 nodes/rack/Empty 51 445 16 16
nodes/screen/PowerOnOverlay 238 411 16 16 nodes/rack/drive/0/Activity 68 445 16 16
nodes/screen/RowLeft 255 411 16 16 nodes/rack/drive/0/Default 85 445 16 16
nodes/screen/RowMiddle 272 411 16 16 nodes/rack/drive/0/Floppy 102 445 16 16
nodes/screen/RowRight 289 411 16 16 nodes/rack/drive/1/Activity 119 445 16 16
nodes/screen/Standalone 306 411 16 16 nodes/rack/drive/1/Default 136 445 16 16
nodes/screen/TopLeft 323 411 16 16 nodes/rack/drive/1/Floppy 153 445 16 16
nodes/screen/TopMiddle 340 411 16 16 nodes/rack/drive/2/Activity 170 445 16 16
nodes/screen/TopRight 357 411 16 16 nodes/rack/drive/2/Default 187 445 16 16
panel/BorderB 43 487 4 4 nodes/rack/drive/2/Floppy 204 445 16 16
panel/BorderL 103 487 4 2 nodes/rack/drive/3/Activity 221 445 16 16
panel/BorderR 48 487 4 4 nodes/rack/drive/3/Default 238 445 16 16
panel/BorderT 53 487 4 4 nodes/rack/drive/3/Floppy 255 445 16 16
panel/CornerBL 58 487 4 4 nodes/rack/drive/Floppy 272 445 16 16
panel/CornerBR 63 487 4 4 nodes/rack/server/0/Activity 289 445 16 16
panel/CornerTL 68 487 4 4 nodes/rack/server/0/Default 306 445 16 16
panel/CornerTR 73 487 4 4 nodes/rack/server/0/Error 323 445 16 16
panel/Fill 7 496 2 2 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 particles/Note 502 61 7 10
screen/BorderB 508 25 2 8 screen/BorderB 508 25 2 8
screen/BorderT 505 25 2 10 screen/BorderT 505 25 2 10
@ -192,10 +222,30 @@ screen/CornerBL 502 99 8 8
screen/CornerBR 502 108 8 8 screen/CornerBR 502 108 8 8
screen/CornerTL 502 39 8 10 screen/CornerTL 502 39 8 10
screen/CornerTR 502 50 8 10 screen/CornerTR 502 50 8 10
window/BorderDark 0 496 1 4 window/BorderDark 510 117 1 4
window/BorderLight 2 496 1 4 window/BorderLight 86 479 1 4
window/CornerBL 78 487 4 4 window/CornerBL 209 494 4 4
window/CornerBR 83 487 4 4 window/CornerBR 214 494 4 4
window/CornerTL 88 487 4 4 window/CornerTL 219 494 4 4
window/CornerTR 93 487 4 4 window/CornerTR 224 494 4 4
window/OpenFMRadio 0 305 232 105 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

View File

@ -56,6 +56,16 @@ class SoundSource(val kind: SoundSource.Kind,
} }
object SoundSource { 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 sealed trait Kind
case class KindSoundBuffer(buffer: SoundBuffer) extends Kind case class KindSoundBuffer(buffer: SoundBuffer) extends Kind

View File

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

View File

@ -4,7 +4,7 @@ import ocelot.desktop.util.Persistable
import totoro.ocelot.brain.nbt.NBTTagCompound import totoro.ocelot.brain.nbt.NBTTagCompound
object Vector2D { 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) def apply(x: Double, y: Double): Vector2D = Vector2D(x.toFloat, y.toFloat)
} }

View File

@ -14,6 +14,8 @@ object Icons {
val EepromIcon: IconDef = IconDef("icons/EEPROM") val EepromIcon: IconDef = IconDef("icons/EEPROM")
val FloppyIcon: IconDef = IconDef("icons/Floppy") val FloppyIcon: IconDef = IconDef("icons/Floppy")
val MemoryIcon: IconDef = IconDef("icons/Memory") val MemoryIcon: IconDef = IconDef("icons/Memory")
val ServerIcon: IconDef = IconDef("icons/Server")
val ComponentBusIcon: IconDef = IconDef("icons/ComponentBus")
val TierIcon: Tier => IconDef = { tier => val TierIcon: Tier => IconDef = { tier =>
IconDef(s"icons/Tier${tier.id}") IconDef(s"icons/Tier${tier.id}")
@ -75,6 +77,16 @@ object Icons {
IconDef(s"items/Memory${tier.id}") 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 //noinspection ScalaWeakerAccess
object Animations { object Animations {
val Apu: Animation = Array( val Apu: Animation = Array(

View File

@ -3,15 +3,18 @@ package ocelot.desktop.inventory
import ocelot.desktop.ColorScheme import ocelot.desktop.ColorScheme
import ocelot.desktop.color.Color import ocelot.desktop.color.Color
import ocelot.desktop.graphics.IconDef 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.contextmenu.ContextMenu
import ocelot.desktop.ui.widget.tooltip.ItemTooltip import ocelot.desktop.ui.widget.tooltip.ItemTooltip
import ocelot.desktop.util.Disposable
import totoro.ocelot.brain.util.Tier import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
/** /**
* Something that can be stored in an [[Inventory]]. * Something that can be stored in an [[Inventory]].
*/ */
trait Item { trait Item extends EventAware with Updatable with Disposable {
private var _slot: Option[Inventory#Slot] = None private var _slot: Option[Inventory#Slot] = None
/** /**
@ -24,7 +27,8 @@ trait Item {
if (slot.nonEmpty) { if (slot.nonEmpty) {
onInserted() onInserted()
} else { }
else {
onRemoved() onRemoved()
} }
} }
@ -102,6 +106,12 @@ trait Item {
* Keep this rather cheap. * Keep this rather cheap.
*/ */
def factory: ItemFactory def factory: ItemFactory
/**
* Override this in subclasses to implement some specific item behavior
* during UI update
*/
def update(): Unit = {}
} }
object Item { object Item {

View File

@ -139,4 +139,8 @@ object Items extends Logging {
} }
registerSingleton(SoundCardItem.Factory) registerSingleton(SoundCardItem.Factory)
registerSingleton(SelfDestructingCardItem.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)
} }

View File

@ -3,8 +3,8 @@ package ocelot.desktop.inventory
import ocelot.desktop.OcelotDesktop import ocelot.desktop.OcelotDesktop
import ocelot.desktop.inventory.PersistedInventory._ import ocelot.desktop.inventory.PersistedInventory._
import ocelot.desktop.inventory.traits.{ComponentItem, PersistableItem} import ocelot.desktop.inventory.traits.{ComponentItem, PersistableItem}
import ocelot.desktop.util.{Logging, Persistable}
import ocelot.desktop.util.ReflectionUtils.findUnaryConstructor 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.ExtendedNBT.{extendNBTTagCompound, extendNBTTagList}
import totoro.ocelot.brain.nbt.persistence.NBTPersistence import totoro.ocelot.brain.nbt.persistence.NBTPersistence
import totoro.ocelot.brain.nbt.{NBT, NBTTagCompound} import totoro.ocelot.brain.nbt.{NBT, NBTTagCompound}

View File

@ -4,7 +4,7 @@ import ocelot.desktop.OcelotDesktop
import ocelot.desktop.inventory.PersistedInventory.ItemLoadException import ocelot.desktop.inventory.PersistedInventory.ItemLoadException
import ocelot.desktop.inventory.SyncedInventory.SlotStatus.SlotStatus import ocelot.desktop.inventory.SyncedInventory.SlotStatus.SlotStatus
import ocelot.desktop.inventory.SyncedInventory.SyncDirection.SyncDirection 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.inventory.traits.ComponentItem
import ocelot.desktop.ui.event.BrainEvent import ocelot.desktop.ui.event.BrainEvent
import ocelot.desktop.ui.widget.EventHandlers import ocelot.desktop.ui.widget.EventHandlers
@ -54,12 +54,7 @@ trait SyncedInventory extends PersistedInventory with Logging {
isLoading = false isLoading = false
} }
val occupiedSlots = inventoryIterator.map(_.index).toSet syncSlots()
.union(brainInventory.inventory.iterator.map(_.index).toSet)
for (slotIndex <- occupiedSlots) {
sync(slotIndex, SyncDirection.Reconcile)
}
} }
@throws[ItemLoadException]("if the item could not be loaded") @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 = { private def sync(slotIndex: Int, direction: SyncDirection): Unit = {
val initialSync = syncFuel == InitialSyncFuel val initialSync = syncFuel == InitialSyncFuel
try { try {
doSync(slotIndex, direction) doSync(slotIndex, direction)
} finally {
if (initialSync) {
refuel()
} }
finally {
if (initialSync)
refuel()
} }
} }
@ -138,7 +141,8 @@ trait SyncedInventory extends PersistedInventory with Logging {
if (syncFuel < 0) { if (syncFuel < 0) {
// ignore: the limit has already been reached // ignore: the limit has already been reached
} else if (syncFuel == 0) { }
else if (syncFuel == 0) {
logger.error( logger.error(
s"Got trapped in an infinite loop while trying to synchronize the slot $slotIndex " + s"Got trapped in an infinite loop while trying to synchronize the slot $slotIndex " +
s"in $this (class ${this.getClass.getName})!", 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.") logger.error("Breaking the loop forcefully by removing the items.")
Slot(slotIndex).remove() Slot(slotIndex).remove()
brainInventory.inventory(slotIndex).remove() brainInventory.inventory(slotIndex).remove()
} else { }
else {
direction match { direction match {
case _ if checkSlotStatus(slotIndex) == SlotStatus.Synchronized => case _ if checkSlotStatus(slotIndex) == SlotStatus.Synchronized =>

View File

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

View File

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

View File

@ -4,12 +4,12 @@ import ocelot.desktop.graphics.{IconDef, Icons}
import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem} import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem}
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer} import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
import totoro.ocelot.brain.entity.NetworkCard 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
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
class NetworkCardItem(val networkCard: NetworkCard) extends Item with ComponentItem with PersistableItem with CardItem { class NetworkCardItem(val card: NetworkCard) extends Item with ComponentItem with PersistableItem with CardItem {
override def component: Entity with Environment = networkCard override def component: Entity with NetworkCard = card
override def factory: ItemFactory = NetworkCardItem.Factory override def factory: ItemFactory = NetworkCardItem.Factory
} }

View File

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

View 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(_)))
}
}

View File

@ -1,20 +1,13 @@
package ocelot.desktop.inventory.item package ocelot.desktop.inventory.item
import ocelot.desktop.graphics.{IconDef, Icons} import ocelot.desktop.graphics.{IconDef, Icons}
import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem} import ocelot.desktop.inventory.{ItemFactory, ItemRecoverer}
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
import totoro.ocelot.brain.entity.WirelessNetworkCard 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
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
abstract class WirelessNetworkCardItem(val card: WirelessNetworkCard) abstract class WirelessNetworkCardItem(override val card: WirelessNetworkCard)
extends Item extends NetworkCardItem(card) {
with ComponentItem
with PersistableItem
with CardItem {
override def component: Entity with Environment = card
} }
object WirelessNetworkCardItem { object WirelessNetworkCardItem {

View File

@ -1,6 +1,5 @@
package ocelot.desktop.inventory.traits package ocelot.desktop.inventory.traits
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
import ocelot.desktop.ui.widget.window.Window import ocelot.desktop.ui.widget.window.Window

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

View File

@ -2,6 +2,7 @@ package ocelot.desktop.node
import ocelot.desktop.OcelotDesktop import ocelot.desktop.OcelotDesktop
import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.event.ClickEvent
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
import totoro.ocelot.brain.entity.traits.{Entity, Environment, SidedEnvironment} import totoro.ocelot.brain.entity.traits.{Entity, Environment, SidedEnvironment}
import totoro.ocelot.brain.nbt.NBTTagCompound 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) { if (exposeAddress && entity.node != null && entity.node.address != null) {
menu.addEntry( menu.addEntry(
ContextMenuEntry("Copy address") { 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 ports: Array[NodePort] = Array(NodePort())
override def getNodeByPort(port: NodePort): network.Node = entity.node 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 = { override def dispose(): Unit = {
super.dispose() super.dispose()

View File

@ -2,6 +2,7 @@ package ocelot.desktop.node
import ocelot.desktop.color.RGBAColor import ocelot.desktop.color.RGBAColor
import ocelot.desktop.graphics.Graphics import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.event.ClickEvent
import ocelot.desktop.ui.widget.InputDialog import ocelot.desktop.ui.widget.InputDialog
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
import ocelot.desktop.util.DrawUtils import ocelot.desktop.util.DrawUtils
@ -24,9 +25,8 @@ trait LabeledNode extends Node {
nbt.setString("label", _label.getOrElse("")) nbt.setString("label", _label.getOrElse(""))
} }
override def setupContextMenu(menu: ContextMenu): Unit = { override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
menu.addEntry( menu.addEntry(ContextMenuEntry("Set label") {
ContextMenuEntry("Set label") {
new InputDialog( new InputDialog(
"Set label", "Set label",
text => { text => {
@ -34,10 +34,9 @@ trait LabeledNode extends Node {
}, },
_label.getOrElse(""), _label.getOrElse(""),
).show() ).show()
} })
)
super.setupContextMenu(menu) super.setupContextMenu(menu, event)
} }
override def drawLabel(g: Graphics): Unit = { override def drawLabel(g: Graphics): Unit = {

View File

@ -3,13 +3,13 @@ package ocelot.desktop.node
import ocelot.desktop.color.{Color, RGBAColor} import ocelot.desktop.color.{Color, RGBAColor}
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D} import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
import ocelot.desktop.graphics.Graphics 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.handlers.{ClickHandler, DragHandler, HoverHandler}
import ocelot.desktop.ui.event.{ClickEvent, DragEvent, HoverEvent, MouseEvent} import ocelot.desktop.ui.event.{ClickEvent, DragEvent, HoverEvent, MouseEvent}
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} 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.ui.widget.{Widget, WorkspaceView}
import ocelot.desktop.util.Persistable import ocelot.desktop.util.Disposable
import ocelot.desktop.util.animation.ColorAnimation import ocelot.desktop.util.animation.ColorAnimation
import org.lwjgl.input.Keyboard import org.lwjgl.input.Keyboard
import totoro.ocelot.brain.nbt.NBTTagCompound import totoro.ocelot.brain.nbt.NBTTagCompound
@ -17,7 +17,7 @@ import totoro.ocelot.brain.network
import scala.collection.mutable.ArrayBuffer 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 = _ var workspaceView: WorkspaceView = _
protected val highlight = new ColorAnimation(RGBAColor(0, 0, 0, 0)) 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 size = minimumSize
override def load(nbt: NBTTagCompound): Unit = { override def load(nbt: NBTTagCompound): Unit = {
super.load(nbt)
position = new Vector2D(nbt.getCompoundTag("pos")) position = new Vector2D(nbt.getCompoundTag("pos"))
window.foreach(window => {
val tag = nbt.getCompoundTag("window")
window.load(tag)
})
} }
override def save(nbt: NBTTagCompound): Unit = { override def save(nbt: NBTTagCompound): Unit = {
val posTag = new NBTTagCompound super.save(nbt)
position.save(posTag)
nbt.setTag("pos", posTag)
window.foreach(window => {
val tag = new NBTTagCompound val tag = new NBTTagCompound
window.save(tag) position.save(tag)
nbt.setTag("window", tag) nbt.setTag("pos", tag)
})
} }
override def receiveMouseEvents = true override def receiveMouseEvents = true
@ -92,7 +86,7 @@ abstract class Node extends Widget with DragHandler with ClickHandler with Hover
highlight.goto(NoHighlight) highlight.goto(NoHighlight)
} }
def setupContextMenu(menu: ContextMenu): Unit = { def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
if (ports.nonEmpty) { if (ports.nonEmpty) {
menu.addEntry( menu.addEntry(
ContextMenuEntry("Disconnect") { ContextMenuEntry("Disconnect") {
@ -182,9 +176,10 @@ abstract class Node extends Widget with DragHandler with ClickHandler with Hover
window.foreach(_.open()) window.foreach(_.open())
else else
window.foreach(_.close()) window.foreach(_.close())
case ClickEvent(MouseEvent.Button.Right, _) => case ClickEvent(MouseEvent.Button.Right, _) =>
val menu = new ContextMenu val menu = new ContextMenu
setupContextMenu(menu) setupContextMenu(menu, event)
root.get.contextMenus.open(menu) root.get.contextMenus.open(menu)
case _ => case _ =>
} }
@ -272,10 +267,10 @@ abstract class Node extends Widget with DragHandler with ClickHandler with Hover
g.sprite( g.sprite(
icon, icon,
position. x + HighlightSize, position. x + HighlightThickness,
position.y + HighlightSize, position.y + HighlightThickness,
size.width - HighlightSize * 2, size.width - HighlightThickness * 2,
size.height - HighlightSize * 2, size.height - HighlightThickness * 2,
iconColor 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() disconnectFromAll()
window.foreach(_.closeAndDispose()) window.foreach(_.closeAndDispose())
} }
@ -307,7 +302,10 @@ object Node {
protected val HoveredHighlight: RGBAColor = RGBAColor(160, 160, 160) protected val HoveredHighlight: RGBAColor = RGBAColor(160, 160, 160)
protected val NoHighlight: RGBAColor = RGBAColor(160, 160, 160, 0) protected val NoHighlight: RGBAColor = RGBAColor(160, 160, 160, 0)
val Size = 68f val TexelCount = 16f
val HighlightSize = 2f val Scale = 4f
val SizeWithoutHighlight: Float = Size - HighlightSize * 2 val HighlightThickness = 2f
val NoHighlightSize: Float = TexelCount * Scale
val Size: Float = NoHighlightSize + HighlightThickness * 2f
} }

View File

@ -12,7 +12,7 @@ case class NodePort(direction: Option[Direction.Value] = None) extends Ordered[N
case Some(Direction.South) => ColorScheme("PortSouth") case Some(Direction.South) => ColorScheme("PortSouth")
case Some(Direction.East) => ColorScheme("PortEast") case Some(Direction.East) => ColorScheme("PortEast")
case Some(Direction.West) => ColorScheme("PortWest") case Some(Direction.West) => ColorScheme("PortWest")
case None => ColorScheme("PortAny") case _ => ColorScheme("PortAny")
} }
override def compare(that: NodePort): Int = this.direction.compare(that.direction) override def compare(that: NodePort): Int = this.direction.compare(that.direction)

View File

@ -2,7 +2,7 @@ package ocelot.desktop.node
import ocelot.desktop.entity.{Camera, OpenFMRadio} import ocelot.desktop.entity.{Camera, OpenFMRadio}
import ocelot.desktop.node.nodes._ 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 totoro.ocelot.brain.util.Tier
import scala.collection.mutable import scala.collection.mutable
@ -27,20 +27,30 @@ object NodeRegistry {
for (tier <- Tier.One to Tier.Three) { for (tier <- Tier.One to Tier.Three) {
addType(NodeType(s"Screen (${tier.label})", "nodes/screen/Standalone", tier) { 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) { addType(NodeType("Disk Drive", "nodes/disk-drive/Default", None) {
new DiskDriveNode(new FloppyDiskDrive(), initDisk = true) val node = new DiskDriveNode(new FloppyDiskDrive())
node.fillSlotsWithDefaultItems()
node
}) })
for (tier <- Tier.One to Tier.Creative) { for (tier <- Tier.One to Tier.Creative) {
addType(NodeType(s"Computer Case (${tier.label})", "nodes/Computer", tier) { addType(NodeType(s"Computer Case (${tier.label})", "nodes/computer/Default", tier) {
new ComputerNode(new Case(tier)).setup() 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) { addType(NodeType("Relay", "nodes/Relay", None) {
new RelayNode(new Relay) new RelayNode(new Relay)
}) })

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

View File

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

View File

@ -1,396 +1,121 @@
package ocelot.desktop.node.nodes package ocelot.desktop.node.nodes
import ocelot.desktop.ColorScheme
import ocelot.desktop.audio._
import ocelot.desktop.color.Color import ocelot.desktop.color.Color
import ocelot.desktop.geometry.Vector2D
import ocelot.desktop.graphics.Graphics import ocelot.desktop.graphics.Graphics
import ocelot.desktop.inventory.item._ import ocelot.desktop.inventory.SyncedInventory
import ocelot.desktop.inventory.traits.ComponentItem import ocelot.desktop.node.ComputerAwareNode
import ocelot.desktop.inventory.{Item, SyncedInventory} import ocelot.desktop.node.Node.HighlightThickness
import ocelot.desktop.node.{EntityNode, LabeledEntityNode} import ocelot.desktop.ui.event.ClickEvent
import ocelot.desktop.node.nodes.ComputerNode.{ErrorMessageMoveSpeed, MaxErrorMessageDistance} import ocelot.desktop.ui.widget.contextmenu.ContextMenu
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.ui.widget.slot._ 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 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.Case
import totoro.ocelot.brain.entity.traits.{Environment, Inventory} import totoro.ocelot.brain.entity.traits.{Computer, Inventory, TieredPersistable}
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.util.Tier import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier import totoro.ocelot.brain.util.Tier.Tier
import scala.collection.mutable class ComputerNode(val computerCase: Case)
import scala.math.Ordering.Implicits.infixOrderingOps extends ComputerAwareNode(computerCase)
import scala.reflect.ClassTag with ComputerAware
import scala.util.Random {
override val icon: String = "nodes/computer/Default"
override def iconColor: Color = TierColor.get(computerCase.tier)
class ComputerNode(val computer: Case) override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
extends EntityNode(computer) addPowerContextMenuEntries(menu)
with LabeledEntityNode addTierContextMenuEntries(menu)
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)
},
)
}
},
)
menu.addSeparator() menu.addSeparator()
super.setupContextMenu(menu)
}
override def onClick(event: ClickEvent): Unit = { super.setupContextMenu(menu, event)
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)
} }
override def update(): Unit = { override def update(): Unit = {
super.update() super.update()
if (!isRunning && soundComputerRunning.isPlaying) { updateRunningSound()
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")
}
} }
override def dispose(): Unit = { override def dispose(): Unit = {
super.dispose() super.dispose()
soundComputerRunning.stop() soundComputerRunning.stop()
} }
private var currentWindow: ComputerWindow = _ override def draw(g: Graphics): Unit = {
super.draw(g)
override def window: Option[ComputerWindow] = { DrawUtils.drawComputerNodeActivity(
if (currentWindow == null) { g,
currentWindow = new ComputerWindow(this) position.x + HighlightThickness,
Some(currentWindow) position.y + HighlightThickness,
} else Some(currentWindow) computerCase.machine,
"nodes/computer/"
)
} }
private val messages = mutable.ArrayBuffer[(Float, ErrorMessageLabel)]() // ---------------------------- ComputerAware ----------------------------
private def addErrorMessage(message: ErrorMessageLabel): Unit = synchronized { override def computer: Case = computerCase
messages += ((0f, message)) 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 { memorySlots = addSlotWidgets(new MemorySlotWidget(_, Tier.Three), new MemorySlotWidget(_, Tier.Three))
for ((time, message) <- messages.reverseIterator) {
message.position = message.initialPosition + Vector2D(0, -MaxErrorMessageDistance * time) diskSlots =
message.alpha = 1 - time if (computerCase.tier == Tier.Three) {
message.draw(g) 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) } floppySlot = Some(addSlotWidget(new FloppySlotWidget(_)))
messages.filterInPlace(_._1 <= 1f) cpuSlot = addSlotWidget(new CpuSlotWidget(_, this, Tier.Three))
eepromSlot = addSlotWidget(new EepromSlotWidget(_))
} }
} }
object ComputerNode { // ---------------------------- ShiftClickNode ----------------------------
private val MaxErrorMessageDistance: Float = 50
private val ErrorMessageMoveSpeed: Float = 0.5f override protected def onShiftClick(event: ClickEvent): Unit = toggleIsTurnedOn()
override protected def hoveredShiftStatusBarText: String = if (computer.machine.isRunning) "Turn off" else "Turn on"
} }

View File

@ -1,77 +1,58 @@
package ocelot.desktop.node.nodes package ocelot.desktop.node.nodes
import ocelot.desktop.color.IntColor import ocelot.desktop.geometry.Rect2D
import ocelot.desktop.graphics.Graphics import ocelot.desktop.graphics.Graphics
import ocelot.desktop.inventory.SyncedInventory import ocelot.desktop.node.Node.{HighlightThickness, NoHighlightSize}
import ocelot.desktop.inventory.item.FloppyItem import ocelot.desktop.node.{LabeledEntityNode, ShiftClickNode, SyncedInventoryEntityNode}
import ocelot.desktop.node.{EntityNode, LabeledEntityNode} import ocelot.desktop.ui.event.ClickEvent
import ocelot.desktop.ui.widget.slot.FloppySlotWidget import ocelot.desktop.ui.event.handlers.DiskActivityHandler
import ocelot.desktop.windows.DiskDriveWindow import ocelot.desktop.ui.widget.contextmenu.ContextMenu
import ocelot.desktop.util.DiskDriveAware
import totoro.ocelot.brain.entity.FloppyDiskDrive 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) class DiskDriveNode(entity: FloppyDiskDrive)
extends EntityNode(diskDrive) extends SyncedInventoryEntityNode(entity)
with LabeledEntityNode with LabeledEntityNode
with SyncedInventory { with DiskDriveAware
with DiskActivityHandler
def this(diskDrive: FloppyDiskDrive) = { with ShiftClickNode
this(diskDrive, false) {
} override def icon: String = "nodes/disk-drive/Default"
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"
override protected val canOpen = true override protected val canOpen = true
private val colorMap: Map[DyeColor, Int] = Map( override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
DyeColor.Black -> 0x444444, // 0x1E1B1B addDiskDriveMenuEntries(menu)
DyeColor.Red -> 0xB3312C,
DyeColor.Green -> 0x339911, // 0x3B511A menu.addSeparator()
DyeColor.Brown -> 0x51301A,
DyeColor.Blue -> 0x6666FF, // 0x253192 super.setupContextMenu(menu, event)
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 draw(g: Graphics): Unit = { override def draw(g: Graphics): Unit = {
super.draw(g) super.draw(g)
if (System.currentTimeMillis() - diskDrive.lastDiskAccess < 400 && Math.random() > 0.1) { drawActivityAndFloppy(
g.sprite("nodes/DiskDriveActivity", position.x + 2, position.y + 2, size.width - 4, size.height - 4) g,
} Rect2D(
position.x + HighlightThickness,
for (item <- slot.item) { position.y + HighlightThickness,
g.sprite( NoHighlightSize,
"nodes/DiskDriveFloppy", NoHighlightSize
position.x + 2, ),
position.y + 2, "nodes/disk-drive/"
size.width - 4,
size.height - 4,
IntColor(colorMap(item.color.value)),
) )
} }
// ---------------------------- 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"
} }

View File

@ -1,12 +1,13 @@
package ocelot.desktop.node.nodes package ocelot.desktop.node.nodes
import ocelot.desktop.ui.event.ClickEvent
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu} import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu}
import totoro.ocelot.brain.entity.NoteBlock import totoro.ocelot.brain.entity.NoteBlock
class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBlock) { class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBlock) {
override def icon: String = "nodes/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") { menu.addEntry(new ContextMenuSubmenu("Instrument") {
{ {
val maxLen = NoteBlockNode.Instruments.map(_._2.length).max val maxLen = NoteBlockNode.Instruments.map(_._2.length).max
@ -20,7 +21,8 @@ class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBloc
}) })
menu.addSeparator() menu.addSeparator()
super.setupContextMenu(menu)
super.setupContextMenu(menu, event)
} }
} }

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

View File

@ -1,16 +1,17 @@
package ocelot.desktop.node.nodes package ocelot.desktop.node.nodes
import ocelot.desktop.{OcelotDesktop, Settings}
import ocelot.desktop.color.{Color, IntColor} import ocelot.desktop.color.{Color, IntColor}
import ocelot.desktop.geometry.{Rect2D, Size2D} import ocelot.desktop.geometry.{Rect2D, Size2D}
import ocelot.desktop.graphics.Graphics 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.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.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.util.TierColor
import ocelot.desktop.windows.ScreenWindow import ocelot.desktop.windows.ScreenWindow
import ocelot.desktop.{OcelotDesktop, Settings}
import totoro.ocelot.brain.entity.{Keyboard, Screen} import totoro.ocelot.brain.entity.{Keyboard, Screen}
import totoro.ocelot.brain.nbt.NBTTagCompound import totoro.ocelot.brain.nbt.NBTTagCompound
import totoro.ocelot.brain.util.PackedColor 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 val kbd = new Keyboard
OcelotDesktop.workspace.add(kbd) OcelotDesktop.workspace.add(kbd)
screen.connect(kbd) screen.connect(kbd)
keyboard = Some(kbd) keyboard = Some(kbd)
this
} }
override def icon: String = "nodes/screen/Standalone" 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 protected val canOpen = true
override def setupContextMenu(menu: ContextMenu): Unit = { override def setupContextMenu(menu: ContextMenu, event: ClickEvent): Unit = {
if (screen.getPowerState) if (screen.getPowerState)
menu.addEntry(ContextMenuEntry("Turn off") { screen.setPowerState(false) }) menu.addEntry(ContextMenuEntry("Turn off") { screen.setPowerState(false) })
else else
@ -76,7 +76,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
menu.addSeparator() menu.addSeparator()
super.setupContextMenu(menu) super.setupContextMenu(menu, event)
} }
def drawScreenData(g: Graphics, startX: Float, startY: Float, scaleX: Float, scaleY: Float): Unit = { 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) { if (aspectRatioHeight == 1) {
drawScreenPart( drawScreenPart(
"nodes/screen/Standalone", "nodes/screen/Standalone",
position.x + HighlightSize, position.x + HighlightThickness,
position.y + HighlightSize, position.y + HighlightThickness,
SizeWithoutHighlight, NoHighlightSize,
SizeWithoutHighlight, NoHighlightSize,
) )
} }
// 1 x n // 1 x n
@ -142,19 +142,19 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
// Top // Top
drawScreenPart( drawScreenPart(
"nodes/screen/ColumnTop", "nodes/screen/ColumnTop",
position.x + HighlightSize, position.x + HighlightThickness,
position.y + HighlightSize, position.y + HighlightThickness,
SizeWithoutHighlight, NoHighlightSize,
Size - HighlightSize, Size - HighlightThickness,
) )
// Middle // Middle
for (y <- 1 until aspectRatioHeight - 1) { for (y <- 1 until aspectRatioHeight - 1) {
drawScreenPart( drawScreenPart(
"nodes/screen/ColumnMiddle", "nodes/screen/ColumnMiddle",
position.x + HighlightSize, position.x + HighlightThickness,
position.y + y * Size, position.y + y * Size,
SizeWithoutHighlight, NoHighlightSize,
Size Size
) )
} }
@ -162,10 +162,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
// Bottom // Bottom
drawScreenPart( drawScreenPart(
"nodes/screen/ColumnBottom", "nodes/screen/ColumnBottom",
position.x + HighlightSize, position.x + HighlightThickness,
position.y + (aspectRatioHeight - 1) * Size, position.y + (aspectRatioHeight - 1) * Size,
SizeWithoutHighlight, NoHighlightSize,
SizeWithoutHighlight, NoHighlightSize,
) )
} }
} }
@ -176,10 +176,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
// Left // Left
drawScreenPart( drawScreenPart(
"nodes/screen/RowLeft", "nodes/screen/RowLeft",
position.x + HighlightSize, position.x + HighlightThickness,
position.y + HighlightSize, position.y + HighlightThickness,
Size - HighlightSize, Size - HighlightThickness,
SizeWithoutHighlight, NoHighlightSize,
) )
// Middle // Middle
@ -187,18 +187,18 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
drawScreenPart( drawScreenPart(
"nodes/screen/RowMiddle", "nodes/screen/RowMiddle",
position.x + x * Size, position.x + x * Size,
position.y + HighlightSize, position.y + HighlightThickness,
Size, Size,
SizeWithoutHighlight NoHighlightSize
) )
// Right // Right
drawScreenPart( drawScreenPart(
"nodes/screen/RowRight", "nodes/screen/RowRight",
position.x + (aspectRatioWidth - 1) * Size, position.x + (aspectRatioWidth - 1) * Size,
position.y + HighlightSize, position.y + HighlightThickness,
Size - HighlightSize, Size - HighlightThickness,
SizeWithoutHighlight NoHighlightSize
) )
} }
// n x n // n x n
@ -207,7 +207,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
// Left // Left
drawScreenPart( drawScreenPart(
leftName, leftName,
position.x + HighlightSize, position.x + HighlightThickness,
y, y,
Size, Size,
height height
@ -226,7 +226,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
// Right // Right
drawScreenPart( drawScreenPart(
rightName, rightName,
position.x + (aspectRatioWidth - 1) * Size - HighlightSize, position.x + (aspectRatioWidth - 1) * Size - HighlightThickness,
y, y,
Size, Size,
height height
@ -235,8 +235,8 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
// Top // Top
drawLine( drawLine(
position.y + HighlightSize, position.y + HighlightThickness,
Size - HighlightSize, Size - HighlightThickness,
"nodes/screen/TopLeft", "nodes/screen/TopLeft",
"nodes/screen/TopMiddle", "nodes/screen/TopMiddle",
"nodes/screen/TopRight" "nodes/screen/TopRight"
@ -255,7 +255,7 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
// Bottom // Bottom
drawLine( drawLine(
position.y + (aspectRatioHeight - 1) * Size, position.y + (aspectRatioHeight - 1) * Size,
Size - HighlightSize, Size - HighlightThickness,
"nodes/screen/BottomLeft", "nodes/screen/BottomLeft",
"nodes/screen/BottomMiddle", "nodes/screen/BottomMiddle",
"nodes/screen/BottomRight" "nodes/screen/BottomRight"
@ -289,11 +289,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
screen.getHeight * FontHeight screen.getHeight * FontHeight
) )
var scale = virtualScreenBounds.w / pixelDataSize.width val scale = Math.min(
val scaleY = virtualScreenBounds.h / pixelDataSize.height virtualScreenBounds.w / pixelDataSize.width,
virtualScreenBounds.h / pixelDataSize.height
if (scaleY < scale) )
scale = scaleY
// Drawing pixel data // Drawing pixel data
drawScreenData( drawScreenData(
@ -308,10 +307,10 @@ class ScreenNode(val screen: Screen) extends EntityNode(screen) with LabeledEnti
else { else {
g.sprite( g.sprite(
"nodes/screen/PowerOnOverlay", "nodes/screen/PowerOnOverlay",
position.x + HighlightSize, position.x + HighlightThickness,
position.y + HighlightSize, position.y + HighlightThickness,
SizeWithoutHighlight, NoHighlightSize,
SizeWithoutHighlight NoHighlightSize
) )
} }
} }

View File

@ -183,7 +183,7 @@ object UiHandler extends Logging {
def init(): Unit = { def init(): Unit = {
scalingFactor = Settings.get.scaleFactor scalingFactor = Settings.get.scaleFactor
fullScreen = Settings.get.windowFullscreen fullScreen = Settings.get.windowFullscreen
windowTitle = "Ocelot Desktop v" + BuildInfo.version windowTitle = "Ocelot Desktop"
loadIcons() loadIcons()
@ -375,13 +375,13 @@ object UiHandler extends Logging {
hierarchy.reverseIterator.foreach(_.handleEvent(event)) hierarchy.reverseIterator.foreach(_.handleEvent(event))
for (event <- MouseEvents.events) 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 val scrollTarget = hierarchy.reverseIterator
.find(w => w.receiveScrollEvents && w.clippedBounds.contains(mousePos)) .find(w => w.receiveScrollEvents && w.clippedBounds.contains(mousePos))
val mouseTarget = hierarchy.reverseIterator 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) for (event <- ScrollEvents.events)
scrollTarget.foreach(_.handleEvent(event)) scrollTarget.foreach(_.handleEvent(event))

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

View File

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

View File

@ -1,7 +1,7 @@
package ocelot.desktop.ui.widget package ocelot.desktop.ui.widget
import ocelot.desktop.ColorScheme import ocelot.desktop.ColorScheme
import ocelot.desktop.audio.{SoundSource, SoundSources} import ocelot.desktop.audio.SoundSource
import ocelot.desktop.color.Color import ocelot.desktop.color.Color
import ocelot.desktop.geometry.Size2D import ocelot.desktop.geometry.Size2D
import ocelot.desktop.graphics.Graphics import ocelot.desktop.graphics.Graphics
@ -14,8 +14,6 @@ class Button extends Widget with ClickHandler with ClickSoundSource {
def onClick(): Unit = {} def onClick(): Unit = {}
def enabled: Boolean = true
override def receiveMouseEvents: Boolean = true override def receiveMouseEvents: Boolean = true
eventHandlers += { 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) 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
} }

View File

@ -1,7 +1,7 @@
package ocelot.desktop.ui.widget package ocelot.desktop.ui.widget
import ocelot.desktop.OcelotDesktop import ocelot.desktop.OcelotDesktop
import ocelot.desktop.audio.{SoundSource, SoundSources} import ocelot.desktop.audio.SoundSource
import ocelot.desktop.geometry.Padding2D import ocelot.desktop.geometry.Padding2D
import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.layout.LinearLayout
import ocelot.desktop.ui.widget.ChangeSimulationSpeedDialog.validateIntervalUs import ocelot.desktop.ui.widget.ChangeSimulationSpeedDialog.validateIntervalUs
@ -87,7 +87,7 @@ class ChangeSimulationSpeedDialog() extends ModalDialog {
children :+= new PaddingBox(new Button { children :+= new PaddingBox(new Button {
override def text: String = "Cancel" override def text: String = "Cancel"
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClickLow override protected def clickSoundSource: SoundSource = SoundSource.InterfaceClickLow
override def onClick(): Unit = close() override def onClick(): Unit = close()
}, Padding2D(right = 8)) }, Padding2D(right = 8))

View File

@ -1,6 +1,6 @@
package ocelot.desktop.ui.widget package ocelot.desktop.ui.widget
import ocelot.desktop.audio.{SoundSource, SoundSources} import ocelot.desktop.audio.SoundSource
import ocelot.desktop.geometry.Padding2D import ocelot.desktop.geometry.Padding2D
import ocelot.desktop.ui.layout.LinearLayout import ocelot.desktop.ui.layout.LinearLayout
import ocelot.desktop.ui.widget.modal.ModalDialog import ocelot.desktop.ui.widget.modal.ModalDialog
@ -19,7 +19,7 @@ class CloseConfirmationDialog extends ModalDialog {
children :+= new Widget { children :+= new Widget {
children :+= new PaddingBox(new Button { children :+= new PaddingBox(new Button {
override def text: String = "Cancel" override def text: String = "Cancel"
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClickLow override protected def clickSoundSource: SoundSource = SoundSource.InterfaceClickLow
override def onClick(): Unit = close() override def onClick(): Unit = close()
}, Padding2D(left = 8)) }, Padding2D(left = 8))

View File

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

View File

@ -2,26 +2,14 @@ package ocelot.desktop.ui.widget
import ocelot.desktop.ColorScheme import ocelot.desktop.ColorScheme
import ocelot.desktop.color.Color import ocelot.desktop.color.Color
import ocelot.desktop.geometry.Size2D
import ocelot.desktop.graphics.Graphics 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 { import scala.collection.mutable.ArrayBuffer
override def minimumSize: Size2D = Size2D(274, 70)
override def maximumSize: Size2D = minimumSize
var text = "N/A" class Histogram extends Widget {
var history: Array[Float] = Seq.fill(21)(0.0f).toArray var value = "N/A"
var title = "Hello world"
override def receiveMouseEvents = true var history: ArrayBuffer[Float] = ArrayBuffer(50)
eventHandlers += {
case HoverEvent(state) =>
if (state == HoverEvent.State.Enter) onHoverEnter()
else onHoverLeave()
}
private def drawBars(g: Graphics): Unit = { private def drawBars(g: Graphics): Unit = {
def drawBarSegment(i: Int, color: Color): Unit = { def drawBarSegment(i: Int, color: Color): Unit = {
@ -33,52 +21,90 @@ class Histogram extends Widget with HoverHandler {
val fillBars = (ratio * 10).round val fillBars = (ratio * 10).round
val emptyBars = (9 - fillBars).max(0) val emptyBars = (9 - fillBars).max(0)
for (i <- 0 until emptyBars) { for (i <- 0 until emptyBars)
drawBarSegment(i, ColorScheme("HistogramBarEmpty")) drawBarSegment(i, ColorScheme("HistogramBarEmpty"))
}
for (i <- emptyBars + 1 until 10) { for (i <- emptyBars + 1 until 10)
drawBarSegment(i, ColorScheme("HistogramBarFill")) drawBarSegment(i, ColorScheme("HistogramBarFill"))
}
drawBarSegment(emptyBars, ColorScheme("HistogramBarTop")) 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.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() g.setNormalFont()
} }
private def drawHistogram(g: Graphics): Unit = { private def drawHistogram(g: Graphics): Unit = {
for (i <- 0 until 22) { val marginLeft = 41
g.rect(position.x + 41 + i * 11, position.y, 2, 57, ColorScheme("HistogramGrid")) val marginBottom = 12
} var x = position.x + marginLeft
for (i <- 0 until 6) {
g.rect(position.x + 41, position.y + i * 11, 233, 2, ColorScheme("HistogramGrid")) 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) { // Value line
val width = if (i == 20) 13 else 11 g.rect(
val height = (entry * 57).max(2) x,
g.rect(position.x + 41 + i * 11, position.y + 57 - height, width, 2, ColorScheme("HistogramEdge")) position.y + gridHeight + cellThickness - historyValueHeight,
g.rect(position.x + 41 + i * 11, position.y + 59 - height, width, height - 2, ColorScheme("HistogramFill")) historyValueWidth,
} cellThickness,
} ColorScheme("HistogramEdge")
)
protected val tooltip: Option[Tooltip] = None x += cellSize
def onHoverEnter(): Unit = {
if (tooltip.isDefined)
root.get.tooltipPool.addTooltip(tooltip.get)
} }
def onHoverLeave(): Unit = {
if (tooltip.isDefined)
root.get.tooltipPool.closeTooltip(tooltip.get)
} }
override def draw(g: Graphics): Unit = { override def draw(g: Graphics): Unit = {
drawBars(g) drawBars(g)
drawText(g)
drawHistogram(g) drawHistogram(g)
} }
} }

View File

@ -1,7 +1,7 @@
package ocelot.desktop.ui.widget package ocelot.desktop.ui.widget
import ocelot.desktop.ColorScheme import ocelot.desktop.ColorScheme
import ocelot.desktop.audio.{SoundSource, SoundSources} import ocelot.desktop.audio.SoundSource
import ocelot.desktop.color.Color import ocelot.desktop.color.Color
import ocelot.desktop.geometry.Size2D import ocelot.desktop.geometry.Size2D
import ocelot.desktop.graphics.Graphics import ocelot.desktop.graphics.Graphics
@ -143,7 +143,7 @@ class IconButton(
alphaAnimation.update() alphaAnimation.update()
} }
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClick override protected def clickSoundSource: SoundSource = SoundSource.InterfaceClick
} }
object IconButton { object IconButton {

Some files were not shown because too many files have changed in this diff Show More